Bring your own data#
Drag & drop handles one recording session whose files all start together. Everything else — trials split across files, media on separate clocks — needs a session file plus an alignment file, built in the Data wizard or from a short Python script.
The variables, attributes and dimensions EthoGraph expects, per backend.
Grouping datasets into a trial structure, or splitting a continuous recording.
Pair media files, or align video to a recording system with neuroconv.
Supported formats, Kilosort output, and the two trace viewers.
Session, project and home folders: what lives where and who writes it.
EthoGraph supports three backends. Pick the one matching your workflow; every page has a tab per backend where they differ.
Backend |
Best for |
Core object |
|---|---|---|
xarray |
Custom datasets, pose estimation, multi-dim arrays |
|
Pynapple |
Neuroscience time-series, NWB interop |
|
NWB |
Standardised neurodata, DANDI archives |
|
Note
NWB needs almost none of this. An .nwb file already stores trials, media
references and features together, so it loads directly. The NWB tabs only
note where behaviour differs.
Putting it together#
A complete two-camera, ten-trial setup: Dataset schema and Trials for step 1, Media alignment (w. neuroconv) for step 2.
import numpy as np
import xarray as xr
import ethograph as eto
# 1) One Dataset per trial
datasets = []
for trial_id in range(1, 11):
n_time = 9000 # 5 minutes at 30 fps
ds = xr.Dataset(
data_vars={
"position": xr.DataArray(
np.random.randn(n_time, 2, 4, 2),
dims=["time", "space", "keypoint", "individual"],
),
"speed": xr.DataArray(
np.abs(np.random.randn(n_time, 4, 2)),
dims=["time", "keypoint", "individual"],
),
},
coords={
"time": np.arange(n_time) / 30.0,
"space": ["x", "y"],
"keypoint": ["nose", "left_ear", "right_ear", "tail"],
"individual": ["mouse1", "mouse2"],
},
)
ds.attrs["trial"] = trial_id
ds.attrs["fps"] = 30.0
datasets.append(ds)
dt = eto.from_datasets(datasets)
dt.save("session.nc")
# 2) Pairing: media files + trial timing
sources = [
eto.SourceSpec("video", device="cam-1", folder="video/cam1"),
eto.SourceSpec("video", device="cam-2", folder="video/cam2"),
eto.SourceSpec("pose", device="cam-1", folder="pose/cam1"),
eto.SourceSpec("pose", device="cam-2", folder="pose/cam2"),
]
trial_table = eto.discover_media(".", sources) # 10 rows, natural sort order
trial_table["start_time"] = [i * 300.0 for i in range(10)]
trial_table["stop_time"] = [(i + 1) * 300.0 - 0.5 for i in range(10)]
eto.pair_media(
trial_table,
stream_rates={"video": 30.0, "pose": 30.0},
output_path=".ethograph/alignment.nwb",
)
Then launch EthoGraph, select session.nc in the Custom set-up card, and
point the media folders at your video and pose directories.
NWB stores trials, media references and features together, so there is no
separate alignment step. For DANDI datasets, select the .nwb URL or
downloaded file in the GUI and click Load. To build one yourself with
pynwb:
from datetime import datetime
import pynwb
from dateutil.tz import tzlocal
from pynwb.behavior import BehavioralTimeSeries
nwbfile = pynwb.NWBFile(
session_description="My experiment",
identifier="session-001",
session_start_time=datetime.now(tzlocal()),
)
nwbfile.add_trial_column(name="stimulus", description="Stimulus type")
for i in range(10):
nwbfile.add_trial(
start_time=i * 300.0,
stop_time=(i + 1) * 300.0 - 0.5,
stimulus="tone_A" if i % 2 else "tone_B",
)
behavior_mod = nwbfile.create_processing_module("behavior", "Behavioral data")
behavior_ts = BehavioralTimeSeries(name="BehavioralTimeSeries")
behavior_ts.create_timeseries(name="speed", data=speed_array, rate=30.0, unit="cm/s")
behavior_mod.add(behavior_ts)
with pynwb.NWBHDF5IO("session.nwb", "w") as io:
io.write(nwbfile)
References#
TrialTree —
from_datasets(),from_continuous(), timing, iterationNWBAlignment— alignment reader APIdiscover_media()— the pairing table from folders or a filename patternpair_media()— writes the alignment file, or adds streams to an existing.nwb