Intersection between events conditions

Hi all,
I have an issue working with events and I have been reading the tutorials and the forums but can’t find a solution. After loading raw data and reading events, I want to combine events like this:

  • Condition A (500 events)
  • Condition B (200 events)
  • Condition C (700 events, present in both A and B).
  • And a Condition D that combines A and C but not B, so it should also have 500 events.

The problem is that using mne.epochs.combine_event_ids or hierarchical selection kind of adds up all of the events so Condition D ends up having 1200 events (500 A and 700 C). What I want to do is more of an intersection between both, so Condition D includes only events on Condition A that also are present in Condition C.

  • MNE version: 1.2.3
  • Operating system: Windows 11

If more information is needed, please ask (first time reporting this kind of stuff). Thanks!

I don’t quite follow your situation. Specifically I’m not sure what you mean when you say condition C is “present” in both A and B. As far as I can tell: you have an Epochs object, and when you created the epochs object, there were how many different event codes? Just 2 (A and B)? And now you want to… create new Epochs objects for condition C and condition D?

There are 2 things that might help here. One is to consider using epochs metadata so you can have one epochs object and easily select different subsets of epochs based on metadata columns. The other thing that would help is if you showed us the code you’re using to create the epochs (including how you defined your event_id dictionary) and what you’re trying to do after that, with comments on specific lines of the code about what you’re trying to do and what isn’t working.

Hi Dan,

Thanks for your comment and sorry for the confusion. I’m reading my post again and I can see how difficult it is to follow the issue so I’ll try to be clearer here. Here’s the code I’m using to create the epochs:

DATA_ROOT = '../data'
RAW_DATA = '../raw_data'

raw ="{}/S{}/{}{:06d}.vhdr".format(RAW_DATA, SUBJECT, FILE_PREFIX, SUBJECT), preload=True, eog={'EOG1', 'EOG2'})
events = mne.events_from_annotations(raw, event_id='auto', regexp='Stimulus/') # 'Stimulus/' is the prefix the recording software uses for events
# I have stimuli that can be either present or absent, participants can either see them or not, and can also be localized at the left or right
event_dict = {'Present/Seen': 55,
              'Absent/Unseen': 56,
              'Absent/Seen': 57,
              'Present/Unseen': 59,
              'Right': 75,
              'Left': 74}

epochs = mne.Epochs(raw, events=events[0], event_id=event_dict, tmin=-2.0, tmax=2.0, baseline=None, preload=True, on_missing='warn')

When I check my epochs, I have the following events:

<Epochs |  1495 events (all good), -2 - 2 sec, baseline off, ~2.81 GB, data loaded,
 'Present/Seen': 445
 'Absent/Unseen': 293
 'Absent/Seen': 7
 'Present/Unseen': 152
 'Right': 299
 'Left': 299>

What I need is, for example, to select the events that correspond to all of the ‘Present’ stimuli, easily done with:

In [8]: epochs['Present']
Out [8]: 
<Epochs |  597 events (all good), -2 - 2 sec, baseline off, ~1.12 GB, data loaded,
 'Present/Seen': 445
 'Present/Unseen': 152>

But also those that are either ‘Right’ or ‘Left’. If I try epochs['Present', 'Right'] I get:

In [9]: epochs['Present', 'Right']
Out [9]: 
<Epochs |  896 events (all good), -2 - 2 sec, baseline off, ~1.68 GB, data loaded,
 'Present/Seen': 445
 'Present/Unseen': 152
 'Right': 299>

My problem here is that I believe I should have a maximum of 597 ‘Right’ stimuli, which would correspond to those also belonging to ‘Present’. But what is going on (or at least this is what I understand) is that I get all of the ‘Present’ stimuli but also all of the ‘Right’ ones, including those that are ‘Absent’. Is there a way to select only those ‘Right’ stimuli that are also ‘Present’?

Thanks again!

aahhh, ok. Much clearer now. I think the problem is that you have an events array that specifies left/right separately from the other conditions. Ideally the sample number of each Right or Left event has an identical, matching present/absent/seen/unseen event. In other words, what we hope for is something like this:

1234, 0, 55  # present/seen
1234, 0, 74  # left
5678, 0, 57  # absent/unseen
5678, 0, 75  # right
...          # etc

A quick way to check would be

assert (np.unique(events[:, 0], return_counts=True)[1] == 2).all()

which checks that every sample number in your events array occurs exactly twice. If there are other events in there besides the listed ones of interest, remove them first and then check. Assuming that check succeeds, you now need to re-code your events to combine the encoding of left/right with present/seen/etc. One way to do that is with Pandas:

import pandas as pd
event_df = pd.DataFrame(events, columns=['sample', 'prev', 'event'])
new_df = event_df.groupby('sample').apply(lambda group: int(''.join(map(str, group['event']))))
new_df = new_df.reset_index()
new_df.insert(1, 'prev', 0)
new_events = new_df.to_numpy()

That should get you a new events array with only one event per sample, and the event codes will be the concatenation of the digits of the existing event codes. So your new event_dict will be something like:

Present/Seen/Left: 5574
Present/Seen/Right: 5575
Absent/Unseen/Left: 5674
Absent/Unseen/Right: 5675

At that point you should be able to create epochs and then index like epochs['Present/Right'] and get present/seen/right and present/unseen/right, etc.

1 Like