TimelineGroup

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: id: Unique identifier for this group. name: Optional human-readable name. is_locked: Whether the group can be extended. meta: 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
>>> group.add_timeline(
...     score_section,
...     start=(45.0, "audio:1"),
...     end=(135.0, "audio:1"),
... )

>>> # Convert coordinates via timestamp lookup
>>> ts = group.get_timestamp_at(75.0, "audio:1")
>>> ts["dgt1:1"]  # -> 2437.5

Attributes

Name Description
is_locked Whether the group is locked.
n_timelines Number of timelines in the group.
n_timestamps Number of boundary timestamps.
reference The first timeline added (for compatibility).
reference_timeline_id ID of the first timeline added (for compatibility).
timeline_ids IDs of all member timelines.
timestamps All boundary timestamps as view objects.

Methods

Name Description
add_timeline Add a timeline (or Child) to the group.
convert Convert a coordinate from one timeline to another.
diagram Generate ASCII diagram for this group.
from_reference Create a new group with a reference timeline.
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 Get a specific timestamp by index.
get_timestamp_at Get a TimeStamp at a specific coordinate.
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_df Convenience wrapper returning pandas DataFrame with units in column names.
get_timestamps_of Get timestamps for multiple events.
get_unified_interval_stamp Get a unified TimeIntervalStamp for a coordinate range.
get_unified_timestamp Get a unified TimeStamp at a specific coordinate.
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 column names.
unfold Unfold ALL timelines in this group via a single flow.
unlock Unlock the group to allow extension.

add_timeline

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).

Args: 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). start: Where this timeline’s section STARTS in the group. - IdCoordinate: Coordinate with explicit timeline_id (preferred) - GroupTimestamp: Use this existing timestamp - (coord, timeline_id): Legacy tuple form - float: Coordinate (only if single timeline in group) - None: Use group’s current start, or 0 if empty end: Where this timeline’s section ENDS in the group. - Same options as start - None: Use group’s current end, or timeline.length if empty allow_extension: If True and end extends beyond current group end, add a new end timestamp. If False (default) and group is locked, raise an error.

Raises: 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,
... )

convert

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.

Args: coordinate: The coordinate value to convert. source: Source timeline ID. target: Target timeline ID. relative_to: “group” - coordinate is relative to timeline’s 0-origin IN THIS GROUP “original” - coordinate is relative to timeline’s ORIGINAL origin

Returns: The converted coordinate, or None if target timeline is not present at this coordinate.

Raises: 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

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

Generate ASCII diagram for this group.

Args: width: Total width of the diagram in characters. show_children: Whether to expand child timelines. max_children: Maximum children per timeline. unicode: Use Unicode characters (True) or ASCII fallback (False).

Returns: 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

from_reference

TimelineGroup.from_reference(reference, uid=None, name=None)

Create a new group with a reference timeline.

DEPRECATED: Use TimelineGroup(id=…, timelines=[reference]) instead.

Args: reference: The timeline to use as reference. uid: Optional explicit ID. name: Optional human-readable name.

Returns: A new TimelineGroup containing only the reference timeline.

get_events

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 them into a single DataFrame. Each row includes a timeline_id column identifying the source timeline.

Args: timeline_id: If provided, only return events from this timeline. Supports partial string and regex matching. **kwargs: Passed through to each timeline’s get_events() method (e.g., min_coord, max_coord, event_type).

Returns: DataFrame with events from all (or specified) timelines. Includes a timeline_id column and standard event columns (start, end, event_type, etc.).

Examples: >>> # Get all events from all timelines >>> df = group.get_events()

>>> # Get events from a specific timeline
>>> df = group.get_events(timeline_id="clt1")

>>> # Get events with filters
>>> df = group.get_events(event_type="Note", min_coord=0.0, max_coord=100.0)

get_range

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

Get the coordinate range for a timeline in the group.

Args: timeline_id: The timeline to query. relative_to: Coordinate system for the result.

Returns: (start, end) tuple, or None if timeline not in group.

get_timeline

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.

Args: timeline_id: The timeline’s unique identifier, or a partial/regex pattern.

Returns: The Timeline object.

Raises: 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

TimelineGroup.get_timestamp(index)

Get a specific timestamp by index.

Args: index: The row index in the timestamp table.

Returns: The GroupTimestamp at that index.

Raises: IndexError: If index is out of range.

get_timestamp_at

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).

Args: coordinate: 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) timeline_id: Which timeline the coordinate refers to. Required unless coordinate is an IdCoordinate. relative_to: “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. conversion_maps: 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

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

Raises: 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_of

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).

Args: event_id: The event’s unique identifier.

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

Raises: 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

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

Get the timestamp table (or a filtered subset).

Args: timeline_filter: Only include these timeline columns. conversion_maps: Whether to include C-Map columns from member timelines. - True (default): Include all attached C-Maps from all timelines - False/None: No C-Map columns

Returns: pa.Table with one row per timestamp, one column per timeline, plus C-Map columns if conversion_maps=True. Returns empty table if group has no timestamps.

get_timestamps_at

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 all timeline columns and C-Maps.

