TimelineGroup

timelines.TimelineGroup(
    id=None,
    name=None,
    timelines=None,
    is_locked=False,
    meta=None,
)

Container for commensurable timelines.

A TimelineGroup holds timelines (or sections thereof) that are commensurable - i.e., bijectively mapped to each other via linear interpolation. The group is defined by a timestamp table where each row is a boundary instant.

Between any two adjacent timestamps, all present timelines have coordinates that can be converted via linear interpolation.

Like Timeline, a Group can be locked to prevent extension.

Attributes

Name Type Description
id str Unique identifier for this group.
name str | None Optional human-readable name.
is_locked bool Whether the group can be extended.
meta dict[str, Any] Additional metadata dictionary.

Examples

>>> # Create empty group
>>> group = TimelineGroup(id="my_group")
>>> # Or create with initial timelines
>>> group = TimelineGroup(id="my_group", timelines=[dgt1, audio])
>>> # Add a timeline
>>> group.add_timeline(dgt1)
>>> # Add with explicit boundaries
>>> from timetoalign import IdCoordinate, TimeUnit
>>> group.add_timeline(
...     score_section,
...     start=IdCoordinate(45.0, TimeUnit.seconds, "audio:1"),
...     end=IdCoordinate(135.0, TimeUnit.seconds, "audio:1"),
... )
>>> # Convert coordinates via timestamp lookup
>>> ts = group.get_timestamp_at(75.0, "audio:1")
>>> ts["dgt1:1"]  # -> 2437.5

Methods

Name Description
add_timeline Add a timeline (or Child) to the group.
apply_flow Unfold ALL timelines in this group via a single flow.
convert Convert a coordinate from one timeline to another.
diagram Generate ASCII diagram for this group.
get_events Get events from all timelines in the group, concatenated.
get_range Get the coordinate range for a timeline in the group.
get_timeline Get a timeline by ID.
get_timestamp_at Get a TimeStamp at a specific coordinate.
get_timestamp_at_index Get a specific timestamp by index.
get_timestamp_of Get the TimeStamp for a specific event by its ID.
get_timestamp_table Get the timestamp table (or a filtered subset).
get_timestamps_at Get timestamps at multiple coordinates - the batch version of get_timestamp_at.
get_timestamps_of Get timestamps for multiple events.
lock Lock the group to prevent extension.
remove_timeline Remove a timeline from the group.
summary Get a summary of the group.
to_dataframe Generate timestamps as a pandas DataFrame with formatted field names.
unlock Unlock the group to allow extension.

add_timeline

timelines.TimelineGroup.add_timeline(
    timeline,
    *,
    start=None,
    end=None,
    allow_extension=False,
)

Add a timeline (or Child) to the group.

The timeline’s full extent (0 to length) becomes commensurable with the group between the specified start and end boundaries.

This method works the same whether the group is empty or already has timelines. For an empty group, start/end default to the timeline’s own boundaries (0 and length).

Parameters

Name Type Description Default
timeline 'Timeline' The timeline or Child to add. If a Child, its 0-origin extent is used. If a Timeline, its full extent (0 to length). required
start CoordinateSpec | GroupTimestamp | None Where this timeline’s section STARTS in the group. - CoordinateSpec: Coordinate in the alignment-reference timeline - IdCoordinate: Coordinate with explicit timeline_id - GroupTimestamp: Use this existing timestamp - float: Coordinate (only if single timeline in group) - None: Use group’s current start, or 0 if empty None
end CoordinateSpec | GroupTimestamp | None Where this timeline’s section ENDS in the group. - Same options as start - None: Use group’s current end, or timeline.length if empty None
allow_extension bool If True and end extends beyond current group end, add a new end timestamp. If False (default) and group is locked, raise an error. False

Raises

Name Type Description
ValueError If timeline ID already exists in group.
ValueError If start/end specification is ambiguous.
RuntimeError If group is locked and extension would be required.

Examples

