Ejemplo n.º 1
0
def make_epochs_from_raw(raw: mne.io.Raw,
                         tmin,
                         tlen,
                         event_ids=None,
                         baseline=None,
                         decim=1,
                         filter_bp=None,
                         drop_bad=False,
                         use_annotations=False,
                         chunk_duration=None):
    sfreq = raw.info['sfreq']
    if filter_bp is not None:
        if isinstance(filter_bp, (list, tuple)) and len(filter_bp) == 2:
            raw.load_data()
            raw.filter(filter_bp[0], filter_bp[1])
        else:
            print('Filter must be provided as a two-element list [low, high]')

    try:
        if use_annotations:
            events = mne.events_from_annotations(
                raw, event_id=event_ids, chunk_duration=chunk_duration)[0]
        else:
            events = mne.find_events(raw)
    except ValueError as e:
        raise DN3ConfigException(*e.args)

    return mne.Epochs(raw,
                      events,
                      tmin=tmin,
                      tmax=tmin + tlen - 1 / sfreq,
                      preload=True,
                      decim=decim,
                      baseline=baseline,
                      reject_by_annotation=drop_bad)
Ejemplo n.º 2
0
def spectral_features(raw: mne.io.Raw,
                      max_freq: int = 100,
                      n_fft: int = 48) -> np.array:
    """Compute flattened spectral features given a cropped raw recording.

    Data is low-pass filtered to max_freq (default 100Hz) and downsampled to 2*max_freq before features are
    computed. 

    Computes a n_fft (default 48) point FFT. Takes the log to compute the final features which
    are then concatenated into a one dimensional feature vector.

    Returns a N x 24 np.array where N is the number of channels"""

    raw.filter(None,
               max_freq,
               h_trans_bandwidth=0.5,
               filter_length='10s',
               phase='zero-double',
               fir_design='firwin2')
    raw.resample(2 * max_freq, npad="auto")

    psds, _freqs = mne.time_frequency.psd_welch(raw, fmin=1, n_fft=n_fft)

    #return np.ravel(np.log(psds))
    return np.log(psds)
Ejemplo n.º 3
0
def _get_target_and_crop(raw: mne.io.Raw):
    target_char = parse.search('#Tgt{}_', raw.annotations[0]['description'])[0]

    # Find the first speller flash (it isn't consistently at the second or nth index for that matter)
    start_off = 0
    while len(raw.annotations[start_off]['description']) > 6 and start_off < len(raw.annotations):
        start_off += 1
    assert start_off < len(raw.annotations) - 1
    start_t = raw.annotations[start_off]['onset']
    end_t = start_t + TOTAL_RUN_TIME_S
    # Operates in-place
    raw.crop(start_t, end_t, include_tmax=False)
    return target_char
Ejemplo n.º 4
0
def remove_correlated_eog(raw: mne.io.Raw, eog_chs, picks=None):
    # ICA not stable with drifting signals
    tqdm.tqdm.write(
        "Removing ICA components correlated to channels {}".format(eog_chs))
    if 'highpass' not in raw.info.keys() or raw.info['highpass'] < 1.0:
        tqdm.tqdm.write("High pass prefilter...")
        raw.filter(1.0, None, n_jobs=4, picks=picks)

    filename = Path(raw.filenames[0])
    ica_filename = filename.parent / (filename.stem + "-ica.fif.gz")
    if ica_filename.exists():
        tqdm.tqdm.write("Found existing ICA at {}".format(str(ica_filename)))
        ica = mne.preprocessing.read_ica(str(ica_filename))
    else:
        # Calculate components
        tqdm.tqdm.write("Fitting ICA...")
        ica = mne.preprocessing.ICA(n_components=len(raw.ch_names) // 2,
                                    method='fastica')
        ica.fit(raw, picks=picks)

        # Preference for components identified across all eog channels
        excludes = list()
        for ch in eog_chs:
            # Try more liberal thresholds if no correlations found
            for thresh in [3.0, 2.5, 2.0]:
                eog_inds, scores = ica.find_bads_eog(raw,
                                                     ch_name=ch,
                                                     threshold=thresh)
                if len(eog_inds) > 0:
                    break
            # No more than 2 components subtracted per EOG channel, take highest 2 scores
            excludes.append(eog_inds[:2])

        common_components = set.intersection(*[set(s) for s in excludes])
        # Fall back to most frequent components if no common components
        if len(common_components) == 0:
            excludes = [
                comp for comp, cnt in Counter(
                    [idx for ex in excludes for idx in ex]).most_common()
            ]
        else:
            excludes = list(common_components)

        # No more than 3 total components
        ica.exclude = excludes[:3]
        ica.save(ica_filename)

    tqdm.tqdm.write("Dropping {} components".format(len(ica.exclude)))
    raw = ica.apply(raw)
    return raw
