Trials#

One dataset per trial#

Build a xarray.Dataset per trial, then combine them with eto.from_datasets():

import numpy as np
import xarray as xr
import ethograph as eto

datasets = []
for trial_id in range(1, 6):
    n_time = 9000                                   # 5 min at 30 fps
    ds = xr.Dataset(
        {"speed": xr.DataArray(
            np.random.randn(n_time),
            dims=["time"],
            coords={"time": np.arange(n_time) / 30.0},
        )},
    )
    ds.attrs["trial"] = trial_id
    ds.attrs["fps"] = 30.0
    datasets.append(ds)

dt = eto.from_datasets(datasets)
dt.save("session.nc")

Extra attributes such as stimulus become per-trial metadata and flow through to label TSV exports.

For pynapple, trials are an IntervalSet saved alongside the features rather than separate objects:

import pynapple as nap

trials = nap.IntervalSet(
    start=[i * 300.0 for i in range(5)],
    end=[(i + 1) * 300.0 - 0.5 for i in range(5)],
)
nap.save_file({"speed": speed, "trials": trials}, "session")

Splitting a continuous recording into trials#

If you have a single session-long xr.Dataset and want to parcelate it into a trial structure, use eto.from_continuous():

import numpy as np
import pandas as pd
import xarray as xr
import ethograph as eto

# A continuous 10-minute recording at 30 fps
n_samples = 18000
time = np.arange(n_samples) / 30.0

ds = xr.Dataset({
    "speed": xr.DataArray(np.random.randn(n_samples), dims=["time"],
                          coords={"time": time}),
})

# Define trial boundaries (seconds)
trials = pd.DataFrame({
    "trial": [1, 2, 3],
    "start_time": [0.0, 120.0, 300.0],
    "stop_time": [100.0, 250.0, 500.0],
})

dt = eto.from_continuous(ds, trials)
dt.save("session.nc")

dt.trial(2)  # returns the 120–250 s slice, time shifted to start at 0

from_continuous slices the dataset on demand and shifts time coordinates to 0 for each trial.

See TrialTree for the full TrialTree API.