Skip to content

TrackingCollection

py3r.behaviour.tracking.tracking_collection.TrackingCollection

TrackingCollection(tracking_dict: dict[str, Tracking])

Bases: BaseCollection

Collection of Tracking objects, keyed by name (e.g. for grouping individuals) note: type-hints refer to Tracking, but factory methods allow for other classes these are intended ONLY for subclasses of Tracking, and this is enforced.

each instance-attribute

each: Tracking

each_forcebatch instance-attribute

each_forcebatch: Tracking

group_keys property

group_keys

Keys for the groups in a grouped view. Empty list if not grouped.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('group','G1'); coll['B'].add_tag('group','G2')
>>> g = coll.groupby('group')
>>> sorted(g.group_keys)
[('G1',), ('G2',)]

groupby_tags property

groupby_tags

The tag names used to form this grouped view (or None if flat).

iloc property

iloc

Slice all elements with Tracking object .iloc and return a new collection.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> sub = coll.iloc[0:2]
>>> all(len(t.data) == 2 for t in sub.values())
True

is_grouped property

is_grouped

True if this collection is a grouped view.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> coll.is_grouped
False

loc property

loc

Slice all elements with Tracking object .loc and return a new collection.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> sub = coll.loc[0:2]
>>> all(len(t.data) == 3 for t in sub.values())
True

tracking_dict property

tracking_dict

add_tags_from_csv

add_tags_from_csv(csv_path: str) -> None

Adds tags to all Tracking objects in the collection from a csv file. csv_path: path to a csv file with first column: "handle" and other columns with tagnames as titles and tagvalues as values.

Examples
>>> import tempfile, shutil, pandas as pd
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     # build a small collection
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     # tags csv
...     tagcsv = d / 'tags.csv'
...     tagdf = pd.DataFrame([{'handle':'A','group':'G1'},{'handle':'B','group':'G2'}])
...     tagdf.to_csv(tagcsv, index=False)
...     coll.add_tags_from_csv(str(tagcsv))
>>> coll['A'].tags
{'group': 'G1'}
>>> coll['B'].tags
{'group': 'G2'}

concat classmethod

concat(
    collections: list[TrackingCollection],
    *,
    reindex: Literal[
        "rezero", "follow_previous", "keep_original"
    ] = "follow_previous",
) -> TrackingCollection

Concatenate multiple TrackingCollections along the time (frame) axis.

Each collection must have the same handles (keys). For each handle, the corresponding Tracking objects are concatenated in order. Supports both flat and grouped collections.

Parameters:

Name Type Description Default

collections

list[TrackingCollection]

List of TrackingCollection objects to concatenate, in temporal order. All must have matching keys (handles).

required

reindex

Literal['rezero', 'follow_previous', 'keep_original']

How to handle frame indices. "rezero" reindexes all frames starting from 0. "follow_previous" continues from where the previous chunk ended. "keep_original" leaves indices untouched; duplicates are allowed.

'follow_previous'

Returns:

Type Description
TrackingCollection

A new collection with concatenated Tracking objects for each handle.

Raises:

Type Description
ValueError

If collections is empty, keys don't match, or grouping structure differs.

Examples

Concatenate two flat collections:

>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv'); _ = shutil.copy(p, d / 'B.csv')
...     tc1 = TrackingCollection.from_dlc({'A': str(d/'A.csv'),
...                                       'B': str(d/'B.csv')}, fps=30)
...     tc2 = TrackingCollection.from_dlc({'A': str(d/'A.csv'),
...                                        'B': str(d/'B.csv')}, fps=30)
>>> combined = TrackingCollection.concat([tc1, tc2])
>>> len(combined['A'].data) == len(tc1['A'].data) + len(tc2['A'].data)
True
>>> 'concat' in combined['A'].meta
True

copy

copy()

Creates a copy of the BaseCollection. Raises NotImplementedError if any leaf does not implement copy().

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv')
...         _ = shutil.copy(p, d / 'B.csv')
...     coll = TrackingCollection.from_folder(
...         str(d), tracking_loader=Tracking.from_dlc, fps=30
...     )
>>> coll_copy = coll.copy()
>>> sorted(coll_copy.keys())
['A', 'B']

flatten

flatten()