>>> # Add to empty group - defines initial extent
>>> group.add_timeline(dgt1)
>>> # Add to existing group - maps to existing extent
>>> group.add_timeline(audio)
>>> # Add partial section with explicit boundaries (using IdCoordinate)
>>> from timetoalign import IdCoordinate, TimeUnit
>>> group.add_timeline(
...     score_section,
...     start=IdCoordinate(45.0, TimeUnit.seconds, "audio:1"),
...     end=IdCoordinate(135.0, TimeUnit.seconds, "audio:1"),
... )
>>> # Extend group with new timeline
>>> group.add_timeline(
...     extended_audio,
...     end=IdCoordinate(200.0, TimeUnit.seconds, "extended_audio:1"),
...     allow_extension=True,
... )

apply_flow

timelines.TimelineGroup.apply_flow(
    flow,
    flow_controller,
    reference_timeline_id,
    *,
    include_children=True,
    name=None,
)

Unfold ALL timelines in this group via a single flow.

Uses the flow controller’s repeat structure to compute section boundaries in the reference timeline’s coordinate space, then resolves those boundaries into every other timeline’s coordinates via the group’s interpolation maps. Each member is unfolded along its own resolved played spans: a new timeline of the member’s same concrete type with one appended child (plus a matching named Region) per section, in unfolded coordinates.

This is the group-level equivalent of timetoalign.timelines.flow.create_unfolded_timeline, but applied to every member at once. It shares the append-children assembly via unfold_via_flowmap, so each member’s children and Regions carry the same per-section names as the single-timeline path (repeats suffixed -rend2, -rend3 …).

Parameters

Name Type Description Default
flow 'Flow' The computed Flow (from controller.compute_flow()). required
flow_controller 'FlowControllerBase' The FlowControllerBase that produced the flow. Required to convert flow sections to quarter-beat coordinates. required
reference_timeline_id str ID of the timeline whose coordinate space the flow is defined in (typically the score CLT). Section boundaries are resolved here first, then mapped to all other timelines. required
include_children bool Whether to recursively slice child timelines within each section. True
name str | None Name for the returned group. Defaults to f"{self.name} (unfolded)". None

Returns

Name Type Description
'TimelineGroup' A new TimelineGroup containing the unfolded timelines. Each
'TimelineGroup' member keeps its original timeline ID, is the same concrete type as
'TimelineGroup' its source, and carries one appended child and Region per section.
'TimelineGroup' The reference timeline additionally carries a reverse FlowMap
'TimelineGroup' (id "source") and a forward FlowMap (id f"forward_{flow.id}").

Raises

Name Type Description
KeyError If reference_timeline_id is not in the group.
ValueError If the flow controller cannot compute QB sections.

Examples

>>> loader = Ms3Loader.from_file("notes.tsv", "measures.tsv")
>>> controller = loader.create_flow_controller()
>>> flow = controller.compute_flow(FlowMode.default)
>>> score_group = TimelineGroup(
...     id="score", timelines=[clt1, dgt1, openscore]
... )
>>> unfolded = score_group.apply_flow(flow, controller, "clt1")

convert

timelines.TimelineGroup.convert(
    coordinate,
    source,
    target,
    *,
    relative_to='group',
)

Convert a coordinate from one timeline to another.

This is a convenience method that gets the timestamp at the source coordinate and returns the target timeline’s coordinate from it.

Parameters

Name Type Description Default
coordinate CoordinateSpec The coordinate value to convert. Accepts a raw int/float/Fraction, a Coordinate, or an IdCoordinate. A unit-qualified coordinate is resolved by the source timeline, and an IdCoordinate must agree with source. required
source str Source timeline ID. required
target str Target timeline ID. required
relative_to Literal['group', 'original'] “group” - coordinate is relative to timeline’s 0-origin IN THIS GROUP “original” - coordinate is relative to timeline’s ORIGINAL origin 'group'

Returns

Name Type Description
float | None The converted coordinate, or None if target timeline is not
float | None present at this coordinate.

Raises