Ejemplo n.º 5
0
def standard_epochsdataset(raw: mne.io.Raw, tmin=0, tlen=3, trial_normalizer=zscore, baseline=None, annot_resolver=None,
                           euclidean_alignment=True, runs=None, subject_normalizer=None, filter_bp=None, decim=1,
                           pick_eog=False, reject_eog_by_ica=None):
    picks = mne.pick_types(raw.info, stim=False, meg=False, eeg=True, emg=False, fnirs=False, eog=pick_eog)
    sfreq = raw.info['sfreq']
    if subject_normalizer:
        raw.load_data()
        raw = raw.apply_function(subject_normalizer, channel_wise=False)
    if filter_bp is not None:
        if isinstance(filter_bp, (list, tuple)) and len(filter_bp) == 2:
            raw.load_data()
            raw.filter(filter_bp[0], filter_bp[1], picks=picks, n_jobs=4)
        else:
            print('Filter must be provided as a two-element list [low, high]')

    if reject_eog_by_ica is not None:
        raw = remove_correlated_eog(raw, reject_eog_by_ica, picks=picks)

    events = mne.events_from_annotations(raw, event_id=annot_resolver)[0] if annot_resolver is not None \
        else mne.find_events(raw)

    # Map various event codes to a incremental label system
    _, events[:, -1] = np.unique(events[:, -1], return_inverse=True)

    epochs = mne.Epochs(raw, events, tmin=tmin, tmax=tmin + tlen - 1 / sfreq, preload=True, decim=decim,
                        picks=picks, baseline=None, reject_by_annotation=False)
    return EpochsDataset(epochs, preproccesors=EuclideanAlignment if euclidean_alignment else [],
                         normalizer=trial_normalizer, runs=runs)
Ejemplo n.º 6
0
def events_computation(raw: mne.io.Raw,
                       time_points: Union[List[int], range, np.ndarray],
                       ids: List[int],
                       _subject_tree: Optional[SubjectTree] = None,
                       _priority: Optional[int] = None) -> np.ndarray:
    """creates events for given raw_ object, uses :func:`nodestimation.project.read_or_write` decorator

        :param raw: raw_ to filter
        :type raw: |iraw|_
        :param time_points: times for events start
        :type time_points: |ilist|_ *of* |iint|_ *or* |inp.ndarray|_
        :param ids: number for events identification
        :type ids: |ilist|_ *of* |iint|_
        :param _subject_tree: representation of patient`s files structure, default None
        :type _subject_tree: *look for SubjectTree in* :mod:`nodestimation.project.annotations` *, optional*
        :param _priority: if several files are read, which one to choose, if None, read all of them, default None
        :type _priority: int, optional
        :return: array of events
        :rtype: np.ndarray_
    """

    return np.array(
        [[raw.first_samp + raw.time_as_index(time_point)[0], 0, event_id]
         for time_point, event_id in zip(time_points, ids)])
Ejemplo n.º 7
0
def split(raw_merge: mne.io.Raw) -> mne.io.Raw:
    """
    Splits merged Raw data into 2 subjects Raw data.

    Arguments:
        raw_merge: Raw data for the dyad with data from subject 1
          and data from subject 2 (channels name are defined with
          the suffix S1 and S2 respectively).

    Note:
        Subject's Raw data is set to the standard montage 1020
        available in MNE. An average is computed to avoid reference bias
        (see MNE documentation about set_eeg_reference).

    Returns:
        raw_1020_S1, raw_1020_S2: Raw data for each subject separately.
          Raws are MNE objects.
    """
    ch_S1 = []
    ch_S2 = []
    ch = []
    for name in raw_merge.info['ch_names']:
        if name.endswith('S1'):
            ch_S1.append(name)
            ch.append(name.split('_')[0])
        elif name.endswith('S2'):
            ch_S2.append(name)

    # picking individual subject data
    data_S1 = raw_merge.get_data(picks=ch_S1)
    data_S2 = raw_merge.get_data(picks=ch_S2)

    # creating info for raws
    info = mne.create_info(ch,
                           raw_merge.info['sfreq'],
                           ch_types='eeg',
                           montage=None,
                           verbose=None)
    raw_S1 = mne.io.RawArray(data_S1, info)
    raw_S2 = mne.io.RawArray(data_S2, info)

    # setting info about channels and task
    raw_S1.info['bads'] = [
        ch.split('_')[0] for ch in ch_S1 if ch in raw_merge.info['bads']
    ]
    raw_S2.info['bads'] = [
        ch.split('_')[0] for ch in ch_S2 if ch in raw_merge.info['bads']
    ]
    for raws in (raw_S1, raw_S2):
        raws.info['description'] = raw_merge.info['description']
        raws.info['events'] = raw_merge.info['events']

        # setting montage 94 electrodes (ignore somes to correspond to our data)
        for ch in raws.info['chs']:
            if ch['ch_name'].startswith('MOh') or ch['ch_name'].startswith(
                    'MOb'):
                # print('emg')
                ch['kind'] = FIFF.FIFFV_EOG_CH
            else:
                ch['kind'] = FIFF.FIFFV_EEG_CH
    montage = mne.channels.make_standard_montage('standard_1020')
    raw_1020_S1 = raw_S1.copy().set_montage(montage)
    raw_1020_S2 = raw_S2.copy().set_montage(montage)
    # raw_1020_S1.plot_sensors()

    # set reference to electrodes average
    # (instate of initial ref to avoid ref biais)
    # and storing it in raw.info['projs']: applied when Epochs
    raw_1020_S1, _ = mne.set_eeg_reference(raw_1020_S1,
                                           'average',
                                           projection=True)
    raw_1020_S2, _ = mne.set_eeg_reference(raw_1020_S2,
                                           'average',
                                           projection=True)

    # TODO: annotations, subj name, events
    # task description different across subj

    # raw_1020_S1.plot()
    # raw_1020_S1.plot_psd()

    return raw_1020_S1, raw_1020_S2