Flatten a MultipleCollection to a flat Collection. If already flat, return self.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('group','G1'); coll['B'].add_tag('group','G1')
...     g = coll.groupby('group')
>>> flat = g.flatten()
>>> flat.is_grouped
False
>>> sorted(flat.keys())
['A', 'B']

from_dlc classmethod

from_dlc(
    handles_and_filepaths: dict[str, str],
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type[Tracking] = Tracking,
)

Load a collection from DLC CSVs.

Parameters:

Name Type Description Default

handles_and_filepaths

dict[str, str]

Mapping of session handle to DLC CSV file path.

required

fps

float

Frame rate of the recording in frames per second.

required

aspectratio_correction

float

Multiplicative correction applied to the x-axis to account for non-square pixels.

1.0

tracking_cls

type[Tracking]

Tracking subclass to instantiate.

Tracking
Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'a.csv'; b = d / 'b.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> len(coll)
2

from_dlc_folder classmethod

from_dlc_folder(
    folder_path: str,
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type = Tracking,
) -> TrackingCollection

Convenience for from_folder using DLC loader.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv')
...         _ = shutil.copy(p, d / 'B.csv')
...     coll = TrackingCollection.from_dlc_folder(str(d), fps=30)
>>> set(coll.keys()) == {'A','B'}
True

from_dlcma classmethod

from_dlcma(
    handles_and_filepaths: dict[str, str],
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type[Tracking] = Tracking,
)

Load a collection from DLC multi-animal CSVs.

Parameters:

Name Type Description Default

handles_and_filepaths

dict[str, str]

Mapping of session handle to DLC multi-animal CSV file path.

required

fps

float

Frame rate of the recording in frames per second.

required

aspectratio_correction

float

Multiplicative correction applied to the x-axis to account for non-square pixels.

1.0

tracking_cls

type[Tracking]

Tracking subclass to instantiate.

Tracking
Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlcma_multi.csv') as p:
...         a = d / 'a.csv'; b = d / 'b.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlcma({'A': str(a), 'B': str(b)}, fps=30)
>>> len(coll) == 2
True

from_dlcma_folder classmethod

from_dlcma_folder(
    folder_path: str,
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type = Tracking,
) -> TrackingCollection

Convenience for from_folder using DLCMA loader.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlcma_multi.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv')
...         _ = shutil.copy(p, d / 'B.csv')
...     coll = TrackingCollection.from_dlcma_folder(str(d), fps=30)
>>> len(coll) == 2
True

from_dogfeather classmethod

from_dogfeather(
    handles_and_filepaths: dict[str, str],
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls=Tracking,
)

Loads a TrackingCollection from a dict of dogfeather tracking csvs. handles_and_filepaths: dict mapping handles to file paths.

from_folder classmethod

from_folder(
    folder_path: str,
    *,
    tracking_loader,
    tracking_cls: type = Tracking,
    **loader_kwargs,
) -> TrackingCollection

Build a collection by scanning a folder for CSVs (or multi-view subfolders).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv')
...         _ = shutil.copy(p, d / 'B.csv')
...     coll = TrackingCollection.from_folder(
...         str(d), tracking_loader=Tracking.from_dlc, fps=30)
>>> sorted(coll.keys())
['A', 'B']

from_groups classmethod

from_groups(
    groups: dict[str, list],
    *,
    fps: float,
    fmt: str = "yolo3r",
    tag: str = "group",
    strip_columns: bool = True,
) -> TrackingCollection

Load a TrackingCollection from a group-keyed dict of path lists.

Takes the natural output format of the GUI (dict[str, list[Path]]) and returns a single merged, tagged collection ready for analysis.

Parameters:

Name Type Description Default

groups

dict[str, list]

Mapping of group name to list of file paths. Each group must be non-empty.

required

fps

float

Frame rate of the recording in frames per second.

required

fmt

str

File format to load. Currently only "yolo3r" is supported.

'yolo3r'

tag

str

Tag key used to label each Tracking object with its group name.

'group'

strip_columns

bool

If True, call .strip_column_names() on every loaded object.

True

Returns:

Type Description
TrackingCollection

A single merged TrackingCollection with all recordings tagged by group.

Raises:

Type Description
ValueError

If a group has an empty path list or fmt is not supported.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
...         a = d / 'mouse1.csv'; b = d / 'mouse2.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     groups = {'control': [a], 'treated': [b]}
...     tc = TrackingCollection.from_groups(groups, fps=30)
>>> sorted(t.tags['group'] for t in tc.values())
['control', 'treated']