Args: coordinates: Sequence of CoordinateSpec to query. timeline_id: Which timeline the coordinates refer to. conversion_maps: Whether to include C-Map columns from member timelines. - True (default): Include all attached C-Maps - False/None: Only timeline coordinates units: If True (default), append units to column names.

Returns: DataFrame with one row per coordinate, columns for all timelines and C-Maps.

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_df

TimelineGroup.get_timestamps_df(
    timeline_filter=None,
    conversion_maps=True,
    *,
    units=True,
)

Convenience wrapper returning pandas DataFrame with units in column names.

Args: timeline_filter: Only include these timeline columns. conversion_maps: Whether to include C-Map columns from member timelines. - True (default): Include all attached C-Maps - False/None: No C-Map columns units: If True (default), append units to column names like “name (unit)”.

Returns: pandas DataFrame with timestamp data and units in column names, including C-Map columns if conversion_maps=True.

get_timestamps_of

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.

Args: event_ids: List of event IDs to look up.

Returns: DataFrame with one row per event, indexed by event_id. Columns are timeline IDs with their coordinates. Events not found have NaN values.

Examples: >>> df = group.get_timestamps_of([“note:000001”, “note:000002”]) >>> df.columns Index([‘clt1’, ‘audio’, ‘dgt1’])

get_unified_interval_stamp

TimelineGroup.get_unified_interval_stamp(start, end, timeline_id)

Get a unified TimeIntervalStamp for a coordinate range.

.. deprecated:: This method is deprecated. Use get_timestamp_at() for start and end coordinates separately, then combine into a TimeIntervalStamp if needed.

Args: start: Start coordinate. end: End coordinate. timeline_id: Which timeline the coordinates refer to.

Returns: TimeIntervalStamp with start and end TimeStamps.

Examples: >>> interval = group.get_unified_interval_stamp(0.0, 100.0, “audio:1”) >>> interval.duration 100.0 >>> interval[“dgt1:1”] # Get (start, end) tuple (0.0, 3250.0)

get_unified_timestamp

TimelineGroup.get_unified_timestamp(coordinate, timeline_id)

Get a unified TimeStamp at a specific coordinate.

.. deprecated:: This method is deprecated. Use get_timestamp_at(coordinate, timeline_id) instead, which provides the same functionality.

Args: coordinate: The query coordinate. timeline_id: Which timeline the coordinate refers to.

Returns: TimeStamp with the axis coordinate and source set to this group.

Raises: KeyError: If timeline_id is not in the group. ValueError: If group has no timestamps.

Examples: >>> ts = group.get_unified_timestamp(75.0, “audio:1”) >>> ts.axis # The resolved coordinate 75.0 >>> ts[“dgt1:1”] # Get coordinate on another timeline 2437.5

lock

TimelineGroup.lock()

Lock the group to prevent extension.

remove_timeline

TimelineGroup.remove_timeline(timeline_id)

Remove a timeline from the group.

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

Args: timeline_id: ID of the timeline to remove.

Returns: The removed timeline.

Raises: KeyError: If timeline_id is not in the group.

summary

TimelineGroup.summary()

Get a summary of the group.

Returns: Dictionary with group information.

to_dataframe

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

Generate timestamps as a pandas DataFrame with formatted column names.

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

Args: timeline_filter: Only include these timeline columns. conversion_maps: Whether to include C-Map columns from member timelines. - True (default): Include all attached C-Maps - False/None: No C-Map columns columns: How to name the columns. 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 column names units: If True (default), append units to column names like “name (unit)”. format: Output format. Currently only “pandas” is supported.

Returns: pandas DataFrame with: - Columns named according to the columns parameter - Units appended if units=True - Integer columns using pandas nullable Int64 dtype

Examples: >>> df = group.to_dataframe() >>> df.columns Index([‘audio (seconds)’, ‘dgt1 (pixels)’, ‘pixels_to_beats (beats)’])

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

unfold

TimelineGroup.unfold(
    flow,
    flow_controller,
    reference_timeline_id,
    *,
    include_children=True,
    as_segment_lines=False,
    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 timeline is sliced at the corresponding coordinates and the slices are assembled into contiguous SegmentLine objects (or flattened into plain timelines).

This is the group-level equivalent of timetoalign.timelines.flow.create_unfolded_timeline, but applied to every member at once.

Args: flow: The computed Flow (from controller.compute_flow()). flow_controller: The FlowControllerBase that produced the flow. Required to convert flow sections to quarter-beat coordinates. reference_timeline_id: 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. include_children: Whether to recursively slice child timelines within each section. as_segment_lines: If True, each unfolded timeline is a SegmentLine (one segment per section). If False (default), events are flattened into a single timeline of the source’s concrete type. name: Name for the returned group. Defaults to f"{self.name} (unfolded)".

Returns: A new TimelineGroup containing the unfolded timelines. Each timeline retains its original ID.

Raises: KeyError: If reference_timeline_id is not in the group. ValueError: If the flow controller cannot compute QB sections.

Examples: >>> loader = TSVLoader.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.unfold(flow, controller, “clt1”)

unlock

TimelineGroup.unlock()

Unlock the group to allow extension.