Removing time segments from raw object without epoching

  • MNE version: 0.24.0
  • operating system: macOS 12.0.1

Dear MNE experts,

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.

Best,
Keyvan

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.

2 Likes

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)
1 Like