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)
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)
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
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
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)
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)])
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
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