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.
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
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 |
|---|---|---|---|
|
list[TrackingCollection]
|
List of TrackingCollection objects to concatenate, in temporal order. All must have matching keys (handles). |
required |
|
Literal['rezero', 'follow_previous', 'keep_original']
|
How to handle frame indices. |
'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 |
|---|---|---|---|
|
dict[str, str]
|
Mapping of session handle to DLC CSV file path. |
required |
|
float
|
Frame rate of the recording in frames per second. |
required |
|
float
|
Multiplicative correction applied to the x-axis to account for non-square pixels. |
1.0
|
|
type[Tracking]
|
|
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 |
|---|---|---|---|
|
dict[str, str]
|
Mapping of session handle to DLC multi-animal CSV file path. |
required |
|
float
|
Frame rate of the recording in frames per second. |
required |
|
float
|
Multiplicative correction applied to the x-axis to account for non-square pixels. |
1.0
|
|
type[Tracking]
|
|
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 |
|---|---|---|---|
|
dict[str, list]
|
Mapping of group name to list of file paths. Each group must be non-empty. |
required |
|
float
|
Frame rate of the recording in frames per second. |
required |
|
str
|
File format to load. Currently only |
'yolo3r'
|
|
str
|
Tag key used to label each |
'group'
|
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
TrackingCollection
|
A single merged |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a group has an empty path list or |
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 |
|---|---|---|---|
|
dict[str, str]
|
Mapping of session handle to file path. |
required |
|
Callable[..., Tracking]
|
Loader callable, e.g. |
required |
|
type[Tracking]
|
|
Tracking
|
|
Any
|
Extra keyword arguments forwarded to |
{}
|
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 |
|---|---|---|---|
|
dict[str, str]
|
Mapping of session handle to YOLO3R CSV file path. |
required |
|
float
|
Frame rate of the recording in frames per second. |
required |
|
float
|
Multiplicative correction applied to the x-axis to account for non-square pixels. |
1.0
|
|
type[Tracking]
|
|
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 |
|---|---|---|---|
|
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 |
|---|---|---|---|
|
list[Self]
|
Two or more collections of the same concrete type. Every element across all collections must have a unique handle. |
required |
|
bool
|
If True, each leaf is copied (via its |
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 |
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