def test_to_ptsa(self, which): data = np.random.random((10, 32, 100)) rate = 1000 if which == "epochs": epochs = [(i, i + 100) for i in range(data.shape[0])] ts = EEGContainer(data, rate, epochs=epochs) else: filename = resource_filename("cmlreaders.test.data", "R1111M_FR1_0_events.json") events = pd.read_json(filename).iloc[:data.shape[0]] ts = EEGContainer(data, rate, events=events) tsx = ts.to_ptsa() assert isinstance(tsx, TimeSeries) assert tsx["samplerate"] == ts.samplerate assert_equal(tsx.data, data) offsets = tsx["event"].data["eegoffset"] if which == "epochs": assert_equal(offsets, ts.start_offsets) else: assert_equal(offsets, events["eegoffset"]) assert_equal(tsx["time"].data, ts.time)
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 test_resample(self): x = np.linspace(0, 4 * np.pi, 400) data = np.sin([x, x]) ts = EEGContainer(data, 100) new_ts = ts.resample(200) assert len(new_ts.time) == 2 * len(ts.time) assert (new_ts.time[1] - new_ts.time[0]) * 2 == \ (ts.time[1] - ts.time[0])
def test_create_channels(self): data = np.random.random((11, 32, 100)) contacts = [i + 1 for i in range(data.shape[1])] ts = EEGContainer(data, 1000, channels=contacts) assert ts.channels == contacts with pytest.raises(ValueError): EEGContainer(data, 1000, channels=[1, 2])
def test_create_epochs(self, data): good_epochs = [(0, 100)] bad_epochs = [(0, 100), (100, 200)] ts = EEGContainer(data, 1000, epochs=good_epochs) assert ts.epochs == good_epochs with pytest.raises(ValueError): EEGContainer(data, 1000, epochs=bad_epochs)
def test_create_defaults(self, data): if len(data.shape) < 2: with pytest.raises(ValueError): EEGContainer(data, 1000) return ts = EEGContainer(data, 1000) assert ts.shape == ts.data.shape for epoch in ts.epochs: assert epoch == (-1, -1) assert_equal(ts.channels, [i + 1 for i in range(ts.data.shape[1])])
def test_to_mne(self): events = int(np.random.uniform(1, 10)) channels = int(np.random.uniform(1, 128)) samples = int(np.random.uniform(10, 100)) data = np.random.random((events, channels, samples)) rate = 1000 ts = EEGContainer(data, rate) ea = ts.to_mne() assert len(ea.times) == samples assert len(ea.events) == events assert len(ea.info['chs']) == channels assert_equal(ea.get_data().shape, data.shape)
def make_eeg(self, events, rel_start, rel_stop): """Fake EEG data for testing without rhino.""" from cmlreaders.eeg_container import EEGContainer channels = ["CH{}".format(n) for n in range(1, 10)] data = np.random.random( (len(events), len(channels), rel_stop - rel_start)) container = EEGContainer(data, 1000, events=events, channels=channels) return container
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 test_make_time_array_samplerate(self, samplerate): data = np.random.random((1, 1, 100)) ts = EEGContainer(data, samplerate=samplerate) assert len(ts.time) == ts.data.shape[-1] assert_equal(ts.time[1] - ts.time[0], 1000. / samplerate)
def test_make_time_array_tstart(self, tstart): data = np.random.random((1, 32, 2)) rate = 1000 ts = EEGContainer(data, rate, tstart=tstart) assert_equal([tstart, tstart + 1], ts.time)
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