Name Type Description
KeyError If source or target timeline is not in the group.
ValueError If coordinate is outside the source timeline’s range.

Examples

>>> group.convert(75.0, source="audio:1", target="dgt1:1")
2437.5

diagram

timelines.TimelineGroup.diagram(
    width=70,
    show_children=True,
    max_children=6,
    unicode=True,
)

Generate ASCII diagram for this group.

Parameters

Name Type Description Default
width int Total width of the diagram in characters. 70
show_children bool Whether to expand child timelines. True
max_children int Maximum children per timeline. 6
unicode bool Use Unicode characters (True) or ASCII fallback (False). True

Returns

Name Type Description
'Diagram' Diagram object (displays as ASCII in terminal, rich HTML in Jupyter).

Examples

>>> print(group.diagram())
TimelineGroup[my_group] (2 timelines, 2 timestamps)
┌────────────────────────────────────────────────────────────┐
│ DiscreteGraphicalTimeline[dgt1:1] (11 events, 5 children)  │
0 ∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶∶ 4835 pixels    │
│   ├─ system_1     0   ∶∶∶∶∶∶∶                    967
│   └─ ...                                                   │
└────────────────────────────────────────────────────────────┘
Timestamps: 2

get_events

timelines.TimelineGroup.get_events(timeline_id=None, **kwargs)

Get events from all timelines in the group, concatenated.

Collects events from all member timelines (or a specific one) and concatenates their Arrow tables into a single EventData. Each row includes a timeline_id field identifying the source timeline. A member-provided timeline_id column is discarded because group membership is authoritative.

Parameters

Name Type Description Default
timeline_id str | None If provided, only return events from this timeline. Supports partial string and regex matching. None
**kwargs Any Passed through to each timeline’s get_events() method (e.g., min_coord, max_coord, event_type). {}

Returns

Name Type Description
EventData EventData with events from all (or specified) timelines. Includes
EventData a timeline_id field and standard event fields (start,
EventData end, event_type, etc.).

Examples

>>> # Get all events from all timelines
>>> df = group.get_events().to_dataframe()
>>> # Get events from a specific timeline
>>> df = group.get_events(timeline_id="clt1").to_dataframe()
>>> # Get events with filters
>>> df = group.get_events(
...     event_type="Note", min_coord=0.0, max_coord=100.0
... ).to_dataframe()

get_range

timelines.TimelineGroup.get_range(timeline_id, relative_to='group')

Get the coordinate range for a timeline in the group.

Parameters

Name Type Description Default
timeline_id str The timeline to query. required
relative_to Literal['group', 'original'] Coordinate system for the result. 'group'

Returns

Name Type Description
tuple[float, float] | None (start, end) tuple, or None if timeline not in group.

get_timeline

timelines.TimelineGroup.get_timeline(timeline_id)

Get a timeline by ID.

Supports partial string and regex matching: 1. Exact match: If timeline_id matches an ID exactly, returns it. 2. Substring match: If timeline_id is a substring of exactly one ID, returns that timeline. If multiple match, returns the first and warns. 3. Regex match: If timeline_id is a valid regex, matches via re.search(). Same first-match logic with warning.

Parameters

Name Type Description Default
timeline_id str The timeline’s unique identifier, or a partial/regex pattern. required

Returns

Name Type Description
'Timeline' The Timeline object.

Raises

Name Type Description
KeyError If no timeline matches the pattern.

Examples

>>> group.get_timeline("clt1")           # Exact match
>>> group.get_timeline("notes")          # Substring match
>>> group.get_timeline(r"^score:")       # Regex match

get_timestamp_at

timelines.TimelineGroup.get_timestamp_at(
    coordinate,
    timeline_id=None,
    *,
    relative_to='group',
    conversion_maps=True,
)

Get a TimeStamp at a specific coordinate.

This is the primary coordinate resolution API for TimelineGroup. Returns a proper TimeStamp object (same as Timeline.get_timestamp).

Parameters

