-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ENH, MRG] Allow epoch construction from annotations #12311
Conversation
Yes! Yes! Yes!!! I hate having to convert my BIDS annotations to events all the time. It's just so unnceseccary. +1000 for allowing Epochs creation from Annotations. |
mne/epochs.py
Outdated
if events is None: | ||
events, event_id_tmp = events_from_annotations(raw) | ||
if events.size == 0: | ||
raise RuntimeError("No events found in raw.annotations") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This message is a little bit of a misomer, no?
Also how can I constrain the annotations that will be used to construct epochs?
Or would your idea be to select the desired subset of epochs after creation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you thinking no onsets
found?
I was thinking you could not preload so then it would be no big deal to select after creation but before load_data
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you thinking no
onsets
found?
No, more like, "no annotations found" ;)
I was thinking you could not preload so then it would be no big deal to select after creation but before
load_data
.
Fair enough!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this to allow event_id
to match the description
for constraining after looking through examples in the documentation, I think this is pretty elegant.
Sounds good to me, and if people want to control which annotation is used to create the events/epochs, they can fall back to |
I was thinking about accepting |
Why not add a new |
I forgot that we have to pass Raw in any case. So I suppose it's okay to expect the annotations to be glued to that raw. I wouldn't add a new parameter. |
-1 to add a new parameter and/or to pass Annotations separately from |
I suppose you're working with MEG data though. All of our EEG readers create annotations, not events. And MNE-BIDS creates annotations, not events. It's really events these days that are the odd ones, not annotations. |
-1 on new @alexrockhill can you look to see if any of our existing examples could be improved by this? |
Both, with EEG, we usually have a stim channel which behave similarly to MEGIN's |
I agree with this reasoning. My main hesitation is that annotations have a duration, and IIUC the behavior here will simply ignore the duration of annotations, and construct |
Interesting thinking! Are you saying you'd expect that epochs should be created such that they contain an entire annotation? Most Annotations I encounter in BIDS country :) are actually without duration, but I absolutely see your point! Maybe a warning or info message could make sense? |
That seems reasonable to document and maybe I suppose we could infer a more reasonable |
personally no, I don't expect that, but that's because I know how (I agree that for zero-length annotations the conceptual gap isn't really there.) As for what to do:
|
Ok, I think that is very reasonable to make this into manageable chunks. I will add the |
@hoechenberger 's enthusiasm was very motivating so I went through all the examples that use Going through the examples made me realize we should allow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice! just a few suggestions, all are minor and are trying to improve clarity of docstrings / messages / code.
@larsoner do you want a look?
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in Annotations the duration tells you if it's an instantaneous event (so basically just an event as historically named). Would it make sense to warn if you get epochs from events with duration > 0? just thinking at loud...
@@ -3249,6 +3260,38 @@ def __init__( | |||
# keep track of original sfreq (needed for annotations) | |||
raw_sfreq = raw.info["sfreq"] | |||
|
|||
# get events from annotations if no events given | |||
if events is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put this block in a private function to be clear about what it needs and returns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, the __init__
is already long enough as it is and a separate function would help with that
There's a logger.info in that case. Do you feel strongly that it should be a warning? |
In my data I use both: |
then maybe logger.info is enough. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fairly simple and indeed makes examples cleaner! Just one idea for simplification/unification
@@ -3249,6 +3260,38 @@ def __init__( | |||
# keep track of original sfreq (needed for annotations) | |||
raw_sfreq = raw.info["sfreq"] | |||
|
|||
# get events from annotations if no events given | |||
if events is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, the __init__
is already long enough as it is and a separate function would help with that
mne/epochs.py
Outdated
if isinstance(event_id, (list, tuple, set)): | ||
if set(event_id).issubset(set(event_id_tmp)): | ||
event_id = {my_id: event_id_tmp[my_id] for my_id in event_id} | ||
# remove any non-selected annotations | ||
annotations.delete( | ||
~np.isin(raw.annotations.description, list(event_id)) | ||
) | ||
else: | ||
raise RuntimeError( | ||
f"event_id(s) {set(event_id) - set(event_id_tmp)} " | ||
"not found in annotations" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic here should probably mimic what we do already for missing event IDs, e.g.,. having Epochs(raw, event_id=["auditory", "visual"], on_missing="ignore")
should be okay when there are only "auditory"
events in the annotations. Maybe you can remove some of the logic here to allow the existing event_id+on_missing
checking/logic to take care of some stuff? (And please add a test for this case!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this comment was not addressed yet and was errantly resolved by GitHub because the code was moved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I see there is an _on_missing
in there now, never mind! Maybe we could deduplicate more at some point (?) but seems okay for now
Looks good to merge but no rush over the holidays |
Now there is a fix to suppress a deprecation warning for Circle and an xvfb dependency for Qt testing so probably makes sense to merge this or split those out |
Thanks @alexrockhill ! |
nice! |
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Richard Höchenberger <[email protected]>
With the advent of
annotations
and their widespread use, it seems likeevents
could be abstracted from the user unless they want to handle time stamps directly. Especially withannotations
being assigned directly from BIDS datasets throughmne-bids
it seems to me that the conversion toevents
becomes superfluous to the user. So it seems reasonable to me to use theannotations
by default when constructingepochs
. Thoughts?