[FIX] Add tol parameter to events_from_annotations#12324
[FIX] Add tol parameter to events_from_annotations#12324drammock merged 10 commits intomne-tools:mainfrom
Conversation
6a3e0e5 to
5059033
Compare
|
the issue you report in #12321 suggests it's a silent bug that can happen quiet easily. I fear that with just the docstring you have here, users will unlikely do the right thing. Do you see a way to have a better default? Do you see a case where using tol=1e-15 can lead to some unexpected behavior? do you think there is bug in this tutorial https://mne.tools/stable/auto_tutorials/clinical/60_sleep.html ? |
|
@agramfort The example case needs at least tol of 1e-11: I think something like 1e-8 is enough large for the tolerance I updated the default value of For the tutorial, Both |
3f9a260 to
1278319
Compare
|
Ok I fully get the issue now. Note that this reminds me of: basically when you have a float step size you have rounding errors. Here it's created cause there is a loss of accuracy with the meas_date but it feels it can happen also without. what makes me less supportive of this change is that if you use the public API ie |
mne/tests/test_annotations.py
Outdated
| with raw.info._unlock(check_after=True): | ||
| raw.info["meas_date"] = meas_date | ||
| annot = Annotations([32730.12, 32760.12, 32790.12], 30.0, ["0", "1", "2"], 0) | ||
| raw._annotations = annot |
There was a problem hiding this comment.
raw.set_annotations(annot)
is the recommended way. If you do this then the issue does not occur on my end.
|
@rcmdnk sorry my last comment about using raw.set_annotations had not been sent. |
b9cd0c1 to
1780981
Compare
|
Apologies for the delayed response. To clarify, the previous test would not have encountered any loss if set_annotations were used, because the orig_time of the annotation is modified within this function. The reason I opted not to use set_annotations is due to my choice of 32730.12 as the onset value. Starting from 0 with this onset value would require a significantly large dataset (on the order of 10,000,000). However, if the meas_date of the raw data and the orig_time of the annotations are identical, using set_annotations won't alter the onsets, and the original issue persists. Furthermore, if the duration includes a decimal value, the same problem can arise. I've simplified the test to demonstrate that using set_annotations with a tol value can effectively address the issue as anticipated. |
agramfort
left a comment
There was a problem hiding this comment.
ok it seems legit !
a couple of remaining nitpicks.
sorry for the slow reaction time ...
doc/changes/devel/12324.bugfix.rst
Outdated
| @@ -0,0 +1 @@ | |||
| Add ``tol`` parameter to :meth:`mne.events_from_annotations`, by `Michiru Kaneda`_ | |||
There was a problem hiding this comment.
can you be more explicit for this sentence? it's written as an enhancement and not really a bug fix. Something like
Add tol parameter to :meth:mne.events_from_annotations so that ... when using chunk_duration != None, by Michiru Kaneda_
mne/annotations.py
Outdated
| The tolerance which is used to check if the calculated onsets have the | ||
| enough duration to the end of the annotation. If the duration from the | ||
| onset to the end of the annotation is smaller than ``chunk_duration`` | ||
| minus ``tol``, the onset will be discarded. |
There was a problem hiding this comment.
can you clarify that this tol parameter is only useful when chunk_duration != None?
|
@rcmdnk sorry for the hassle but it looks like there is a conflict, could you |
Adjust tolerance parameter in events_from_annotations to 1e-8 Update annotation tests to reflect new tolerance parameter of 1e-8
…ons` to include usage details
…from_annotations` docstring
8b146a2 to
727fe92
Compare
|
@larsoner |
drammock
left a comment
There was a problem hiding this comment.
LGTM, just 2 suggestions. +1 for merge after those are addressed
mne/annotations.py
Outdated
| The tolerance which is used to check if the calculated onsets have the | ||
| enough duration to the end of the annotation when``chunk_duration`` is | ||
| not ``None``. If the duration from the onset to the end of the | ||
| annotation is smaller than ``chunk_duration`` minus ``tol``, the onset | ||
| will be discarded. |
There was a problem hiding this comment.
minor wording change; I think this makes the purpose clearer (but please confirm that I've understood the purpose correctly!)
| The tolerance which is used to check if the calculated onsets have the | |
| enough duration to the end of the annotation when``chunk_duration`` is | |
| not ``None``. If the duration from the onset to the end of the | |
| annotation is smaller than ``chunk_duration`` minus ``tol``, the onset | |
| will be discarded. | |
| 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. |
There was a problem hiding this comment.
Thanks, this change is fine with me.
mne/tests/test_annotations.py
Outdated
| assert raw.first_samp == event_latencies[0, 0] | ||
|
|
||
|
|
||
| def test_events_from_annot_with_tolerance(): |
There was a problem hiding this comment.
this test is ripe for a @pytest.mark.parametrize decorator. Do you know how to do that? If not I can push the change... something like
@pytest.mark.parametrize(
"use_rounding,tol,shape,idx",
(
pytest.param(True, 0, (2, 3), [202, 402], id="rounding-notol"),
pytest.param(True, 1e-8, (3, 3), [202, 302, 402], id="rounding-tol"),
pytest.param(False, 0, (3, 3), [202, 302, 402], id="norounding-notol"),
pytest.param(False, 1e-8, (3, 3), [202, 302, 401], id="norounding-tol"),
)
)
def test_events_from_annot_with_tolerance(use_rounding, tol, shape, idx):
"""."""
# ...setup code here
events, _ = events_from_annotations(
raw,
event_id={"0": 0, "1": 1, "2": 2},
chunk_duration=1,
use_rounding=use_rounding,
tol=tol,
)
assert events.shape == shape
assert (events[:, 0] == idx).all()There was a problem hiding this comment.
Yes, thank you for the suggestion; it's a smarter approach. The test has been updated to use parametrize.
update tests events_from_annotations with parameterize
|
thanks @rcmdnk! |
Reference issue
Fixes #12321.
What does this implement/fix?
This change adds a tol (tolerance) parameter to annotations.events_from_annotations.
In the function, the following equation is affected by rounding errors:
Even if
annot_offset - _onsets[x]equalschunk_durationthrough manual calculation,annot_offset - _onsets[x]could be slightly smaller thanchunk_duration.To address this issue, the tol parameter is introduced:
Additional information
It may be advisable to use a non-zero default value for tol. In this context, using a parameter fraction like epsilon may be better.
The tol value should be calculated as:
And a default value of epsilon=1e-5 seems appropriate.
However, this default value alters the behavior, so it should be properly communicated to users.