Name Type Description Default
coordinate CoordinateSpec The query coordinate. Can be: - int/float/Fraction: Raw value, timeline_id required - Coordinate: Value with unit, timeline_id required - IdCoordinate: Value with unit AND timeline_id (timeline_id param optional) required
timeline_id str | None Which timeline the coordinate refers to. Required unless coordinate is an IdCoordinate. None
relative_to Literal['group', 'original'] “group” - coordinate is relative to timeline’s 0-origin IN THIS GROUP (default; e.g., “3 seconds into this group”) “original” - coordinate is relative to timeline’s ORIGINAL origin (e.g., “50 seconds in the original timeline”) NOTE: Currently not implemented, reserved for future use. 'group'
conversion_maps ConversionMapsSpec Whether to include C-Map values in timestamp. - True (default): C-Maps accessible via ts.get_unit() and ts[“unit_name”] - False/None: Only timeline coordinates True

Returns

Name Type Description
TimeStamp TimeStamp with axis set to the input coordinate and source_id set to
TimeStamp the timeline. Access other timelines via ts[“other_id”] or ts.get().
TimeStamp Access C-Maps via ts.get_unit() or ts[“unit_name”].

Raises

Name Type Description
KeyError If timeline_id is not in the group.
ValueError If coordinate is outside the timeline’s range in the group.
ValueError If timeline_id is None and coordinate is not IdCoordinate.

Examples

>>> ts = group.get_timestamp_at(75.0, "audio:1")
>>> ts.axis
75.0
>>> ts["dgt1:1"]
2437.5
>>> ts.get_unit(TimeUnit.seconds)  # C-Map conversion
75.0
>>> # Using IdCoordinate (timeline_id extracted automatically)
>>> coord = IdCoordinate(75.0, TimeUnit.seconds, "audio:1")
>>> ts = group.get_timestamp_at(coord)
>>> ts.axis
75.0

get_timestamp_at_index

timelines.TimelineGroup.get_timestamp_at_index(index)

Get a specific timestamp by index.

Parameters

Name Type Description Default
index int The row index in the timestamp table. required

Returns

Name Type Description
GroupTimestamp The GroupTimestamp at that index.

Raises

Name Type Description
IndexError If index is out of range.

get_timestamp_of

timelines.TimelineGroup.get_timestamp_of(event_id)

Get the TimeStamp for a specific event by its ID.

Searches all timelines in the group for the event and returns the corresponding TimeStamp (same structure as Timeline.get_timestamp).

Parameters

Name Type Description Default
event_id str The event’s unique identifier. required

Returns

Name Type Description
TimeStamp TimeStamp at the event’s coordinate, with access to all
TimeStamp timelines via ts[“timeline_id”] and C-Maps via ts.get_unit().

Raises

Name Type Description
KeyError If the event is not found in any timeline.

Examples

>>> ts = group.get_timestamp_of("note:000001")
>>> ts["audio"]  # Get coordinate on audio timeline
45.5
>>> ts.get_unit(TimeUnit.seconds)  # C-Map conversion
45.5

get_timestamp_table

timelines.TimelineGroup.get_timestamp_table(
    timeline_filter=None,
    conversion_maps=True,
)

Get the timestamp table (or a filtered subset).

Parameters

Name Type Description Default
timeline_filter set[str] | None Only include these timeline fields. None
conversion_maps ConversionMapsSpec Whether to include C-Map fields from member timelines. - True (default): Include all attached C-Maps from all timelines - False/None: No C-Map fields True

Returns

Name Type Description
pa.Table pa.Table with one row per timestamp, one field per timeline,
pa.Table plus C-Map fields if conversion_maps=True.
pa.Table Returns empty table if group has no timestamps.

get_timestamps_at

timelines.TimelineGroup.get_timestamps_at(
    coordinates,
    timeline_id,
    *,
    conversion_maps=True,
    units=True,
)

Get timestamps at multiple coordinates - the batch version of get_timestamp_at.

