Due to specific design of my MEG experiment, I would need to separate different experimental conditions and reject some time segments (e.g. inter-trial intervals) from RAW object (without epoching), and obtain updated event arrays or annotations.
I tried to add annotations to the RAW object according to the following tutorial: https://mne.tools/stable/auto_tutorials/preprocessing/20_rejecting_bad_data.html
As far as I understood, the “Bad Breaks” can be rejected from epoched data but not from a raw object.
of course, I could use raw.get_data() and do all these steps using Numpy. Thought maybe there is an MNE function for this purpose which takes care of events’/annotations’ timing, automatically.
There currently isn’t a simple way to cut a span out of the middle of a Raw object. It is possible to do it by repeatedly copying and cropping the raw, and then concatenating the resulting shorter raw spans. Some pseudocode:
make an empty container list
for index in range(len(raw.annotations)):
copy the raw
get annotation onset and duration
if it’s the first annotation, crop the copy from beginning of raw to onset of raw.annotations[index]; otherwise, crop from raw.annotations[index].onset + raw.annotations[index].duration to raw.annotations[index + 1].onset
append the cropped raw to a container list
concatenate the raws in the container list
the resulting concatenated raw will have new edge annotations at every concatenation point, so that MNE-Python’s filtering operations will know not to treat that part of the data as continuous.
Thanks for sharing your points. I ended up with the following function!
def reject_bad_segs(raw):
""" This function rejects all time spans annotated as 'bad_ITI' and concatenates the rest"""
raw_segs = []
for jsegment in range(len(raw.annotations)):
if raw.annotations.description[jsegment] != 'bad_ITI': # Append all other than 'bad_ITI'
raw_segs.append(raw.copy().crop(tmin=raw.annotations.onset[jsegment], tmax=raw.annotations.onset[jsegment+1],
include_tmax=False))
return mne.concatenate_raws(raw_segs)
The function prototype above seemed a little confusing. I though the goal was to crop out bad_LTI segments and retain everything else. But this implementation is looking for non ‘bad_LTI’ annotations for where to crop.
Instead I think something to consider would be:
def reject_bad_segs(raw, annot_to_reject = ''):
""" This function rejects all time spans annotated as annot_to_reject and concatenates the rest"""
# this implementation seemed buggy, modified it here
raw_segs = []
for jsegment in range(1, len(raw.annotations)):
#print(raw.annotations.description[jsegment], annot_to_reject)
if raw.annotations.description[jsegment] == annot_to_reject: # Append all other than 'bad_ITI'
tmin = raw.annotations.onset[jsegment-1] + raw.annotations.duration[jsegment-1] # start at ending of last bad annot
tmax = raw.annotations.onset[jsegment] # end at onset of current bad annot
raw_segs.append(
raw.copy().crop( # this retains raw between tmin and tmax
tmin=tmin,
tmax=tmax,
include_tmax=False, # this is onset of bad annot
)
)
#print(tmin, tmax, len(raw_segs))
print(len(raw_segs))
return mne.concatenate_raws(raw_segs)