NWB alignment#

The NWB alignment file (<project>/.ethograph/alignment.nwb) stores trial timing, media file paths, and stream offsets. It is the single source of truth for “what file corresponds to what trial, and when did it start”.

See Pairing and alignment for the user-facing walkthrough. At runtime the same interface is available via dt.nwb_alignment on a loaded TrialTree.


Pairing media files#

class ethograph.io.pairing.SourceSpec(stream, folder=None, files=(), pattern=None, device=None, software=None)[source]#

One media source of a rig: where its files are and how they map to trials/devices.

ethograph.io.pairing.discover_media(session_dir, sources)[source]#

The pairing table: trial + one {stream}_{device} column per source/device, basenames only.

  • pattern None: files natsorted; row i is trial i+1; device from device or the default.

  • pattern set: every file must match; named groups build trial + device columns.

  • Extensions: only those in ethograph.io.validation VIDEO_/AUDIO_/POSE_EXTENSIONS for the source’s stream.

  • Fail fast: two pattern-less sources with different file counts -> ValueError naming both counts; a pattern that fails to match a file -> ValueError naming the file; an empty folder -> ValueError.

  • A trial present in one source but not another leaves "" in the missing cell (like the current template).

Return type:

DataFrame

ethograph.io.pairing.pair_media(trial_table, stream_rates=None, session_wide=None, output_path=None, media_root=None, pose_fps=None)[source]#

Write .ethograph/alignment.nwb from a pairing table.

Each per-trial {stream}_{device} column becomes a trials-table column plus one ImageSeries whose segments start at the trial starts; session_wide streams each get one ImageSeries with a starting_time. start_time/stop_time are optional — omitted, they are inferred from the media (needs media_root).

If output_path is an existing NWB that already has a trials table (a neuroconv-written source), the streams are added to that file in place through edit_nwb(), never rebuilt: per-trial columns from trial_table are written as new trial columns (row count must equal the file’s trials, else ValueError) and session_wide streams as acquisition ImageSeries. Existing acquisition names are left alone.

Parameters:
  • trial_table (DataFrame) – trial + {stream}_{device} filename columns (as discover_media() returns). start_time/stop_time optional.

  • stream_rates (dict[str, float] | None) – Sampling rate per stream, e.g. {"video": 30.0, "audio": 48000.0}; a {stream}_{device} key ("video_cam-2": 60.0) overrides its stream’s rate.

  • session_wide (dict[str, tuple[str, float, float]] | None) – {"{stream}_{device}": (file, rate_hz, starting_time_s)} for streams that are one file spanning the whole session rather than one file per trial.

  • output_path (str | Path | None) – Where to write (or, for an existing NWB with a trials table, extend) the .nwb file.

  • media_root (str | Path | None) – Folder the filename columns are relative to; only needed when times are inferred.

  • pose_fps (float | None) – Frame rate for probing pose files, when inferring times from a table whose only media columns are pose_*.

Return type:

The in-memory NWBFile.


Creating alignment files#

ethograph.io.nwb_alignment.make_nwb_alignment(nwb_path=None)[source]#

Create a EmpytAlignment from an NWB path, falling back to base EmpytAlignment.

ethograph.io.nwb_alignment.discover_nwb(nc_path)[source]#

Find an NWB session file near a data file.

Search order: 1. <dir>/.ethograph/alignment.nwb 2. Any .nwb file in <dir>/.ethograph/

Return type:

Path | None

ethograph.io.nwb_alignment.sync_acquisition_for_streams(nwbfile, stream_rates)[source]#

Create ImageSeries acquisition items for ALL external media streams.

Reads the trials table to discover {stream}_{device} columns. For each stream+device pair, creates an ImageSeries in nwbfile.acquisition with external_file, starting_frame, and rate (or timestamps if offsets are present).

Parameters:
  • nwbfile (NWBFile) – NWB file with a populated trials table.

  • stream_rates (dict[str, float]) – Mapping of stream name to sampling rate, e.g. {"video": 30.0, "audio": 44100.0, "pose": 30.0}. A {stream}_{device} key ("video_cam-2": 60.0) overrides its stream’s rate.

Return type:

None

ethograph.io.nwb_alignment.edit_nwb(path)[source]#

Reading alignment metadata#

class ethograph.io.nwb_alignment.NWBAlignment(nwb_path)[source]#

Session metadata backed by an NWB file.

All external media (video, audio, pose) are stored as ImageSeries in acquisition with {stream}_{device} naming. Timing comes from rate or timestamps on the ImageSeries.

devices(stream)[source]#

Discover devices from trials table columns AND acquisition items.

Three sources, checked in order:

  1. Trials table columns (video_cam_1 -> device cam_1).

  2. Acquisition ImageSeries following {stream}_{device} naming.

  3. Acquisition ImageSeries whose external_file extensions match known media types (e.g. .mp4 -> video, .wav -> audio).

Return type:

list[str]

electrical_series()[source]#

Discover ElectricalSeries in acquisition. Returns list of {name, path, n_channels, rate}.

Return type:

list[dict]

get_media(trial, stream, device=None)[source]#

Return the trial table’s media filename for stream/device.

A stored value that is an absolute local path (e.g. baked in by a template authored on a different machine) is reduced to its basename, so callers can always safely join it onto their own video/audio/pose folder instead of resolving to a path that only existed on the authoring machine.

Return type:

str | None

get_stream_rate(stream, device=None)[source]#

Read the sampling rate for a stream from its acquisition ImageSeries.

Return type:

float | None

property has_real_timing: bool#

Whether the trials table has meaningful start/stop times.

Returns False when all trials have identical placeholder timing (e.g. start=0.0, stop=1.0 for every row), which indicates the NWB was generated without real session timing.

resolve_media_path(trial, stream, device=None, fallback_folder=None)[source]#

Resolve the full path for a media file.

  1. Try the ImageSeries external_file path for this trial (if on disk).

  2. Fallback: trial table filename + fallback_folder.

  3. Returns None if unresolvable.

Return type:

str | None

stream_offset_for_trial(trial, stream, device=None)[source]#

Trial-relative time of sample 0 of the file that holds trial.

0.0 for per-trial files, negative for a session-wide file that started before the trial. The file is found by time (the trial’s start falls in its span), never by position – see _file_for_trial.

Return type:

float

class ethograph.io.nwb_alignment.TableAlignment(trials_df)[source]#

Alignment backed by a tabular dataframe with trial timing columns.

Expected columns are trial, start_time, and stop_time. This is used as a fallback when no suitable alignment NWB is available.

class ethograph.io.nwb_alignment.EmpytAlignment[source]#

Base session metadata interface with null-object defaults.

NWBAlignment overrides these with real NWB-backed implementations. When no NWB file is available, the base class is used directly.


NWB import helpers#

ethograph.io.nwb_import.read_trials_table(nwb)[source]#
Return type:

DataFrame

ethograph.io.nwb_import.probe_electrical_series(nwb)[source]#

List all ElectricalSeries in nwb.acquisition.

Return type:

list[dict]

ethograph.io.nwb_import.probe_label_sources(nwb)[source]#

List all potential interval label sources in the NWB file.

Return type:

list[dict]