Handles default to the file stem (e.g. mouse1, mouse2 above) plus the group name (here mouse1, mouse2, with no group suffix, since those names are unique across the whole input):

>>> sorted(tc.keys())
['mouse1', 'mouse2']

If two files anywhere in the input share a stem, parent directory names are prepended (only as far as needed) to keep handles unique -- the group name is not involved unless that's not enough to disambiguate:

>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
...         cohort_a = d / 'cohortA'; cohort_b = d / 'cohortB'
...         cohort_a.mkdir(); cohort_b.mkdir()
...         a = cohort_a / '1.csv'; b = cohort_b / '1.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     groups = {'control': [a], 'treated': [b]}
...     tc = TrackingCollection.from_groups(groups, fps=30)
>>> sorted(tc.keys())
['cohortA_1', 'cohortB_1']

If the same file is deliberately included in more than one group, the path-based disambiguation can't tell the copies apart, so the group name is appended instead:

>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
...         a = d / 'mouse1.csv'
...         _ = shutil.copy(p, a)
...     groups = {'control': [a], 'treated': [a]}
...     tc = TrackingCollection.from_groups(groups, fps=30)
>>> sorted(tc.keys())
['mouse1_control', 'mouse1_treated']

from_list classmethod

from_list(objs)

Construct a collection from a list of items, using their .handle as the key. Raises a clear error if any item does not have a .handle attribute.

Examples
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...     t1 = Tracking.from_dlc(str(p), handle='A', fps=30)
...     t2 = Tracking.from_dlc(str(p), handle='B', fps=30)
>>> coll = TrackingCollection.from_list([t1, t2])
>>> list(sorted(coll.keys()))
['A', 'B']

from_mapping classmethod

from_mapping(
    handles_and_filepaths: dict[str, str],
    *,
    tracking_loader: Callable[..., Tracking],
    tracking_cls: type[Tracking] = Tracking,
    **loader_kwargs: Any,
)

Generic constructor from a mapping of handle -> filepath using a loader callable.

Parameters:

Name Type Description Default

handles_and_filepaths

dict[str, str]

Mapping of session handle to file path.

required

tracking_loader

Callable[..., Tracking]

Loader callable, e.g. Tracking.from_dlc. Called as tracking_loader(filepath, handle=handle, **loader_kwargs).

required

tracking_cls

type[Tracking]

Tracking subclass to instantiate.

Tracking

**loader_kwargs

Any

Extra keyword arguments forwarded to tracking_loader.

{}
Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     # create two files for demonstration
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         f1 = d / 'a.csv'; f2 = d / 'b.csv'
...         _ = shutil.copy(p, f1); _ = shutil.copy(p, f2)
...     mapping = {'A': str(f1), 'B': str(f2)}
...     coll = TrackingCollection.from_mapping(
...         mapping, tracking_loader=Tracking.from_dlc, fps=30)
>>> sorted(coll.keys())
['A', 'B']

from_yolo3r classmethod

from_yolo3r(
    handles_and_filepaths: dict[str, str],
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type[Tracking] = Tracking,
)

Load a collection from YOLO3R CSVs.

Parameters:

Name Type Description Default

handles_and_filepaths

dict[str, str]

Mapping of session handle to YOLO3R CSV file path.

required

fps

float

Frame rate of the recording in frames per second.

required

aspectratio_correction

float

Multiplicative correction applied to the x-axis to account for non-square pixels.

1.0

tracking_cls

type[Tracking]

Tracking subclass to instantiate.

Tracking
Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
...         a = d / 'a.csv'; b = d / 'b.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_yolo3r({'A': str(a), 'B': str(b)}, fps=30)
>>> set(coll.tracking_dict.keys()) == {'A','B'}
True

from_yolo3r_folder classmethod

from_yolo3r_folder(
    folder_path: str,
    *,
    fps: float,
    aspectratio_correction: float = 1.0,
    tracking_cls: type = Tracking,
) -> TrackingCollection

Convenience for from_folder using YOLO3R loader.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv')
...         _ = shutil.copy(p, d / 'B.csv')
...     coll = TrackingCollection.from_yolo3r_folder(str(d), fps=30)
>>> len(coll)
2

get_group

get_group(key)

