def test_concatenate(self, dim): n_channels = 32 n_samples = 100 rate = 1000 data = [ np.random.random((1, n_channels, n_samples)), np.random.random((1, n_channels, n_samples)), ] def get_tstart(i): if dim == "time": return (i * n_samples) / rate * 1000 else: return 0 series = [ EEGContainer(d, rate, tstart=get_tstart(i), attrs={'test': 'me'}) for i, d in enumerate(data) ] ts = EEGContainer.concatenate(series, dim=dim) if dim == "events": assert ts.shape == (2, n_channels, n_samples) assert_equal(ts.data, np.concatenate(data, axis=0)) elif dim == "time": assert ts.shape == (1, n_channels, n_samples * 2) assert_equal(ts.data, np.concatenate(data, axis=2))
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