This is the DEAD-SIMPLE API for batch coordinate transfer: pass a sequence of coordinates and get back a DataFrame with one field per timeline and per C-Map.

Parameters

Name Type Description Default
coordinates Sequence[CoordinateSpec] Sequence of CoordinateSpec to query. required
timeline_id str Which timeline the coordinates refer to. required
conversion_maps ConversionMapsSpec Whether to include C-Map fields from member timelines. - True (default): Include all attached C-Maps - False/None: Only timeline coordinates True
units bool If True (default), append units to field names. True

Returns

Name Type Description
pd.DataFrame DataFrame with one row per coordinate, one field per timeline and C-Map.

Examples

>>> # Get timestamps at multiple score positions
>>> coords = [0.0, 100.0, 200.0, 400.0]
>>> df = group.get_timestamps_at(coords, "clt1_score")
>>> df.columns
Index(['clt1_score (quarterbeats)', 'dgt_holes (pixels)', ...])

get_timestamps_of

timelines.TimelineGroup.get_timestamps_of(event_ids)

Get timestamps for multiple events.

Searches all timelines in the group for each event and returns a DataFrame with coordinates on all timelines.

Parameters

Name Type Description Default
event_ids Sequence[str] List of event IDs to look up. required

Returns

Name Type Description
pd.DataFrame DataFrame with one row per event, indexed by event_id.
pd.DataFrame Fields are timeline IDs with their coordinates.
pd.DataFrame Events not found have NaN values.

Examples

>>> df = group.get_timestamps_of(["note:000001", "note:000002"])
>>> df.columns
Index(['clt1', 'audio', 'dgt1'])

lock

timelines.TimelineGroup.lock()

Lock the group to prevent extension.

remove_timeline

timelines.TimelineGroup.remove_timeline(timeline_id)

Remove a timeline from the group.

Updates timestamp table to remove the timeline’s field. Rows where all remaining timelines have null are removed.

Parameters

Name Type Description Default
timeline_id str ID of the timeline to remove. required

Returns

Name Type Description
'Timeline' The removed timeline.

Raises

Name Type Description
KeyError If timeline_id is not in the group.

summary

timelines.TimelineGroup.summary()

Get a summary of the group.

Returns

Name Type Description
dict[str, Any] Dictionary with group information.

to_dataframe

timelines.TimelineGroup.to_dataframe(
    timeline_filter=None,
    conversion_maps=True,
    *,
    fields=None,
    units=True,
    format='pandas',
)

Generate timestamps as a pandas DataFrame with formatted field names.

This is the recommended high-level method for getting timestamp data. It builds on get_timestamp_table() and applies field formatting.

Parameters

Name Type Description Default
timeline_filter set[str] | None Only include these timelines as fields. None
conversion_maps ConversionMapsSpec Whether to include C-Map fields from member timelines. - True (default): Include all attached C-Maps - False/None: No C-Map fields True
fields 'ColumnNaming | Callable[[str, dict], str] | list[str] | None' How to name the DataFrame fields. Options: - None or ColumnNaming.name (default): Use timeline/cmap name - ColumnNaming.id: Use timeline/cmap id - Callable: Function taking (name, metadata_dict) -> new_name - list[str]: Explicit field names None
units bool If True (default), append units to field names like “name (unit)”. True
format str Output format. Currently only “pandas” is supported. 'pandas'

Returns

Name Type Description
pd.DataFrame pandas DataFrame with:
pd.DataFrame - Fields named according to the fields parameter
pd.DataFrame - Units appended if units=True
pd.DataFrame - Integer fields using pandas nullable Int64 dtype

Examples

>>> df = group.to_dataframe()
>>> df.columns
Index(['audio (seconds)', 'dgt1 (pixels)', 'pixels_to_beats (beats)'])
>>> # Without units in field names
>>> df = group.to_dataframe(units=False)
>>> df.columns
Index(['audio', 'dgt1', 'pixels_to_beats'])

unlock

timelines.TimelineGroup.unlock()

Unlock the group to allow extension.