Get a sub-collection by group key from a grouped view.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('group','G1'); coll['B'].add_tag('group','G2')
>>> g = coll.groupby('group')
>>> sub = g.get_group(('G1',))
>>> list(sub.keys())
['A']

groupby

groupby(tags)

Group the collection by one or more existing tag names. Returns a grouped view (this same collection type) whose values are sub-collections keyed by a tuple of tag values in the order provided.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('group','G1'); coll['B'].add_tag('group','G2')
>>> g = coll.groupby('group')
>>> g.is_grouped
True
>>> sorted(g.group_keys)
[('G1',), ('G2',)]

items

items()

Items iterator (handle, element).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> sorted([h for h, _ in coll.items()])
['A', 'B']

keys

keys()

Keys iterator (handles or group keys).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> list(sorted(coll.keys()))
['A', 'B']

load classmethod

load(dirpath: str)

Load a collection previously saved with save(). Uses the class's _element_type.load to reconstruct leaves.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     out = d / 'coll'
...     coll.save(str(out), overwrite=True, data_format='csv')
...     coll2 = TrackingCollection.load(str(out))
>>> list(sorted(coll2.keys()))
['A', 'B']

map_leaves

map_leaves(fn: Callable[[Any], Any])

Apply a function to every leaf element and return a new collection of the same type. Preserves grouping shape and groupby metadata when grouped.

Parameters:

Name Type Description Default

fn

Callable[[Any], Any]

Callable applied to each leaf element. Must return an element compatible with this collection type.

required
Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> sub = coll.map_leaves(lambda t: t.loc[0:1])
>>> all(len(t.data) == 2 for t in sub.values())
True

merge classmethod

merge(
    collections: list[Self], *, copy: bool = False
) -> Self

Merge multiple collections into a single flat collection containing all leaf elements from each input.

Each input collection is flattened before merging, so grouped inputs are supported. The result is always a new flat collection. Leaves are shared by reference unless copy=True.

Parameters:

Name Type Description Default

collections

list[Self]

Two or more collections of the same concrete type. Every element across all collections must have a unique handle.

required

copy

bool

If True, each leaf is copied (via its .copy() method) so that the merged collection is fully independent of the originals.

False

Returns:

Type Description
Self

A new flat collection containing all leaves.

Raises:

Type Description
ValueError

If collections is empty, or if any handles are duplicated.

TypeError

If any input is not an instance of the calling class.

Warns:

Type Description
UserWarning

If the tag key sets differ across input collections (the merged collection will have mixed tag coverage).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         _ = shutil.copy(p, d / 'A.csv'); _ = shutil.copy(p, d / 'B.csv')
...         _ = shutil.copy(p, d / 'C.csv'); _ = shutil.copy(p, d / 'D.csv')
...     c1 = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
...     c2 = TrackingCollection.from_dlc({'C': str(d/'C.csv'), 'D': str(d/'D.csv')}, fps=30)
>>> merged = TrackingCollection.merge([c1, c2])
>>> sorted(merged.keys())
['A', 'B', 'C', 'D']
>>> len(merged)
4

plot

plot(*args, **kwargs)

Plot all elements in the collection (or per group if grouped).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> _ = coll.plot(show=False)

regroup

regroup()

Recompute the same grouping using the current tags and the original grouping tag order. If not grouped, returns self.

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('group','G1'); coll['B'].add_tag('group','G1')
...     g = coll.groupby('group')
...     coll['B'].add_tag('group','G2', overwrite=True)  # change tag
>>> g2 = g.regroup()
>>> sorted(g2.group_keys)
[('G1',), ('G2',)]

save

save(
    dirpath: str,
    *,
    overwrite: bool = False,
    data_format: str = "parquet",
) -> None

Save this collection to a directory. Preserves grouping and delegates to leaf objects' save(dirpath, data_format, overwrite=True).

Examples
>>> import tempfile, shutil, os
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     out = d / 'coll'
...     coll.save(str(out), overwrite=True, data_format='csv')
...     # collection-level manifest at top-level
...     assert os.path.exists(os.path.join(str(out), 'manifest.json'))
...     # element-level manifests under elements/<handle>/
...     el_manifest = os.path.join(str(out), 'elements', 'A', 'manifest.json')
...     assert os.path.exists(el_manifest)

stereo_triangulate

stereo_triangulate() -> TrackingCollection

