Skip to content

Add lever push durations and fix RT IQR error bars - #32

Merged
celefthe merged 13 commits into
mainfrom
develop
Oct 2, 2026
Merged

celefthe merged 13 commits into
mainfrom
develop

Conversation

@celefthe

@celefthe celefthe commented Oct 2, 2026

Copy link
Copy Markdown
Member

Closes #30 and #31. visiomode-analysis session now picks up the per-session _lever-durations.csv files that sit next to lever-push session JSONs, adds each push's duration (ms) to the trials CSV, and reports lever duration stats in session and subject summaries. This PR also fixes the reaction time plots, which drew their IQR error bars in the wrong place.

Changes

  • get_trials() finds sub-<id>_exp-<exp>_ses-<date>_lever-durations.csv next to the JSON (find_lever_durations()), or uses the file given with the new session --lever-durations PATH option. It adds a lever_duration column, matching the Nth push (by push_id) to the Nth precued, hit or false-alarm trial; other trials are NaN. Sessions without a durations file get no column.
  • A file that can't be matched (empty or unparseable, missing push_id/duration columns, non-numeric durations, push_id not running 0..n-1, or a push count that differs from the number of lever-push trials) produces a warning and leaves the column empty. _read_lever_durations() raises a ValueError for each case and _add_lever_durations() turns it into the warning, so a bad file never breaks commands that don't need durations, such as regressors. Checked against the Rett dataset: all 872 sessions match exactly, with no warnings.
  • summary() adds lever_duration_<mean|median|sd|iqr>[_<subset>][_wc] (SD uses ddof=1) for all pushes and the cued, hits, false_alarms and precued subsets, with and without correction trials (40 fields). These are only emitted when the trials have a lever_duration column. subject picks them up automatically: sessions without durations show NaN, and the columns are left out entirely if no session has durations.
  • The session report gains a "Median lever push duration" plot (median ± IQR per subset; false alarms are left out for target-only sessions), shown only when at least one duration was matched.
  • The reaction time median plots now pass q75 - median / median - q25 to Plotly as error-bar offsets, so the bars span the IQR. Previously they ran from median - q25 to median + q75. The RT and lever duration plots now share _plot_medians_from_dict(), and _rt_stats is renamed to _median_iqr.
  • summary() also accepts a trials DataFrame, and generate_report() takes an optional trials DataFrame (also used by preprocess_session()). A session is now parsed once instead of about nine times per report, and the --lever-durations override applies to every step. The RT plots use a new _select_rts(trials, ...); get_rts(path) is kept as a wrapper.
  • Tests cover:
    • matching, auto-detection and the explicit override (including a stale neighbouring file);
    • each bad-file case;
    • per-subset and _wc stats, empty subsets, and fields being omitted when there are no durations;
    • subject collation with and without durations;
    • the report plot being shown or left out;
    • summary() on full and filtered DataFrames;
    • regressors with a malformed durations file;
    • the corrected RT error bars.
  • write_trials_csv gains a rows= argument. README and CHANGELOG ([Unreleased]) are updated, and the README Python API example now calls generate_report() with its actual arguments.

@celefthe
celefthe merged commit 10ea193 into main Oct 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add lever push durations to trials and session/subject summaries

1 participant