def test_events_to_epochs(events, rel_start, rel_stop): words = events[events.type == 'WORD'] epochs = events_to_epochs(words, rel_start, rel_stop, 1000) assert len(epochs) == 156 for epoch in epochs: assert epoch[1] - epoch[0] == rel_stop - rel_start
def test_events_to_epochs_simple(): offsets = [100, 200] # in samples events = pd.DataFrame({"eegoffset": offsets}) rate = 1000 # in samples / s rel_start = -10 # in ms rel_stop = 10 # in ms epochs = events_to_epochs(events, rel_start, rel_stop, rate) assert epochs[0][0] == 90 assert epochs[0][1] == 110 assert epochs[1][0] == 190 assert epochs[1][1] == 210
def test_split_eeg_reader(self, rhino_root): basename, sample_rate, dtype, filename = self.get_meta( 'R1111M', 'FR1', 0, rhino_root) events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))}) rel_start, rel_stop = 0, 200 epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate) eeg_reader = SplitEEGReader(filename, dtype, epochs, None) ts, contacts = eeg_reader.read() assert ts.shape == (len(epochs), 100, 100) assert len(contacts) == 100
def test_split_eeg_reader_missing_contacts(self, rhino_root): basename, sample_rate, dtype, filename = self.get_meta( 'R1006P', 'FR2', 0, rhino_root) events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))}) rel_start, rel_stop = 0, 200 epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate) eeg_reader = SplitEEGReader(filename, dtype, epochs, None, False) ts, contacts = eeg_reader.read() assert ts.shape == (len(epochs), 123, 102) assert 1 not in contacts assert 98 not in contacts assert 99 not in contacts assert 100 not in contacts assert len(contacts) == 123
def test_ramulator_hdf5_reader_rhino(self, subject, experiment, session, rhino_root): basename, sample_rate, dtype, filename = self.get_meta( subject, experiment, session, rhino_root) events = pd.DataFrame({"eegoffset": list(range(0, 500, 100))}) rel_start, rel_stop = 0, 200 epochs = convert.events_to_epochs(events, rel_start, rel_stop, sample_rate) eeg_reader = RamulatorHDF5Reader(filename, dtype, epochs, None) ts, contacts = eeg_reader.read() num_channels = len( CMLReader(subject, experiment, session, rootdir=rhino_root).load('pairs')) time_steps = 200 assert ts.shape == (len(epochs), num_channels, time_steps)
def as_timeseries(self, events: pd.DataFrame, rel_start: Union[float, int], rel_stop: Union[float, int]) -> EEGContainer: """Read the timeseries. Parameters ---------- events Events to read EEG data from rel_start Relative start times in ms rel_stop Relative stop times in ms Returns ------- A time series with shape (channels, epochs, time). By default, this returns data as it was physically recorded (e.g., if recorded with a common reference, each channel will be a contact's reading referenced to the common reference, a.k.a. "monopolar channels"). Raises ------ RereferencingNotPossibleError When rereferencing is not possible. """ eegs = [] # sanity check on the offsets if rel_start != 0 and rel_stop != -1 and rel_start > rel_stop: raise ValueError('rel_start must precede rel_stop') for filename in events["eegfile"].unique(): # select subset of events for this basename ev = events[events["eegfile"] == filename] # determine experiment, session, dtype, and sample rate experiment = ev["experiment"].unique()[0] session = ev["session"].unique()[0] basename = os.path.basename(filename) finder = PathFinder(subject=self.subject, experiment=experiment, session=session, eeg_basename=basename, rootdir=self.rootdir) sources = EEGMetaReader.fromfile(finder.find("sources"), subject=self.subject) sample_rate = sources["sample_rate"] dtype = sources["data_format"] is_scalp = dtype in (".bdf", ".raw", ".mff") # Convert events to epochs (onset & offset times) if rel_start == 0 and rel_stop == -1 and len(events) == 1: epochs = [(0, None)] else: epochs = convert.events_to_epochs(ev, rel_start, rel_stop, sample_rate) # Scalp EEG reader requires onsets, rel_start (in sec), and rel_ # stop (in sec) to cut data into epochs if is_scalp: on_off_epochs = epochs # The onset & offset times will still # be passed to the EEGContainer later if rel_start == 0 and rel_stop == -1 and len(events) == 1: epochs = None else: epochs = np.zeros((len(ev), 3), dtype=int) epochs[:, 0] = ev["eegoffset"] epochs = dict(epochs=epochs, tmin=rel_start / 1000., tmax=rel_stop / 1000.) root = get_root_dir(self.rootdir) eeg_filename = os.path.join(root, filename.lstrip("/")) reader_class = self._get_reader_class(filename) reader = reader_class(filename=eeg_filename, dtype=dtype, epochs=epochs, scheme=self.scheme, clean=self.clean) # if scalp EEG, info is an MNE Info object; if iEEG, info is a list # of contacts data, info = reader.read() attrs = {} if is_scalp: # Pass MNE info and events as extra attributes, to be able to # fully reconstruct MNE Raw/Epochs objects attrs["mne_info"] = info channels = info["ch_names"] if epochs is not None: # Crop out any events/epoch times that ran beyond the # bounds of the EEG recording te_pre = info["truncated_events_pre"] \ if info["truncated_events_pre"] > 0 else None te_post = -info["truncated_events_post"] \ if info["truncated_events_post"] > 0 else None on_off_epochs = on_off_epochs[te_pre:te_post] ev = ev[te_pre:te_post] # Pass the onset & offset time epoch list to EEGContainer, NOT # the MNE-formatted epoch list epochs = on_off_epochs elif self.scheme is not None: data, channels = reader.rereference(data, info) else: channels = ["CH{}".format(n + 1) for n in range(data.shape[1])] eegs.append( EEGContainer( data, sample_rate, epochs=epochs, events=ev, channels=channels, tstart=rel_start, attrs=attrs ) ) eegs = EEGContainer.concatenate(eegs) eegs.attrs["rereferencing_possible"] = reader.rereferencing_possible return eegs
def as_timeseries(self, events: pd.DataFrame, rel_start: Union[float, int], rel_stop: Union[float, int]) -> EEGContainer: """Read the timeseries. Parameters ---------- events Events to read EEG data from rel_start Relative start times in ms rel_stop Relative stop times in ms Returns ------- A time series with shape (channels, epochs, time). By default, this returns data as it was physically recorded (e.g., if recorded with a common reference, each channel will be a contact's reading referenced to the common reference, a.k.a. "monopolar channels"). Raises ------ RereferencingNotPossibleError When rereferencing is not possible. """ eegs = [] for filename in events["eegfile"].unique(): # select subset of events for this basename ev = events[events["eegfile"] == filename] # determine experiment, session, dtype, and sample rate experiment = ev["experiment"].unique()[0] session = ev["session"].unique()[0] finder = PathFinder(subject=self.subject, experiment=experiment, session=session, rootdir=self.rootdir) sources = EEGMetaReader.fromfile(finder.find("sources"), subject=self.subject) sample_rate = sources["sample_rate"] dtype = sources["data_format"] # convert events to epochs if rel_stop < 0: # We're trying to load to the end of the session. Only allow # this in cases where we're trying to load a whole session. if len(events) > 1: raise ValueError("rel_stop must not be negative") epochs = [(0, None)] else: epochs = convert.events_to_epochs(ev, rel_start, rel_stop, sample_rate) root = get_root_dir(self.rootdir) eeg_filename = os.path.join(root, filename.lstrip("/")) reader_class = self._get_reader_class(filename) reader = reader_class(filename=eeg_filename, dtype=dtype, epochs=epochs, scheme=self.scheme) data, contacts = reader.read() if self.scheme is not None: data, labels = reader.rereference(data, contacts) channels = labels else: channels = ["CH{}".format(n + 1) for n in range(data.shape[1])] eegs.append( EEGContainer( data, sample_rate, epochs=epochs, events=ev, channels=channels, tstart=rel_start, )) eegs = EEGContainer.concatenate(eegs) eegs.attrs["rereferencing_possible"] = reader.rereferencing_possible return eegs