How to Query Timestamps

TimeStamp, TimeIntervalStamp, boundary tables, PyArrow

How to Query Timestamps

Boundary tables, filtered timestamps, the TimeStamp / TimeIntervalStamp objects, and the underlying PyArrow tables.

import numpy as np

from timetoalign.timelines import ContinuousPhysicalTimeline

Setup: A Hierarchical Timeline

parent = ContinuousPhysicalTimeline(length=100, uid="parent")
parent.add_events(
    [
        {"id": "p1", "temporal_type": "instant", "event_type": "Beat", "instant": 0.0},
        {"id": "p2", "temporal_type": "instant", "event_type": "Beat", "instant": 50.0},
    ]
)

child1 = ContinuousPhysicalTimeline(length=20, uid="child1")
child1.add_events(
    [
        {"id": "c1a", "temporal_type": "instant", "event_type": "Note", "instant": 0.0},
        {
            "id": "c1b",
            "temporal_type": "instant",
            "event_type": "Note",
            "instant": 10.0,
        },
    ]
)

child2 = ContinuousPhysicalTimeline(length=15, uid="child2")
child2.add_events(
    [
        {"id": "c2a", "temporal_type": "instant", "event_type": "Note", "instant": 5.0},
    ]
)

parent.add_child(child1, offset=10)  # child1 spans [10, 30] on parent
parent.add_child(child2, offset=60)  # child2 spans [60, 75] on parent

Custom Coordinates

coords = [0.0, 15.0, 25.0, 50.0, 65.0, 100.0]
parent.to_dataframe(coordinates=coords)
axis (seconds) parent (seconds) child1 (seconds) child2 (seconds)
0 0.0 0.0 NaN NaN
1 15.0 15.0 5.0 NaN
2 25.0 25.0 15.0 NaN
3 50.0 50.0 NaN NaN
4 65.0 65.0 NaN 5.0
5 100.0 100.0 NaN NaN
# Efficient numpy array query
coords = np.linspace(0, 100, 21)
parent.to_dataframe(coordinates=coords)
axis (seconds) parent (seconds) child1 (seconds) child2 (seconds)
0 0.0 0.0 NaN NaN
1 5.0 5.0 NaN NaN
2 10.0 10.0 0.0 NaN
3 15.0 15.0 5.0 NaN
4 20.0 20.0 10.0 NaN
5 25.0 25.0 15.0 NaN
6 30.0 30.0 20.0 NaN
7 35.0 35.0 NaN NaN
8 40.0 40.0 NaN NaN
9 45.0 45.0 NaN NaN
10 50.0 50.0 NaN NaN
11 55.0 55.0 NaN NaN
12 60.0 60.0 NaN 0.0
13 65.0 65.0 NaN 5.0
14 70.0 70.0 NaN 10.0
15 75.0 75.0 NaN 15.0
16 80.0 80.0 NaN NaN
17 85.0 85.0 NaN NaN
18 90.0 90.0 NaN NaN
19 95.0 95.0 NaN NaN
20 100.0 100.0 NaN NaN

Boundary Tables

parent.get_boundary_table().to_pandas()
axis parent child1 child2
0 0.0 0.0 NaN NaN
1 10.0 10.0 0.0 NaN
2 30.0 30.0 20.0 NaN
3 60.0 60.0 NaN 0.0
4 75.0 75.0 NaN 15.0
5 100.0 100.0 NaN NaN

Filtering Events

parent.get_events(event_type="Note", include_children=True).to_dataframe()
id name temporal_type event_type start end duration source_timeline
0 c1a NaN instant Note 10.0 None None child1
1 c1b NaN instant Note 20.0 None None child1
2 c2a NaN instant Note 65.0 None None child2
parent.get_events(event_type="Beat", include_children=True).to_dataframe()
id name temporal_type event_type start end duration source_timeline
0 p1 NaN instant Beat 0.0 None None NaN
1 p2 NaN instant Beat 50.0 None None NaN

PyArrow Tables

For large datasets, get_timestamp_table() returns a PyArrow Table directly — convert to pandas only when needed.

table = parent.get_timestamp_table()
{
    "rows": table.num_rows,
    "columns": table.column_names,
}
{'rows': 8, 'columns': ['axis', 'parent', 'child1', 'child2']}
table.to_pandas().head()
axis parent child1 child2
0 0.0 0.0 NaN NaN
1 10.0 10.0 0.0 NaN
2 20.0 20.0 10.0 NaN
3 30.0 30.0 20.0 NaN
4 50.0 50.0 NaN NaN

The TimeStamp Object

Query a single coordinate and get all related values on demand.

ts = parent.get_timestamp(15.0)
ts.to_dict()
{'parent': {'value': 15.0,
  'numerator': None,
  'denominator': None,
  'unit': 'seconds',
  'number_type': 'float'},
 'child1': {'value': 5.0,
  'numerator': None,
  'denominator': None,
  'unit': 'seconds',
  'number_type': 'float'}}
# Access child coordinates by timeline ID. Only the children that actually
# cover the queried position are present; an absent ID raises KeyError.
{
    "parent": ts.axis,
    "present": ts.present_timelines,
    **{
        child_id: ts.get_coordinate_for(child_id)
        for child_id in ("child1", "child2")
        if child_id in ts.present_timelines
    },
}
{'parent': IdCoordinate(15.0, seconds, 'parent'),
 'present': ['parent', 'child1'],
 'child1': IdCoordinate(5.0, seconds, 'child1')}

TimeIntervalStamp

interval = parent.get_interval_stamp(20.0, 60.0)
{
    "axis_interval": interval.get_interval("parent"),
    "axis_duration": interval.duration,
    "present": interval.present_timelines,
}
{'axis_interval': Interval(start=Coordinate(20.0, seconds), end=Coordinate(60.0, seconds)),
 'axis_duration': Duration(40.0, seconds),
 'present': ['parent']}
interval.get_intervals()
{'parent': Interval(start=Coordinate(20.0, seconds), end=Coordinate(60.0, seconds))}

Coordinates with Units

TimeStamp and TimeIntervalStamp can produce proper Coordinate objects that carry their unit.

ts = parent.get_timestamp(25.0)
axis_coord = ts.axis
{
    "value": axis_coord.value,
    "unit": axis_coord.unit,
    "timeline_id": axis_coord.timeline_id,
}
{'value': 25.0, 'unit': "seconds", 'timeline_id': 'parent'}
child1_coord = ts.get_coordinate_for("child1")
{
    "child1 value": child1_coord.value,
    "child1 unit": child1_coord.unit,
}
{'child1 value': 15.0, 'child1 unit': "seconds"}

Unit Metadata in PyArrow Tables

table = parent.get_timestamp_table(coordinates=[0.0, 25.0, 50.0])

for field in table.schema:
    if field.metadata:
        unit = field.metadata.get(b"unit", b"N/A").decode()
        tl_id = field.metadata.get(b"timeline_id", b"N/A").decode()
        print(f"{field.name}: unit={unit}, timeline_id={tl_id}")
axis: unit=N/A, timeline_id=N/A
parent: unit=N/A, timeline_id=N/A
child1: unit=N/A, timeline_id=N/A
child2: unit=N/A, timeline_id=N/A