Skip to content

Commit

Permalink
[FIX] Add tol parameter to events_from_annotations (mne-tools#12324)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcmdnk authored Feb 8, 2024
1 parent e05e77c commit 6857f10
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 1 deletion.
1 change: 1 addition & 0 deletions doc/changes/devel/12324.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``tol`` parameter to :meth:`mne.events_from_annotations` so that the user can specify the tolerance to ignore rounding errors of event onsets when using ``chunk_duration`` is not None (default is 1e-8), by `Michiru Kaneda`_
8 changes: 7 additions & 1 deletion mne/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,7 @@ def events_from_annotations(
regexp=r"^(?![Bb][Aa][Dd]|[Ee][Dd][Gg][Ee]).*$",
use_rounding=True,
chunk_duration=None,
tol=1e-8,
verbose=None,
):
"""Get :term:`events` and ``event_id`` from an Annotations object.
Expand Down Expand Up @@ -1572,6 +1573,11 @@ def events_from_annotations(
they fit within the annotation duration spaced according to
``chunk_duration``. As a consequence annotations with duration shorter
than ``chunk_duration`` will not contribute events.
tol : float
The tolerance used to check if a chunk fits within an annotation when
``chunk_duration`` is not ``None``. If the duration from a computed
chunk onset to the end of the annotation is smaller than
``chunk_duration`` minus ``tol``, the onset will be discarded.
%(verbose)s
Returns
Expand Down Expand Up @@ -1617,7 +1623,7 @@ def events_from_annotations(
for annot in annotations[event_sel]:
annot_offset = annot["onset"] + annot["duration"]
_onsets = np.arange(annot["onset"], annot_offset, chunk_duration)
good_events = annot_offset - _onsets >= chunk_duration
good_events = annot_offset - _onsets >= chunk_duration - tol
if good_events.any():
_onsets = _onsets[good_events]
_inds = raw.time_as_index(
Expand Down
43 changes: 43 additions & 0 deletions mne/tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,49 @@ def test_events_from_annot_onset_alingment():
assert raw.first_samp == event_latencies[0, 0]


@pytest.mark.parametrize(
"use_rounding,tol,shape,onsets,descriptions",
[
pytest.param(True, 0, (2, 3), [202, 402], [0, 2], id="rounding-notol"),
pytest.param(True, 1e-8, (3, 3), [202, 302, 402], [0, 1, 2], id="rounding-tol"),
pytest.param(False, 0, (2, 3), [202, 401], [0, 2], id="norounding-notol"),
pytest.param(
False, 1e-8, (3, 3), [202, 302, 401], [0, 1, 2], id="norounding-tol"
),
pytest.param(None, None, (3, 3), [202, 302, 402], [0, 1, 2], id="default"),
],
)
def test_events_from_annot_with_tolerance(
use_rounding, tol, shape, onsets, descriptions
):
"""Test events_from_annotations w/ and w/o tolerance."""
info = create_info(ch_names=1, sfreq=100)
raw = RawArray(data=np.empty((1, 1000)), info=info, first_samp=0)
meas_date = _handle_meas_date(0)
with raw.info._unlock(check_after=True):
raw.info["meas_date"] = meas_date
chunk_duration = 1
annot = Annotations([2.02, 3.02, 4.02], chunk_duration, ["0", "1", "2"], 0)
raw.set_annotations(annot)
event_id = {"0": 0, "1": 1, "2": 2}

if use_rounding is None:
events, _ = events_from_annotations(
raw, event_id=event_id, chunk_duration=chunk_duration
)
else:
events, _ = events_from_annotations(
raw,
event_id=event_id,
chunk_duration=chunk_duration,
use_rounding=use_rounding,
tol=tol,
)
assert events.shape == shape
assert (events[:, 0] == onsets).all()
assert (events[:, 2] == descriptions).all()


def _create_annotation_based_on_descr(
description, annotation_start_sampl=0, duration=0, orig_time=0
):
Expand Down

0 comments on commit 6857f10

Please sign in to comment.