Tracking
py3r.behaviour.tracking.tracking.Tracking ¶
Tracking(
data: DataFrame,
meta: dict[str, Any],
handle: str,
tags: dict[str, str] = None,
)
Represent frame-by-frame tracked keypoints with convenience loaders and tools.
A Tracking holds a pandas DataFrame of columns like p1.x, p1.y,
p1.z, p1.likelihood with index named frame. Most users create
objects via factory methods and then call instance methods to process or
analyze trajectories.
Quick start with realistic CSVs stored in the package data:
- Load from DLC CSV
- Load from DLC multi-animal CSV
- Load from YOLO3R CSV
- Inspect points, distances
- Filter, interpolate, smooth
- Rescale by known distance, trim, check time
- Save and slice (
loc/iloc) - Minimal plotting
Examples¶
Minimal DLC example:
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> len(t.data), t.meta['fps'], t.handle
(5, 30.0, 'ex')
>>> t.data[['p1.x','p1.y','p1.z','p1.likelihood']].head(2).reset_index().values.tolist()
[[0.0, 0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 2.0, 3.0, 0.75]]
Load from DLC multi-animal (DLCMA):
>>> with data_path('py3r.behaviour.tracking._data', 'dlcma_multi.csv') as p_ma:
... tma = Tracking.from_dlcma(str(p_ma), handle='ma', fps=30)
>>> tma.meta['fps'], tma.handle
(30.0, 'ma')
Load from YOLO3R (3D columns present):
>>> with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p_y:
... ty = Tracking.from_yolo3r(str(p_y), handle='y3r', fps=30)
>>> 'p1.z' in ty.data.columns and 'p1.likelihood' in ty.data.columns
True
>>> ty.data[['p1.x','p1.y','p1.z','p1.likelihood']].head(2).reset_index().values.tolist()
[[0.0, 0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 2.0, 3.0, 0.9]]
Inspect points and distances:
>>> names = t.get_point_names()
>>> sorted(names)[:3]
['p1', 'p2', 'p3']
>>> d = t.distance_between('p1', 'p2')
>>> len(d) == len(t.data)
True
Filter low-likelihood positions and interpolate:
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t2 = Tracking.from_dlc(str(p), handle='ex2', fps=30)
>>> _ = t2.filter_likelihood(0.2)
>>> import numpy as np
>>> bool(np.isnan(t2.data['p1.x']).any())
True
>>> _ = t2.interpolate(method='nearest', limit=1)
>>> has_lik = t2.data.columns.str.endswith('.likelihood').any()
>>> interp_ok = t2.meta['interpolation']['method'] == 'nearest'
>>> has_lik and interp_ok
True
Smooth all points with default window=3 rolling mean, and optional exception for point 'p1':
>>> _ = t.smooth_all(3, 'mean',[(['p1'],'median',4)])
>>> 'smoothing' in t.meta
True
Rescale by known distance between two points (uniform across dims):
>>> _ = t.rescale_by_known_distance('p1', 'p2', distance_in_metres=2.0)
>>> t.meta['distance_units']
'm'
Trim frames and verify time window:
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t3 = Tracking.from_dlc(str(p), handle='ex3', fps=30)
>>> _ = t3.trim(startframe=2, endframe=4)
>>> bool(t3.data.index[0] == 2 and t3.data.index[-1] == 4)
True
>>> bool(t3.time_as_expected(mintime=0.0, maxtime=10.0))
True
Save to a directory (parquet backend) and load back:
>>> import os, tempfile
>>> with tempfile.TemporaryDirectory() as d:
... _ = t.save(d, data_format='csv',overwrite=True)
... t_loaded = Tracking.load(d)
>>> isinstance(t_loaded, Tracking) and len(t_loaded.data) == len(t.data)
True
Slice with loc/iloc and keep handle:
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t4 = Tracking.from_dlc(str(p), handle='ex4', fps=30)
>>> t4s = t4.loc[0:3]
>>> isinstance(t4s, Tracking) and t4s.handle == 'ex4'
True
>>> t4s2 = t4.iloc[0:2]
>>> isinstance(t4s2, Tracking) and len(t4s2.data) == 2
True
Minimal plotting (no display):
>>> _ = t.plot(show=False)
Tagging and user metadata:
>>> t.add_tag('session', 'S1')
>>> t.tags['session']
'S1'
>>> t.add_usermeta({'group': 'G1'}, overwrite=True)
>>> t.meta['usermeta']['group']
'G1'
iloc
property
¶
iloc
Return a new Tracking object with self.data sliced by np.iloc.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.data.shape
(5, 12)
>>> t.iloc[0:2,0].data.shape
(2,)
>>> t.iloc[0:2,0].handle
'ex'
loc
property
¶
loc
Return a new Tracking object with self.data sliced by np.loc.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.data.shape
(5, 12)
>>> t.loc[0:2,'p1.x'].data.shape
(3,)
>>> t.loc[0:2].handle
'ex'
add_tag ¶
add_tag(
tagname: str, tagvalue: str, overwrite: bool = False
) -> None
Adds or updates a tag.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.add_tag('session', 'S1', overwrite=True)
>>> t.tags['session']
'S1'
add_usermeta ¶
add_usermeta(
usermeta: dict, overwrite: bool = False
) -> None
Adds or updates user-defined metadata.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.add_usermeta({'group': 'G1'}, overwrite=True)
>>> t.meta['usermeta']['group']
'G1'
animation_stream ¶
animation_stream(
*,
points: list[str],
lines: list[tuple[str, str]] | None = None,
features: list[str | None]
| dict[str | None, str | None]
| None = None,
dims: tuple[str, ...] = ("x", "y"),
view: dict | None = None,
canvas_size: tuple[int, int] = (800, 800),
bg_color: tuple[int, int, int] = (0, 0, 0),
style: dict | None = None,
pixel_coords: bool = False,
undo_meta_scaling: bool = False,
) -> AnimationStream
Build an OpenCV-backed frame stream for animated point/line overlays.
For style dict documentation and worked examples, see the Animation guide.
This method precomputes the selected point coordinates (and optional 3D projection) once, then returns a stream object that can:
- fetch individual rendered frames via
get_frame(i) - iterate sequentially via
read()/next() - play live via
stream.play(...) - save video via
stream.save(...)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[str]
|
Point names to render as circles. |
required |
|
list[tuple[str, str]] | None
|
Line segments connecting point pairs. Endpoints can include points
not listed in |
None
|
|
list[str | None] | dict[str | None, str | None] | None
|
Per-frame scalar columns to render as text overlays. If a list is
provided, each column is shown as |
None
|
|
tuple[str, ...]
|
Coordinate dimensions. Use 2D ( |
('x', 'y')
|
|
dict | None
|
3D camera options used only when |
None
|
|
tuple[int, int]
|
Canvas size as |
(800, 800)
|
|
tuple[int, int, int]
|
Background color in BGR. |
(0, 0, 0)
|
|
dict | None
|
Style overrides for points/lines/boundaries. |
None
|
|
bool
|
If True, interpret coordinates as absolute pixel locations. If False, auto-fit projected coordinates to the canvas. |
False
|
|
bool
|
If True, invert |
False
|
Returns:
| Type | Description |
|---|---|
AnimationStream
|
Stream object with |
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path("py3r.behaviour.tracking._data", "dlc_single.csv") as p:
... t = Tracking.from_dlc(str(p), handle="ex", fps=30)
>>> style = {
... "points": {
... "default": {"color": (0, 255, 255), "radius": 3}, # default
... "p1": {"color": (0, 255, 0), "radius": 5}, # static override
... "p2": { # dynamic override
... "radius": {"from": "p1.likelihood", "map": {1.0: 6, "default": 2}}
... },
... }
... }
>>> stream = t.animation_stream(
... points=["p1", "p2"],
... lines=[("p1", "p2")],
... pixel_coords=True,
... canvas_size=(96, 72),
... style=style,
... )
>>> stream.frame_count
5
>>> frame0 = stream.get_frame(0)
>>> frame0.shape
(72, 96, 3)
coarse_grain ¶
coarse_grain(
window: int,
method: Literal[
"mean", "median", "min", "max"
] = "mean",
non_numeric: Literal[
"drop", "nan", "first", "mode", "error"
] = "drop",
) -> Self
Coarse-grain tracking data over fixed, non-overlapping windows.
Numeric columns are aggregated with method within each window of
window rows. The result is reindexed from 0 and fps is divided
by window to reflect the new effective frame rate. A
"coarse_grain" entry is appended to meta["transforms"].
Non-numeric columns (e.g. string annotations) are handled according to
non_numeric; the default "drop" removes them from the output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
int
|
Number of consecutive rows to collapse into one. |
required |
|
Literal['mean', 'median', 'min', 'max']
|
Aggregation applied to numeric columns within each window. |
'mean'
|
|
Literal['drop', 'nan', 'first', 'mode', 'error']
|
How to handle non-numeric columns. |
'drop'
|
Returns:
| Type | Description |
|---|---|
Self
|
New |
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> len(t.data)
5
>>> t.meta['fps']
30.0
Coarse-graining by 2 halves the row count and fps (incomplete windows are kept):
>>> t2 = t.coarse_grain(2)
>>> len(t2.data)
3
>>> t2.meta['fps']
15.0
>>> t2.handle
'ex'
The 5-row input produces 3 windows: two complete (rows 0–1, rows 2–3) and one partial (row 4 alone). Incomplete trailing windows are retained rather than dropped, so no data is lost.
The first window's mean p1.x is (0.0 + 1.0) / 2 = 0.5:
>>> float(round(t2.data['p1.x'].iloc[0], 6))
0.5
The transform is recorded in meta:
>>> t2.meta['transforms'][-1]
{'type': 'coarse_grain', 'window': 2, 'method': 'mean'}
Using method='max' takes the per-window maximum instead:
>>> t_max = t.coarse_grain(2, method='max')
>>> float(round(t_max.data['p1.x'].iloc[0], 6))
1.0
concat
classmethod
¶
concat(
trackings: list[Self],
*,
handle: str | None = None,
reindex: Literal[
"rezero", "follow_previous", "keep_original"
] = "follow_previous",
) -> Self
Concatenate multiple Tracking objects along the time (frame) axis.
All Tracking objects must have: - Matching fps - Identical column names (same tracked points and dimensions)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Self]
|
List of Tracking objects to concatenate, in temporal order. |
required |
|
str | None
|
Handle for the concatenated object. If None, uses first object's handle. |
None
|
|
Literal['rezero', 'follow_previous', 'keep_original']
|
How to handle frame indices. |
'follow_previous'
|
Returns:
| Type | Description |
|---|---|
Self
|
A new Tracking object containing all frames from input objects. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If trackings is empty, fps values don't match, or columns differ. |
Examples:
Concatenate two tracking objects:
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t1 = Tracking.from_dlc(str(p), handle='ex1', fps=30)
... t2 = Tracking.from_dlc(str(p), handle='ex2', fps=30)
>>> combined = Tracking.concat([t1, t2], handle='combined')
>>> len(combined.data) == len(t1.data) + len(t2.data)
True
>>> combined.handle
'combined'
>>> combined.meta['fps']
30.0
Verify column preservation:
>>> list(combined.data.columns) == list(t1.data.columns)
True
Concatenation metadata is recorded:
>>> 'concat' in combined.meta
True
>>> combined.meta['concat']['n_chunks']
2
copy ¶
copy() -> Self
Creates a copy of an existing tracking object.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlcma_multi.csv') as p:
... t = Tracking.from_dlcma(str(p), handle='ma', fps=30)
>>> t_copy = t.copy()
>>> len(t_copy.data), t_copy.meta['fps'], t_copy.handle
(4, 30.0, 'ma')
define_midpoint ¶
define_midpoint(
name: str,
points: list[str] | dict[str, float],
*,
inplace: bool = True,
) -> Tracking | None
Define a new point as the (optionally weighted) midpoint of existing points.
Spatial dimensions are inferred from the source points and must be consistent across all of them. Likelihood is taken as the per-frame minimum across all source points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Name for the new derived point. |
required |
|
list[str] | dict[str, float]
|
Source point names with equal weighting (list), or a mapping of
point name to relative weight (dict). Weights are normalised
internally, so |
required |
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
Tracking | None
|
|
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path("py3r.behaviour.tracking._data", "dlc_single.csv") as p:
... t = Tracking.from_dlc(str(p), handle="ex", fps=30)
>>> t.define_midpoint("mid12", ["p1", "p2"])
>>> "mid12" in t.get_point_names()
True
>>> mid_x = float(t.data["mid12.x"].iloc[0])
>>> p1_x = float(t.data["p1.x"].iloc[0])
>>> p2_x = float(t.data["p2.x"].iloc[0])
>>> mid_x == (p1_x + p2_x) / 2
True
>>> "mid12.z" in t.data.columns
True
>>> bool(all(t.data["mid12.likelihood"] <= t.data["p1.likelihood"]))
True
>>> bool(all(t.data["mid12.likelihood"] <= t.data["p2.likelihood"]))
True
Weighted example — p1 carries three times the weight of p2:
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path("py3r.behaviour.tracking._data", "dlc_single.csv") as p:
... t = Tracking.from_dlc(str(p), handle="ex", fps=30)
>>> t.define_midpoint("wt_mid", {"p1": 3, "p2": 1})
>>> val = float(t.data["wt_mid.x"].iloc[0])
>>> expected = 0.75 * float(t.data["p1.x"].iloc[0]) + 0.25 * float(t.data["p2.x"].iloc[0])
>>> abs(val - expected) < 1e-10
True
define_offset_point ¶
define_offset_point(
name: str,
reference: str,
offset: tuple[float, ...],
*,
inplace: bool = True,
) -> Tracking | None
Define a new point as a fixed spatial offset from an existing point.
The offset is added to every frame's coordinates of the reference point. Likelihood is inherited directly from the reference point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Name for the new derived point. |
required |
|
str
|
Name of the existing point to offset from. |
required |
|
tuple[float, ...]
|
Per-dimension displacement, e.g. |
required |
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
Tracking | None
|
|
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path("py3r.behaviour.tracking._data", "dlc_single.csv") as p:
... t = Tracking.from_dlc(str(p), handle="ex", fps=30)
>>> t.define_offset_point("p1_shifted", "p1", offset=(10.0, 0.0, 0.0))
>>> bool(all(t.data["p1_shifted.x"] == t.data["p1.x"] + 10.0))
True
>>> bool(all(t.data["p1_shifted.y"] == t.data["p1.y"]))
True
>>> bool(all(t.data["p1_shifted.likelihood"] == t.data["p1.likelihood"]))
True
distance_between ¶
distance_between(
point1: str, point2: str, dims=("x", "y")
) -> pd.Series
Framewise distance between two points.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> d = t.distance_between('p1', 'p2')
>>> len(d) == len(t.data)
True
filter_likelihood ¶
filter_likelihood(
threshold: float, *, inplace: bool = True
) -> Tracking | None
Set position values with likelihood below threshold to np.nan.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> import numpy as np
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.filter_likelihood(0.5)
>>> bool(np.isnan(t.data.filter(like='.x')).any().any())
True
>>> bool(np.isnan(t.data['p1.x'].values[-1]))
True
>>> float(t.data['p1.likelihood'].values[0])
1.0
from_dlc
classmethod
¶
from_dlc(
filepath: str | Path,
*,
handle: str,
fps: float,
aspectratio_correction: float = 1.0,
tags: dict[str, str] | None = None,
) -> Self
Loads a Tracking object from a (single animal) deeplabcut tracking csv.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> len(t.data), t.meta['fps'], t.handle
(5, 30.0, 'ex')
from_dlcma
classmethod
¶
from_dlcma(
filepath: str | Path,
*,
handle: str,
fps: float,
aspectratio_correction: float = 1.0,
tags: dict[str, str] | None = None,
) -> Self
Loads a Tracking object from a multi-animal deeplabcut tracking csv.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlcma_multi.csv') as p:
... t = Tracking.from_dlcma(str(p), handle='ma', fps=30)
>>> len(t.data), t.meta['fps'], t.handle
(4, 30.0, 'ma')
from_yolo3r
classmethod
¶
from_yolo3r(
filepath: str | Path,
*,
handle: str,
fps: float,
aspectratio_correction: float = 1.0,
tags: dict[str, str] | None = None,
) -> Self
Loads a Tracking object from a single- or multi-animal yolo csv in 3R hub format.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'yolo3r.csv') as p:
... t = Tracking.from_yolo3r(str(p), handle='y3r', fps=30)
>>> 'p1.z' in t.data.columns and 'p1.likelihood' in t.data.columns
True
get_point_data ¶
get_point_data(
point: str, dims: Iterable[str] | None = None
) -> pd.DataFrame
For a specific point, returns the DataFrame with all dimensions data.
colnames are reformated to drop the pointname (i.e p1.x -> x).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
name of the point for which data should be exteracted |
required |
|
Iterable[str] | None
|
dimensons which should exclusively be returned |
None
|
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> df = t.get_point_data('p1')
>>> df['x'].values
array([0, 1, 2, 3, 4])
get_point_dimensions ¶
get_point_dimensions(point: str) -> list[str]
Return viable dimension names associated with a point.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.get_point_dimensions('p1')
['x', 'y', 'z', 'likelihood']
get_point_names ¶
get_point_names() -> list
List of tracked point names, sorted alphabetically (ascending).
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> names = t.get_point_names()
>>> set(['p1','p2','p3']).issubset(names)
True
>>> sorted_names = sorted(names)
>>> sorted_names == names
True
interpolate ¶
interpolate(
method: str = "linear",
limit: int = 1,
*,
inplace: bool = True,
**kwargs,
) -> Tracking | None
Interpolate missing position data and set likelihood columns to NaN.
Uses pandas.DataFrame.interpolate() with kwargs.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> import numpy as np
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.filter_likelihood(0.5)
>>> t.interpolate(method='linear', limit=1)
>>> 'interpolation' in t.meta
True
load
classmethod
¶
load(dirpath: str) -> Self
Load a Tracking (or subclass) previously saved with save().
Examples¶
>>> import tempfile
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> with tempfile.TemporaryDirectory() as d:
... t.save(d, data_format='csv', overwrite=True)
... t2 = Tracking.load(d)
>>> isinstance(t2, Tracking) and len(t2.data) == len(t.data)
True
plot ¶
plot(
trajectories: list[str]
| dict[str, Series]
| None = None,
static: list[str] | None = None,
lines: list[tuple[str, str]] | None = None,
dims: tuple[str, ...] = ("x", "y"),
ax: Any | None = None,
title: str | None = None,
show: bool = True,
savedir: str | None = None,
elev: float = 30,
azim: float = 45,
) -> tuple[Any, Any]
Plot trajectories and static points for this Tracking object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[str] | dict[str, Series] | None
|
Point names to plot as trajectories, or a dict mapping
point name to a color |
None
|
|
list[str] | None
|
Point names to plot as static markers (median position). |
None
|
|
list[tuple[str, str]] | None
|
Pairs of point names to connect with a line. |
None
|
|
tuple[str, ...]
|
Coordinate dimensions to plot. Use |
('x', 'y')
|
|
Any | None
|
Existing matplotlib axis to draw on. |
None
|
|
str | None
|
Plot title. Defaults to |
None
|
|
bool
|
Whether to call |
True
|
|
str | None
|
If provided, saves the figure as |
None
|
|
float
|
Elevation angle for 3D plots. |
30
|
|
float
|
Azimuth angle for 3D plots. |
45
|
Returns:
| Type | Description |
|---|---|
tuple[Any, Any]
|
Tuple of |
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> _ = t.plot(show=False)
points_to_numpy ¶
points_to_numpy(
points: list[str],
dims: tuple[str, ...] = ("x", "y"),
*,
undo_meta_scaling: bool = False,
) -> tuple[list[str], np.ndarray]
Resolve selected point coordinates to a NumPy array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[str]
|
Point names to extract. |
required |
|
tuple[str, ...]
|
Coordinate dimensions to extract (2D or 3D). |
('x', 'y')
|
|
bool
|
If True, invert |
False
|
Returns:
| Type | Description |
|---|---|
tuple[list[str], ndarray]
|
|
Examples¶
>>> import pandas as pd
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> df = pd.DataFrame(
... {
... "nose.x": [1.0, 2.0],
... "nose.y": [3.0, 4.0],
... "tail.x": [5.0, 6.0],
... "tail.y": [7.0, 8.0],
... }
... )
>>> t = Tracking(df, meta={"fps": 30.0}, handle="demo")
>>> names, arr = t.points_to_numpy(["nose", "tail"], dims=("x", "y"))
>>> names
['nose', 'tail']
>>> arr.shape
(2, 2, 2)
rescale_by_known_distance ¶
rescale_by_known_distance(
point1: str,
point2: str,
distance_in_metres: float,
dims=("x", "y"),
*,
inplace: bool = True,
) -> Tracking | None
Rescale all dims by known distance between two points.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.rescale_by_known_distance('p1','p2', 2.0)
>>> t.meta['distance_units']
'm'
save ¶
save(
dirpath: str,
*,
data_format: str = "parquet",
overwrite: bool = False,
) -> None
Save this Tracking into a self-describing directory for exact round-trip.
Examples¶
>>> import tempfile, os
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> with tempfile.TemporaryDirectory() as d:
... t.save(d, data_format='csv', overwrite=True)
... os.path.exists(os.path.join(d, 'manifest.json'))
True
save_3d_tracking_video_multi_view ¶
save_3d_tracking_video_multi_view(
out_path: str,
lines: list[tuple[str, str]] = None,
point_size=40,
line_width=2,
point_color="b",
line_color="k",
dpi=150,
writer="pillow",
startframe=None,
endframe=None,
xlim=None,
ylim=None,
zlim=None,
robust_percentile=1,
invert_z=True,
)
Save a 3D animation of tracked points to a video file.
Renders four subplots per frame (front, side, top, and isometric views). Axis limits can be set manually or derived from robust percentiles. Enforces equal aspect ratio for all axes.
set_point_data ¶
set_point_data(
df: DataFrame, point: str, target_df: DataFrame = None
)
Set the data of a point from an external DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
DataFrame
|
DataFrame containing the point data to write. Column names must
reflect the dimension names (e.g. |
required |
|
str
|
Name of the point to overwrite. |
required |
|
DataFrame
|
An external copy of |
None
|
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> df = t.get_point_data('p1')
>>> df['x'].values
array([0, 1, 2, 3, 4])
>>> df['x'] += 1
>>> t.set_point_data(df,'p1')
>>> t.data['p1.x'].values
array([1, 2, 3, 4, 5])
>>> external_df = t.data.copy()
>>> external_df['p1.x'].values
array([1, 2, 3, 4, 5])
>>> df['x'] += 1
>>> t.set_point_data(df = df, point = 'p1', target_df = external_df)
>>> external_df['p1.x'].values
array([2, 3, 4, 5, 6])
smooth_all ¶
smooth_all(
window: int | None = 11,
method: Literal["mean", "median", "savgol"] = "savgol",
overrides: list[
tuple[
list[str] | tuple[str, ...] | str,
str,
int | None,
]
]
| None = None,
dims: tuple[str, ...] = ("x", "y"),
strict: bool = False,
inplace: bool = True,
smoother: Any | None = None,
smoother_kwargs: dict | None = None,
method_kwargs: dict | None = None,
**kwargs: Any,
) -> Tracking | None
Smooth all tracked points using a default method/window.
Supports optional per-point override groups.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
int | None
|
Default smoothing window applied to any point without an override. |
11
|
|
Literal['mean', 'median', 'savgol']
|
Default smoothing method applied to any point without an override. |
'savgol'
|
|
list[tuple[list[str] | tuple[str, ...] | str, str, int | None]] | None
|
Optional list of |
None
|
|
tuple[str, ...]
|
Coordinate dimensions to smooth. |
('x', 'y')
|
|
bool
|
If True, require an effective window for every point. |
False
|
|
bool
|
If True, mutate self and return None; if False, return a new object. |
True
|
|
Any | None
|
Custom smoother callable, passed directly to |
None
|
|
dict | None
|
Keyword arguments forwarded to the custom |
None
|
|
dict | None
|
Extra kwargs applied to the default method for all points
(e.g. |
None
|
|
Any
|
Additional method kwargs merged into per-point specs (e.g.
|
{}
|
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> t.smooth_all(3, 'mean', overrides=[(['p1'], 'median', 4)])
>>> 'smoothing' in t.meta
True
strip_column_names ¶
strip_column_names(
*, inplace: bool = True
) -> Tracking | None
Strip column names to the last two dot-delimited sections.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> before = list(t.data.columns)[:3]
>>> t.strip_column_names()
>>> after = list(t.data.columns)[:3]
>>> all(len(c.split('.')) == 2 for c in after)
True
time_as_expected ¶
time_as_expected(mintime: float, maxtime: float) -> bool
Check that total tracking duration (seconds) is between mintime and maxtime.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> bool(t.time_as_expected(0.0, 1.0)) # between 0 and 1 second
True
>>> bool(t.time_as_expected(0.0, 0.1)) # less than 0.1 seconds
False
to_features ¶
to_features() -> Features
Create a Features object from this Tracking.
This is a convenience wrapper around Features(self).
Returns:
| Type | Description |
|---|---|
Features
|
A new features object linked to this tracking object. |
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='demo', fps=30)
>>> f = t.to_features()
>>> from py3r.behaviour.features.features import Features
>>> isinstance(f, Features)
True
>>> f.handle
'demo'
trim ¶
trim(
startframe: int | None = None,
endframe: int | None = None,
*,
inplace: bool = True,
) -> Tracking | None
Trims the tracking data object between startframe and endframe.
Examples¶
>>> from py3r.behaviour.util.docdata import data_path
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='ex', fps=30)
>>> _ = t.trim(1, 3)
>>> int(t.data.index[0]), int(t.data.index[-1])
(1, 3)