Ejemplo n.º 8
0
def first_processing(raw: mne.io.Raw,
                     lfreq: int,
                     nfreq: int,
                     hfreq: int,
                     rfreq: Optional[int] = None,
                     crop: Optional[Union[float, List[float],
                                          Tuple[float, float]]] = None,
                     reconstruct: Optional[bool] = False,
                     meg: Optional[bool] = True,
                     eeg: Optional[bool] = True,
                     _subject_tree: Optional[SubjectTree] = None,
                     _priority: Optional[int] = None) -> mne.io.Raw:
    """processes_ given raw_ object, uses :func:`nodestimation.project.read_or_write` decorator

        :param raw: raw_ to filter
        :type raw: |iraw|_
        :param lfreq: frequency for `low-pass filter <https://en.wikipedia.org/wiki/Low-pass_filter>`_, Hz
        :type lfreq: int
        :param nfreq: frequency for `band-stop filter <https://en.wikipedia.org/wiki/Band-stop_filter>`_, Hz
        :type nfreq: int
        :param hfreq: frequency for `high-pass filter <https://en.wikipedia.org/wiki/High-pass_filter>`_, Hz
        :type hfreq: int
        :param crop: end time of the raw data to use (in seconds), if None, does not crop, default None
        :type crop: |ifloat|_ *or* |ilist|_ *of* |ifloat|_ *or* |ituple|_ *of* |ifloat|_ *, optional*
        :param reconstruct: whether make `artifacts <https://www.neuro.mcw.edu/meg/index.php/Artifacts_in_MEG_data>`_ cleaning or not, default False
        :type reconstruct: bool, optional
        :param meg: whether pick meg channel or not, default True
        :type meg: bool, optional
        :param eeg: whether pick meg channel or not, default True
        :type eeg: bool, optional
        :param _subject_tree: representation of patient`s files structure, default None
        :type _subject_tree: *look for SubjectTree in* :mod:`nodestimation.project.annotations` *, optional*
        :param _priority: if several files are read, which one to choose, if None, read all of them, default None
        :type _priority: int, optional
        :return: processed raw_ object
        :rtype: mne.io.raw_

        .. _processes:
        .. note:: Processing for raw_ data means
            `band-pass <https://en.wikipedia.org/wiki/Band-pass_filter>`_ and `band-stop <https://en.wikipedia.org/wiki/Band-stop_filter>`_ filtering,
            `cropping <https://mne.tools/stable/generated/mne.io.Raw.html?highlight=raw%20crop#mne.io.Raw.crop>`_ and `artifacts <https://www.neuro.mcw.edu/meg/index.php/Artifacts_in_MEG_data>`_ cleaning
    """

    out = raw.copy()

    if crop:
        if not isinstance(crop, list) or not isinstance(crop, tuple):
            out.crop(tmax=crop)
        elif isinstance(crop, list) \
                or isinstance(crop, tuple) \
                and len(crop) == 2:
            out.crop(tmin=crop[0], tmax=crop[1])
        else:
            raise ValueError('Crop range is incorrect: {}'.format(crop))

    if rfreq:
        out.resample(rfreq, npad='auto')

    out = notchfir(out, lfreq, nfreq, hfreq)
    if reconstruct:
        out = artifacts_clean(out)

    out.pick_types(meg=meg, eeg=eeg)

    return out