[ENH, MRG] Allow epoch construction from annotations#12311
[ENH, MRG] Allow epoch construction from annotations#12311larsoner merged 34 commits intomne-tools:mainfrom
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.
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.
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.
Are you thinking no
onsetsfound?
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.
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 |
for more information, see https://pre-commit.ci
agramfort
left a comment
There was a problem hiding this comment.
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...
| raw_sfreq = raw.info["sfreq"] | ||
|
|
||
| # get events from annotations if no events given | ||
| if events is None: |
There was a problem hiding this comment.
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.
+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. |
larsoner
left a comment
There was a problem hiding this comment.
Looks fairly simple and indeed makes examples cleaner! Just one idea for simplification/unification
| raw_sfreq = raw.info["sfreq"] | ||
|
|
||
| # get events from annotations if no events given | ||
| if events is None: |
There was a problem hiding this comment.
+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.
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.
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.
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 <richard.hoechenberger@gmail.com>

With the advent of
annotationsand their widespread use, it seems likeeventscould be abstracted from the user unless they want to handle time stamps directly. Especially withannotationsbeing assigned directly from BIDS datasets throughmne-bidsit seems to me that the conversion toeventsbecomes superfluous to the user. So it seems reasonable to me to use theannotationsby default when constructingepochs. Thoughts?