Skip to content

Make raw-file BIDS event latencies 1-based - #316

Open
arnodelorme wants to merge 1 commit into
developfrom
fix/bids-raw-event-latency
Open

Make raw-file BIDS event latencies 1-based#316
arnodelorme wants to merge 1 commit into
developfrom
fix/bids-raw-event-latency

Conversation

@arnodelorme

Copy link
Copy Markdown
Collaborator

🤖

Problem

EEG event latencies are 1-based (latency 1.0 = first sample, EEGLAB convention). The neo-based event reader in src/eegprep/plugins/EEG_BIDS/raw.py (_read_neo_events, used for .edf/.bdf/.vhdr) computed np.searchsorted(times_sec, all_times) without the +1, so annotations stored in the raw file came out 0-based: an event on the first sample had latency 0.

The events.tsv path in pop_load_frombids already adds 1 (ev_lats = ev_lats + 1). Consequences:

  • Raw-file events were one sample early relative to events.tsv events (and relative to .set files and mne_raw_to_eeg, which are 1-based).
  • bidsevent='merge' compared 0-based raw latencies against 1-based tsv latencies, so its de-duplication missed every match: an event present in both sources was kept twice.
  • bidsevent=False/'append' returned off-by-one raw latencies.

Fix

raw.py: event_latencies = np.searchsorted(times_sec, all_times) + 1 — same rounding policy as the tsv path, now 1-based. One-line change; all consumers in pop_load_frombids (replace, merge, append, False) are unchanged.

Verification

New tests in tests/test_bids_load_frombids_helpers.py write a real EDF+ file with pyedflib (100 Hz, 10 s, 2 channels) into a minimal BIDS tree (dataset_description.json, *_eeg.json, *_channels.tsv, *_events.tsv) with annotations at 0.0 s, 1.0 s, 9.99 s, and an events.tsv containing two of those plus one extra event at 5.0 s.

Latencies from load_raw_eeg_file on that EDF:

before after
raw-file annotations [0, 100, 999] [1, 101, 1000]
bidsevent='merge' event count 6 (A and B duplicated at 0 vs 1) 4 (union: A@1, B@101, D@501, C@1000)
bidsevent='append' 6, raw copies off by one 6, duplicates at identical latencies
bidsevent='replace' 3 (tsv only) 3 (unchanged)
bidsevent=False [0, 100, 999] [1, 101, 1000]

Both new tests fail on origin/develop and pass with the fix.

EEGPREP_SKIP_MATLAB=1 pytest tests/test_import_edf.py tests/test_bids_load_frombids_helpers.py \
  tests/test_bids_gen_derived_fpath.py tests/test_bids_preproc.py tests/test_bids_preproc_old.py \
  tests/test_cli_bids_migrate_commands.py -q
# 18 passed, 2 skipped (MATLAB not available)

EEGPREP_SKIP_MATLAB=1 pytest tests/test_file_menu_pop_functions.py tests/test_sample_data_pop_functions.py \
  tests/test_bids_load_frombids_helpers.py -q
# 78 passed

ruff check / ruff format --check on the two changed files: clean

No docs change: docs/source/user_guide/ does not describe raw-file BIDS event latency behaviour.

Related, not changed here

  • raw.py leaves neo annotation durations as returned by neo (seconds for EDF/BDF; 0.5 in the test), whereas the events.tsv path converts to samples (round(max(1, Fs*duration))). Separate inconsistency, left out of this PR.
  • bids_preproc.py (events.tsv export) indexes times[e['latency']] with a 1-based latency, i.e. one sample late; pre-existing for tsv-derived events and unrelated to this change.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped: your organization's credit balance is too low for Code Review to run.

An organization admin can purchase credits or upgrade the plan in Plans & Billing.

Once credits are available, reopen this pull request to trigger a review.

@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown

Claude encountered an error after 0s —— View job


I'll analyze this and get back to you.

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.

1 participant