Triangulate all TrackingMV objects and return a new TrackingCollection. The new collection will have the same grouping as the original.

Note

This requires multi-view TrackingMV elements; typical Tracking elements do not support stereo triangulation.

Examples
>>> import tempfile, shutil, json
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_mv import TrackingMV
>>> # Create a collection with a single multi-view recording
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d) / 'rec1'
...     d.mkdir(parents=True, exist_ok=True)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p_csv:
...         _ = shutil.copy(p_csv, d / 'left.csv')
...         _ = shutil.copy(p_csv, d / 'right.csv')
...     # write a minimal synthetic calibration.json
...     calib = {
...         'view_order': ['left', 'right'],
...         'views': {
...             'left':  {'K': [[1,0,0],[0,1,0],[0,0,1]], 'dist': [0,0,0,0,0]},
...             'right': {'K': [[1,0,0],[0,1,0],[0,0,1]], 'dist': [0,0,0,0,0]},
...         },
...         'relative_pose': {'R': [[1,0,0],[0,1,0],[0,0,1]], 'T': [0.1, 0.0, 0.0]},
...     }
...     (d / 'calibration.json').write_text(json.dumps(calib))
...     # Build collection by scanning the parent folder with TrackingMV
...     parent = str(d.parent)
...     coll_mv = TrackingCollection.from_dlc_folder(
...         parent, tracking_cls=TrackingMV, fps=30)
...     coll_3d = coll_mv.stereo_triangulate()
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> isinstance(next(iter(coll_3d.values())), Tracking)
True
>>> next(iter(coll_3d.keys()))
'rec1'

stored_info

stored_info() -> pd.DataFrame

Summarize stored tracked points across the collection's leaf Tracking objects.

Returns a DataFrame indexed by point_name with columns: - attached_to: number of recordings containing the point - missing_from: number of recordings not containing the point - dims: point dimensions (e.g. ['x', 'y', 'z', 'likelihood']), or a list of such dimension-sets when mixed across recordings.

tags_info

tags_info(
    *, include_value_counts: bool = False
) -> pd.DataFrame

Summarize tag presence across the collection's leaf objects. Works for flat and grouped collections. If include_value_counts is True, include a column 'value_counts' with a dict of value->count for each tag. Returns a pandas.DataFrame with columns: ['tag', 'attached_to', 'missing_from', 'unique_values', ('value_counts')].

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
...     coll['A'].add_tag('genotype', 'WT')
...     coll['B'].add_tag('timepoint', 'T1')
>>> info = coll.tags_info(include_value_counts=True)
>>> int(info.loc['genotype','attached_to'])
1
>>> int(info.loc['genotype','missing_from'])
1
>>> int(info.loc['genotype','unique_values'])
1
>>> info.loc['genotype','value_counts']
{'WT': 1}
>>> int(info.loc['timepoint','attached_to'])
1

to_features

to_features() -> FeaturesCollection

Create a FeaturesCollection from this TrackingCollection.

This is a convenience wrapper around FeaturesCollection.from_tracking_collection(self) and preserves grouped structure when the collection is grouped.

Returns:

Type Description
FeaturesCollection

Collection containing one Features object per tracking object in

FeaturesCollection

this collection.

Examples

```pycon >>> import tempfile, shutil >>> from pathlib import Path >>> from py3r.behaviour.tracking.tracking import Tracking >>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection >>> from py3r.behaviour.util.docdata import data_path >>> with tempfile.TemporaryDirectory() as d: ... d = Path(d) ... with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p: ... a = d / 'A.csv'; b = d / 'B.csv' ... _ = shutil.copy(p, a); _ = shutil.copy(p, b) ... tc = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30) ... fc = tc.to_features() >>> from py3r.behaviour.features.features_collection import FeaturesCollection >>> isinstance(fc, FeaturesCollection) True >>> sorted(fc.keys()) ['A', 'B']

```

values

values()

Values iterator (elements or sub-collections).

Examples
>>> import tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> with tempfile.TemporaryDirectory() as d:
...     d = Path(d)
...     with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
...         a = d / 'A.csv'; b = d / 'B.csv'
...         _ = shutil.copy(p, a); _ = shutil.copy(p, b)
...     coll = TrackingCollection.from_dlc({'A': str(a), 'B': str(b)}, fps=30)
>>> len(list(coll.values())) == 2
True