SummaryCollection
py3r.behaviour.summary.summary_collection.SummaryCollection ¶
SummaryCollection(summary_dict: dict[str, Summary])
Bases: BaseCollection, SummaryCollectionPlotMixin
collection of Summary objects (e.g. for grouping individuals) note: type-hints refer to Summary, but factory methods allow for other classes these are intended ONLY for subclasses of Summary, and this is enforced
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> # add a simple boolean feature to each Features for summaries to consume
>>> for f in fc.values():
... s = pd.Series([True, False] * (len(f.tracking.data)//2 + 1))[:len(f.tracking.data)]
... s.index = f.tracking.data.index
... f.store(s, 'flag', meta={})
>>> sc = SummaryCollection.from_features_collection(fc)
>>> list(sorted(sc.keys()))
['A', 'B']
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
groupby_tags
property
¶
groupby_tags
The tag names used to form this grouped view (or None if flat).
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',)]
from_features_collection
classmethod
¶
from_features_collection(
features_collection: FeaturesCollection,
summary_cls=Summary,
)
creates a SummaryCollection from a FeaturesCollection (flat or grouped)
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> # add numeric scalar per Features via a quick summary to test to_df later
>>> for f in fc.values():
... import numpy as np, pandas as pd
... s = pd.Series(range(len(f.tracking.data)), index=f.tracking.data.index)
... f.store(s, 'counter', meta={})
>>> sc = SummaryCollection.from_features_collection(fc)
>>> isinstance(sc['A'], Summary) and isinstance(sc['B'], Summary)
True
from_list
classmethod
¶
from_list(summary_list: list[Summary])
creates a SummaryCollection from a list of Summary objects, keyed by handle
Examples:
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.features.features import Features
>>> from py3r.behaviour.summary.summary import Summary
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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)
>>> f1, f2 = Features(t1), Features(t2)
>>> # store simple scalar summaries
>>> s1, s2 = Summary(f1), Summary(f2)
>>> s1.store(1, 'count'); s2.store(2, 'count')
>>> sc = SummaryCollection.from_list([s1, s2])
>>> list(sorted(sc.keys()))
['A', 'B']
to_df ¶
to_df(
include_tags: bool = False,
tag_prefix: str = "tag_",
series: Literal["ignore", "separate"] = "ignore",
) -> (
pd.DataFrame
| tuple[pd.DataFrame, dict[str, pd.DataFrame]]
)
Collate values from each Summary.data into tabular output.
- Index: handles of the Summary objects
- Scalar columns: keys from each Summary.data with scalar values
- If include_tags is True, include tag columns with the given prefix
- If series='ignore' (default), Series entries are skipped
- If series='separate', return
(scalars_df, series_tables)whereseries_tablesis{metric_name: dataframe}and each dataframe has one row per handle and one column per Series index value.
Examples:
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.features.features import Features
>>> from py3r.behaviour.summary.summary import Summary
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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)
>>> s1, s2 = Summary(Features(t1)), Summary(Features(t2))
>>> s1.store(1.0, 'score'); s2.store(2.0, 'score')
>>> s1.features.tracking.add_tag('group', 'G1'); s2.features.tracking.add_tag('group', 'G2')
>>> sc = SummaryCollection.from_list([s1, s2])
>>> df = sc.to_df(include_tags=True)
>>> set(df.columns) >= {'score', 'tag_group'}
True
>>> s1.store(pd.Series([1.0, 2.0], index=['A', 'B']), 'speed_by_state')
>>> s2.store(pd.Series([3.0, 4.0], index=['A', 'B']), 'speed_by_state')
>>> scalars, series_tables = sc.to_df(series='separate')
>>> isinstance(scalars, pd.DataFrame) and 'speed_by_state' in series_tables
True
make_bin ¶
make_bin(startframe, endframe)
returns a new SummaryCollection with binned summaries
Examples:
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.features.features import Features
>>> from py3r.behaviour.summary.summary import Summary
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='A', fps=30)
>>> s = Summary(Features(t))
>>> sc = SummaryCollection.from_list([s])
>>> b = sc.make_bin(0, 2)
>>> isinstance(b, SummaryCollection)
True
make_bins ¶
make_bins(numbins)
returns a list of SummaryCollection, one per bin
Examples:
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking import Tracking
>>> from py3r.behaviour.features.features import Features
>>> from py3r.behaviour.summary.summary import Summary
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... t = Tracking.from_dlc(str(p), handle='A', fps=30)
>>> sc = SummaryCollection.from_list([Summary(Features(t))])
>>> bins = sc.make_bins(3)
>>> len(bins) == 3 and all(isinstance(b, SummaryCollection) for b in bins)
True
store ¶
store(
results_dict,
name: str = None,
meta: dict = None,
overwrite: bool = False,
)
Store SummaryResult objects returned by batch methods.
- Flat collection: results_dict is {handle: SummaryResult}
- Grouped collection: results_dict is {group_key: {handle: SummaryResult}}
Examples:
>>> import pandas as pd, tempfile, shutil
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> # add a boolean column for summaries
>>> for f in fc.values():
... m = pd.Series([True, False] * (len(f.tracking.data)//2 + 1))[:len(f.tracking.data)]
... m.index = f.tracking.data.index
... f.store(m, 'mask', meta={})
>>> sc = SummaryCollection.from_features_collection(fc)
>>> rd = {h: s.time_true('mask') for h, s in sc.items()}
>>> sc.store(rd, name='t_mask')
>>> all('t_mask' in s.data for s in sc.values())
True
Returns:
| Type | Description |
|---|---|
str
|
The resolved stored metric name. If auto-naming would resolve to multiple different names across leaves, raises ValueError. |
stored_info ¶
stored_info() -> pd.DataFrame
Summarize stored summary metrics across the collection's leaf Summary objects.
Returns a DataFrame indexed by summary with columns:
- attached_to: number of recordings containing the summary key
- missing_from: number of recordings not containing the summary key
- type: value datatype name when consistent, or a list of datatype names
when mixed across recordings.
bfa ¶
bfa(
column: str,
all_states=None,
numshuffles: int = 1000,
pairs: list[tuple[str, str]] | None = None,
random_state: int | None = 0,
)
Behaviour Flow Analysis between groups for a grouped SummaryCollection.
Requires the collection to be grouped (via groupby). Computes transition matrices per Summary within each group, then computes Manhattan distances between group means and surrogate distributions via shuffling.
If pairs is provided, only those group pairs are analyzed; otherwise all
unique pairs in self.group_keys are evaluated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
int | None
|
Optional seed for deterministic surrogate shuffling. |
0
|
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> # inject simple 2-state labels and tags to build groups
>>> for i, (h, f) in enumerate(fc.items()):
... pat = ['A','A','B','B','A'] * (len(f.tracking.data)//5 + 1)
... states = pd.Series(pat[:len(f.tracking.data)], index=f.tracking.data.index)
... f.store(states, 'state', meta={})
... f.tracking.add_tag('group', f'G{i+1}')
>>> gfc = fc.groupby('group')
>>> sc = SummaryCollection.from_features_collection(gfc)
>>> # compute all pairs
>>> res = sc.bfa('state', all_states=['A','B'], numshuffles=2)
>>> isinstance(res, dict) and 'observed' in next(iter(res.values()))
True
>>> # compute only specific pair(s)
>>> res2 = sc.bfa('state', all_states=['A','B'], numshuffles=2, pairs=[('G1','G2')])
>>> list(res2.keys()) == ['G1_vs_G2']
True
bfa_stats
staticmethod
¶
bfa_stats(
bfa_results: dict[str, dict[str, float]],
) -> dict[str, dict[str, float]]
Compute simple statistics (percentile, zscore, right_tail_p) from bfa results.
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> for i, (h, f) in enumerate(fc.items()):
... pat = ['A','A','B','B','A'] * (len(f.tracking.data)//5 + 1)
... states = pd.Series(pat[:len(f.tracking.data)], index=f.tracking.data.index)
... f.store(states, 'state', meta={})
... f.tracking.add_tag('group', f'G{i+1}')
>>> sc = SummaryCollection.from_features_collection(fc.groupby('group'))
>>> bfa_out = sc.bfa('state', all_states=['A','B'], numshuffles=2)
>>> stats = SummaryCollection.bfa_stats(bfa_out)
>>> set(next(iter(stats.values())).keys()) >= {'percentile','zscore','right_tail_p'}
True
plot_bfa_results
staticmethod
¶
plot_bfa_results(
results: dict[str, dict[str, float]],
compares: str | list[str] | None = None,
add_stats: bool = True,
stats: dict[str, dict[str, float]] | None = None,
bins: int = 50,
figsize: tuple[float, float] = (4, 3),
save_dir: str | None = None,
show: bool = True,
compare: str | None = None,
)
Plot one or more BFA result comparisons as separate single-panel figures.
- If
comparesis None and results contain a single comparison, that one is plotted. - If
comparesis a string, only that comparison is plotted. - If
comparesis a list of strings, each comparison is plotted separately. - If
add_statsis True andstatsnot provided, statistics will be computed viaSummaryCollection.bfa_stats(results)and annotated on each plot.
Returns (fig, ax) for a single comparison, or a dict {compare: (fig, ax)}
for multiple.
Examples:
>>> import tempfile, shutil, os
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> # add simple 2-state labels and tags to build two groups
>>> for i, (h, f) in enumerate(fc.items()):
... pat = ['A','A','B','B','A'] * (len(f.tracking.data)//5 + 1)
... states = pd.Series(pat[:len(f.tracking.data)], index=f.tracking.data.index)
... f.store(states, 'state', meta={})
... f.tracking.add_tag('group', f'G{i+1}')
>>> sc = SummaryCollection.from_features_collection(fc.groupby('group'))
>>> bfa_out = sc.bfa('state', all_states=['A','B'], numshuffles=5)
>>> # plot a single comparison and save it
>>> with tempfile.TemporaryDirectory() as outdir:
... fig, ax = SummaryCollection.plot_bfa_results(
... bfa_out, compare='G1_vs_G2', show=False, save_dir=outdir)
... os.path.exists(os.path.join(outdir, 'G1_vs_G2.png'))
True
plot_transition_umap ¶
plot_transition_umap(
column: str,
all_states=None,
groups: list[str] | list[list[str]] | None = None,
n_neighbors: int = 15,
min_dist: float = 0.1,
random_state: int = 0,
figsize: tuple[float, float] = (4.5, 4),
show: bool = True,
save_dir: str | None = None,
)
Plot a simple UMAP embedding of per-subject transition matrices for selected groups.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Name of the categorical column used to compute transition matrices. |
required |
|
Optional explicit state ordering for transition matrices. |
None
|
|
|
list[str] | list[list[str]] | None
|
|
None
|
|
int
|
UMAP hyperparameters. |
15
|
|
int
|
UMAP hyperparameters. |
15
|
|
int
|
UMAP hyperparameters. |
15
|
|
tuple[float, float]
|
Matplotlib options. |
(4.5, 4)
|
|
tuple[float, float]
|
Matplotlib options. |
(4.5, 4)
|
Returns:
| Type | Description |
|---|---|
(fig, ax): Matplotlib figure and axis.
|
|
Examples:
>>> # xdoctest: +REQUIRES(module: umap)
>>> import tempfile, shutil, os, pandas as pd
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> for i, (h, f) in enumerate(fc.items()):
... pat = ['A','A','B','B','A'] * (len(f.tracking.data)//5 + 1)
... states = pd.Series(pat[:len(f.tracking.data)], index=f.tracking.data.index)
... f.store(states, 'state', meta={})
... f.tracking.add_tag('group', f'G{i+1}')
>>> sc = SummaryCollection.from_features_collection(fc.groupby('group'))
>>> with tempfile.TemporaryDirectory() as outdir:
... fig, ax = sc.plot_transition_umap(
... column='state', all_states=['A','B'], groups=['G1','G2'],
... show=False, save_dir=outdir)
... os.path.exists(os.path.join(outdir, 'transition_umap.png'))
True
plot_chord ¶
plot_chord(
column: str,
all_states: list[str | int] | None = None,
*,
fromkey: str | None = None,
plot_individual: bool = False,
show: bool = True,
save_dir: str | None = None,
cmap: str | list | None = None,
**kwargs,
)
Plot chord diagrams of state transitions using a minimal pattern.
- If not grouped:
- plot_individual=False: sum over the collection and plot a single chord.
- plot_individual=True: plot one chord per recording.
- If grouped:
- plot_individual=False: sum within each group and plot one chord per group.
- plot_individual=True: plot one chord per recording per group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Name of the categorical column used to compute transitions. |
required |
|
list[str | int] | None
|
Optional explicit state ordering for transition matrices.
Required when |
None
|
|
str | None
|
Optional key in each |
None
|
|
bool
|
If True, plot per recording; otherwise plot summed aggregate. |
False
|
|
bool
|
If True, display figures. |
True
|
|
str | None
|
Optional directory to save figures; created if missing. |
None
|
|
Additional keyword arguments to pass to pycirclize.Circos.chord_diagram. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
object |
|
Examples:
>>> # xdoctest: +REQUIRES(module: pycirclize)
>>> import tempfile, os, shutil
>>> import pandas as pd
>>> from pathlib import Path
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> with tempfile.TemporaryDirectory() as d:
... d = Path(d)
... # create two recordings from the sample csv
... with data_path('py3r.behaviour.tracking._data', 'dlc_single.csv') as p:
... _ = shutil.copy(p, d / 'A.csv'); _ = shutil.copy(p, d / 'B.csv')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
... # build features and inject a simple 3-state sequence
... fc = FeaturesCollection.from_tracking_collection(tc)
... for _, f in fc.items():
... pat = ['0','1','2','1','0'] * (len(f.tracking.data)//5 + 1)
... seq = pd.Series(pat[:len(f.tracking.data)], index=f.tracking.data.index)
... f.store(seq, 'state', meta={})
... sc = SummaryCollection.from_features_collection(fc)
... # plot flat aggregate and save it
... with tempfile.TemporaryDirectory() as outdir:
... _ = sc.plot_chord(
... 'state', all_states=['0','1','2'], show=False, save_dir=outdir)
... os.path.exists(os.path.join(outdir, 'chord_state.png'))
True
prepare_plot ¶
prepare_plot(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
merge_by: str | None = "metric",
ax=None,
figsize=None,
)
Prepare a tidy DataFrame and seaborn kwargs without drawing anything.
This is the single entry point for all plot data preparation. The
convenience sns* methods call this internally; power users can
call it directly for full control over the seaborn call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult or list[str | BatchResult]
|
Metric to prepare. Lists are merged into a single plot-ready metric. |
required |
|
dict[str, list] | None
|
|
None
|
|
list[str] | str | None
|
Override spatial sort priority (which tag is the primary x-axis
sort dimension). Colours are unaffected — they always follow
the |
None
|
|
('metric', 'component')
|
Used only when metric is a list with more than one item. Controls
whether merged labels are arranged as |
"metric"
|
|
Axes
|
Axes to plot on. If None, a new figure is created with auto-calculated size. |
None
|
|
tuple[float, float]
|
Override the automatic figure size. |
None
|
Returns:
| Type | Description |
|---|---|
PlotSpec
|
A namespace with the following attributes:
|
Examples:
Basic power-user workflow::
import seaborn as sns
spec = sc_grouped.prepare_plot(
"total_distance",
group_order=GROUP_ORDER,
sort_by="timepoint",
)
# Full seaborn control — override anything you like
sns.boxplot(**spec.sns_kwargs, width=0.6)
spec.ax.set_title("My custom title")
spec.fig.savefig("custom.png", dpi=300)
Composing multiple layers::
spec = sc_grouped.prepare_plot(metric, group_order=ORDER)
sns.barplot(**spec.sns_kwargs, errorbar=None, alpha=0.4)
sns.stripplot(**spec.sns_kwargs, size=4, jitter=True)
snsstrip ¶
snsstrip(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Strip plot (jittered scatter) using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Axes
|
Axes to plot on. If None, creates new figure. |
None
|
|
bool
|
Display the plot. Default True. |
True
|
|
str | None
|
Directory to save figure. |
None
|
|
str | None
|
Custom filename. |
None
|
|
str | None
|
Plot title. |
None
|
|
Passed to seaborn.stripplot (e.g., jitter, alpha, size, palette).
Also accepts |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> for f in fc.values():
... idx = f.tracking.data.index[:30]
... f.store(pd.Series(([True, False] * 15)[:len(idx)], index=idx),
... 'active', meta={})
>>> sc = SummaryCollection.from_features_collection(fc)
>>> fig, ax, df = sc.snsstrip(sc.each.time_in_state('active'), show=False)
>>> isinstance(df, pd.DataFrame)
True
snsswarm ¶
snsswarm(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Swarm plot (non-overlapping scatter) using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Passed to seaborn.swarmplot (e.g., size, palette). |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
snsbar ¶
snsbar(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Bar plot with error bars using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Passed to seaborn.barplot (e.g., errorbar, palette, saturation). |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
snsbox ¶
snsbox(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Box plot using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Passed to seaborn.boxplot (e.g., width, palette, fliersize). |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
snsviolin ¶
snsviolin(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Violin plot using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Passed to seaborn.violinplot (e.g., inner, split, palette). |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
snspoint ¶
snspoint(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
**kwargs,
)
Point plot (mean + CI) using seaborn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Save/display options. |
None
|
|
|
Passed to seaborn.pointplot (e.g., errorbar, markers, linestyles). |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
snssuperplot ¶
snssuperplot(
metric,
*,
group_order: dict | None = None,
sort_by: list | str | None = None,
annotate=None,
ax=None,
show: bool = True,
savedir: str | None = None,
filename: str | None = None,
title: str | None = None,
ylabel: str | None = None,
bar_kwargs: dict | None = None,
strip_kwargs: dict | None = None,
**kwargs,
)
Superplot: bar plot (mean) with strip plot (individual dots) overlay.
This is the "publication-ready" visualization showing mean bars with individual data points scattered on top, commonly used in scientific papers. The dots are constrained within the bar width by default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str or BatchResult
|
Either a key from Summary.data, or a BatchResult from a batch method. |
required |
|
dict[str, list] | None
|
Control group display order. See :meth: |
None
|
|
list[str] | str | None
|
Override spatial sort priority. See :meth: |
None
|
|
str or dict or None
|
Statistical annotations. See :meth: |
None
|
|
Axes
|
Axes to plot on. If None, creates new figure. |
None
|
|
bool
|
Display the plot. Default True. |
True
|
|
str | None
|
Directory to save figure. |
None
|
|
str | None
|
Custom filename. |
None
|
|
str | None
|
Plot title. |
None
|
|
str | None
|
Y-axis label. Auto-detected from metric when None. |
None
|
|
dict | None
|
Extra kwargs for barplot (e.g., errorbar, capsize, saturation). |
None
|
|
dict | None
|
Extra kwargs for stripplot (e.g., alpha, size, jitter). |
None
|
|
Common kwargs passed to both plots (e.g., palette, dodge).
Also accepts |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Figure, Axes, DataFrame]
|
|
Examples:
>>> import tempfile, shutil
>>> from pathlib import Path
>>> import pandas as pd
>>> from py3r.behaviour.util.docdata import data_path
>>> from py3r.behaviour.tracking.tracking_collection import TrackingCollection
>>> from py3r.behaviour.features.features_collection import FeaturesCollection
>>> from py3r.behaviour.summary.summary_collection import SummaryCollection
>>> 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')
... tc = TrackingCollection.from_dlc({'A': str(d/'A.csv'), 'B': str(d/'B.csv')}, fps=30)
>>> fc = FeaturesCollection.from_tracking_collection(tc)
>>> for f in fc.values():
... n = len(f.tracking.data)
... states = pd.Series((['A', 'B', 'A'] * (n // 3 + 1))[:n],
... index=f.tracking.data.index)
... f.store(states, 'zone', meta={})
>>> sc = SummaryCollection.from_features_collection(fc)
>>> fig, ax, df = sc.snssuperplot(sc.each.time_in_state('zone'), show=False)
>>> isinstance(df, pd.DataFrame)
True
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
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']
merge
classmethod
¶
merge(collections, *, copy=False)
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[BaseCollection]
|
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 |
|---|---|
BaseCollection
|
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
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',)]
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']
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']
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',)]
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
map_leaves ¶
map_leaves(fn)
Apply a function to every leaf element and return a new collection of the same type. Preserves grouping shape and groupby metadata when grouped.
fn: callable(Element) -> ElementLike
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
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']
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)
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']