def load_eeg_from_times(df, channel_file, rel_start_ms, rel_stop_ms, buf_ms=0, noise_freq=[58., 62.], downsample_freq=1000, resample_freq=None, pass_band=None): """ Parameters ---------- df: pandas.DataFrame An dataframe with a stTime column channel_file: str Path to Ncs file from which to load eeg. rel_start_ms: int Initial time (in ms), relative to the onset of each spike rel_stop_ms: int End time (in ms), relative to the onset of each spike buf_ms: int Amount of time (in ms) of buffer to add to both the beginning and end of the time interval noise_freq: list Stop filter will be applied to the given range. Default=[58. 62] downsample_freq resample_freq pass_band Returns ------- """ # # make a df with 'stTime' column to pass to _load_eeg_timeseries # events = pd.DataFrame(data=np.stack([s_times, clust_nums], -1), columns=['stTime', 'cluster_num']) # load eeg for this channel eeg = _load_eeg_timeseries(df, rel_start_ms, rel_stop_ms, [channel_file], buf_ms, downsample_freq, resample_freq) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # mean center the data eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) return eeg
def test_ButterworthFilter(self): bfilter = ButterworthFilter(time_series=self.time_series, freq_range=[10., 20.], filt_type='stop', order=2) bfilter.filter() return True
def test_butterworth(self): filtered0 = ButterworthFilter(self.timeseries, self.freqs).filter() filtered1 = ButterworthFilter(self.timeseries.transpose(), self.freqs).filter() xr.testing.assert_allclose(filtered0, filtered1.transpose(*filtered0.dims))
def test_ButterwothFilter(self): from xarray.testing import assert_equal b_filter = ButterworthFilter(time_series=self.base_eegs, freq_range=[58., 62.], filt_type='stop', order=4) base_eegs_filtered_1 = b_filter.filter() base_eegs_filtered_2 = self.base_eegs.filtered(freq_range=[58., 62.], filt_type='stop', order=4) assert_equal(base_eegs_filtered_1, base_eegs_filtered_2) with self.assertRaises(AssertionError): assert_equal(base_eegs_filtered_1, self.base_eegs)
def load_eeg_from_times(df, channel_file, rel_start_ms, rel_stop_ms, buf_ms=0, noise_freq=[58., 62.], downsample_freq=1000, resample_freq=None, pass_band=None): """ Parameters ---------- df: pandas.DataFrame An dataframe with a stTime column channel_file: str Path to Ncs file from which to load eeg. rel_start_ms: int Initial time (in ms), relative to the onset of each spike rel_stop_ms: int End time (in ms), relative to the onset of each spike buf_ms: int Amount of time (in ms) of buffer to add to both the beginning and end of the time interval noise_freq: list Stop filter will be applied to the given range. Default=[58. 62] downsample_freq resample_freq pass_band Returns ------- """ # # make a df with 'stTime' column to pass to _load_eeg_timeseries # events = pd.DataFrame(data=np.stack([s_times, clust_nums], -1), columns=['stTime', 'cluster_num']) # load eeg for this channel eeg = _load_eeg_timeseries(df, rel_start_ms, rel_stop_ms, [channel_file], buf_ms, downsample_freq, resample_freq) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # mean center the data eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) return eeg
def test_ButterwothFilter(self): from xarray.testing import assert_equal b_filter = ButterworthFilter(timeseries=self.base_eegs, freq_range=[58., 62.], filt_type='stop', order=4) base_eegs_filtered_1 = b_filter.filter() base_eegs_filtered_2 = self.base_eegs.filtered(freq_range=[58., 62.], filt_type='stop', order=4) assert_equal(base_eegs_filtered_1, base_eegs_filtered_2) with self.assertRaises(AssertionError): assert_equal(base_eegs_filtered_1, self.base_eegs)
def test_resting_state_connectivity(rhino_root): subject = "R1354E" index = get_data_index("r1", rhino_root) sessions = index[(index.subject == subject) & (index.experiment == 'FR1')].session.unique() all_events = [] all_resting = [] data = [] for session in sessions: reader = CMLReader(subject, 'FR1', session, rootdir=rhino_root) events = get_countdown_events(reader) resting = countdown_to_resting(events, reader.load('sources')['sample_rate']) all_events.append(events) all_resting.append(resting) eeg = read_eeg_data(reader, resting, reref=False) data.append(eeg) # Verify that events match Ethan's analysis; his events are ordered in an # odd way, so we have to sort them to make sure they match ethan = np.load(resource_filename("thetamod.test.data", "R1354E_events_ethan.npy")) assert_equal(sorted(ethan["eegoffset"]), sorted(pd.concat(all_resting).eegoffset.values)) eegs = TimeSeries.concatenate(data) eegs.data = ButterworthFilter(time_series=eegs.to_ptsa(), ).filter().values conn = get_resting_state_connectivity(eegs.to_mne(), eegs.samplerate) basename = ('{subject}_baseline3trials_network_theta-alpha.npy' .format(subject=subject)) filename = Path(rhino_root).joinpath('scratch', 'esolo', 'tmi_analysis', subject, basename) data = np.load(filename) np.savez("test_output.npz", eeg=eegs.data, my_conn=conn, ethans_conn=data, events=pd.concat(all_events, ignore_index=True).to_records(), resting=pd.concat(all_resting, ignore_index=True).to_records()) assert_almost_equal(scipy.special.logit(conn), scipy.special.logit(data), 3)
def band_pass_eeg(eeg, freq_range, order=4): """ Runs a butterworth band pass filter on an eeg time seriesX object. Parameters ---------- eeg: timeseries A ptsa.timeseries object freq_range: list List of two floats defining the range to filter in order: int Order of butterworth filter Returns ------- timeseries Filtered EEG object """ return ButterworthFilter(eeg, freq_range, filt_type='pass', order=order).filter()
def power_spectra_from_spike_times(s_times, clust_nums, channel_file, rel_start_ms, rel_stop_ms, freqs, noise_freq=[58., 62.], downsample_freq=250, mean_over_spikes=True): """ Function to compute power relative to spike times. This computes power at given frequencies for the ENTIRE session and then bins it relative to spike times. You WILL run out of memory if you don't let it downsample first. Default downsample is to 250 Hz. Parameters ---------- s_times: np.ndarray Array (or list) of timestamps of when spikes occured. EEG will be loaded relative to these times. clust_nums: s_times: np.ndarray Array (or list) of cluster IDs, same size as s_times channel_file: str Path to Ncs file from which to load eeg. rel_start_ms: int Initial time (in ms), relative to the onset of each spike rel_stop_ms: int End time (in ms), relative to the onset of each spike freqs: np.ndarray array of frequencies at which to compute power noise_freq: list Stop filter will be applied to the given range. Default=[58. 62] downsample_freq: int or float Frequency to downsample the data. Use decimate, so we will likely not reach the exact frequency. mean_over_spikes: bool After computing the spike x frequency array, do we mean over spikes and return only the mean power spectra Returns ------- dict dict of either spike x frequency array of power values or just frequencies, if mean_over_spikes. Keys are cluster numbers """ # make a df with 'stTime' column for epoching events = pd.DataFrame(data=np.stack([s_times, clust_nums], -1), columns=['stTime', 'cluster_num']) # load channel data signals, timestamps, sr = load_ncs(channel_file) # downsample the session if downsample_freq is not None: signals, timestamps, sr = _my_downsample(signals, timestamps, sr, downsample_freq) else: print( 'I HIGHLY recommend you downsample the data before computing power across the whole session...' ) print('You will probably run out of memory.') # make into timeseries eeg = TimeSeries.create(signals, samplerate=sr, dims=['time'], coords={'time': timestamps / 1e6}) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # compute power wave_pow = MorletWaveletFilter(eeg, freqs, output='power', width=5, cpus=12, verbose=False).filter() # log the power data = wave_pow.data wave_pow.data = numexpr.evaluate('log10(data)') # get start and stop relative to the spikes epochs = _compute_epochs(events, rel_start_ms, rel_stop_ms, timestamps, sr) bad_epochs = (np.any(epochs < 0, 1)) | (np.any(epochs > len(signals), 1)) epochs = epochs[~bad_epochs] events = events[~bad_epochs].reset_index(drop=True) # mean over time within epochs spikes_x_freqs = np.stack( [np.mean(wave_pow.data[:, x[0]:x[1]], axis=1) for x in epochs]) # make dict with keys being cluster numbers. Mean over spikes if desired. pow_spect_dict = {} for this_cluster in events.cluster_num.unique(): if mean_over_spikes: pow_spect_dict[this_cluster] = spikes_x_freqs[ events.cluster_num == this_cluster].mean(axis=0) else: pow_spect_dict[this_cluster] = spikes_x_freqs[events.cluster_num == this_cluster] return pow_spect_dict
use_reref_eeg=True, common_root='data').read() words = events[events.type == 'WORD'] # select all available channels eegfile = words[0].eegfile eegfile_reref = str.split(str(eegfile), '/') day = eegfile_reref[-1] eegfile_reref = '/'.join(eegfile_reref[0:-1]) channels = glob.glob(eegfile_reref + '/' + day + "*.[0-9]*") channels = np.array([str.split(x, '.')[-1] for x in channels]) #channels = np.array(['{:03}'.format(x) for x in range(0,132)]) eeg = EEGReader(events=words, channels=channels, start_time=0.3, end_time=1.6).read() b_filter = ButterworthFilter(time_series=eeg, order=4, freq_range=[58, 62]) eeg_filtered = b_filter.filter() eeg_buffered = eeg_filtered.add_mirror_buffer(1.3) eeg_buffered.data = np.ascontiguousarray(eeg_buffered.data) import time pt = time.time() freqs = np.logspace(np.log10(3), np.log10(180), 50) pow_ev, _ = MorletWaveletFilterCpp(time_series=eeg_buffered, freqs=freqs[0:1], output='power', cpus=20, verbose=False).filter() run_time = time.time() - pt
def load_eeg_full_timeseries(task, subject, session, elec_scheme=None, noise_freq=[58., 62.], resample_freq=None, pass_band=None): """ Function for loading continuous EEG data from a full session, not based on event times. Returns a list of timeseries object. task: str The experiment name subject: str The subject number session: int The session number for this subject and task elec_scheme: pandas.DataFrame A dataframe of electrode information, returned by load_elec_info(). If the column 'contact' is in the dataframe, monopolar electrodes will be loads. If the columns 'contact_1' and 'contact_2' are in the df, bipolar will be loaded. You may pass in a subset of rows to only load data for electrodes in those rows. If you do not enter an elec_scheme, all monopolar channels will be loaded (but they will not be labeled with correct channel tags). LOADING ALL ELECTRODES AND FOR AN ENTIRE SESSION AT ONCE IS NOT REALLY RECOMMENDED. noise_freq: list Stop filter will be applied to the given range. Default=(58. 62) resample_freq: float Sampling rate to resample to after loading eeg pass_band: list If given, the eeg will be band pass filtered in the given range Returns ------- list A TimeSeries object. """ # load eeg eeg = CMLReader(subject=subject, experiment=task, session=session).load_eeg(scheme=elec_scheme).to_ptsa() # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # resample if desired. Note: can be a bit slow especially if have a lot of eeg data if resample_freq is not None: r_filter = ResampleFilter(eeg, resample_freq) eeg = r_filter.filter() # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) return eeg
def load_eeg(events, rel_start_ms, rel_stop_ms, buf_ms=0, elec_scheme=None, noise_freq=[58., 62.], resample_freq=None, pass_band=None, use_mirror_buf=False, demean=False, do_average_ref=False): """ Returns an EEG TimeSeries object. Parameters ---------- events: pandas.DataFrame An events dataframe that contains eegoffset and eegfile fields rel_start_ms: int Initial time (in ms), relative to the onset of each event rel_stop_ms: int End time (in ms), relative to the onset of each event buf_ms: Amount of time (in ms) of buffer to add to both the begining and end of the time interval elec_scheme: pandas.DataFrame A dataframe of electrode information, returned by load_elec_info(). If the column 'contact' is in the dataframe, monopolar electrodes will be loads. If the columns 'contact_1' and 'contact_2' are in the df, bipolar will be loaded. You may pass in a subset of rows to only load data for electrodes in those rows. If you do not enter an elec_scheme, all monopolar channels will be loaded (but they will not be labeled with correct channel tags). Entering a scheme is recommended. noise_freq: list Stop filter will be applied to the given range. Default=(58. 62) resample_freq: float Sampling rate to resample to after loading eeg. pass_band: list If given, the eeg will be band pass filtered in the given range. use_mirror_buf: bool If True, the buffer will be data taken from within the rel_start_ms to rel_stop_ms interval, mirrored and prepended and appended to the timeseries. If False, data outside the rel_start_ms and rel_stop_ms interval will be read. demean: bool If True, will subject the mean voltage between rel_start_ms and rel_stop_ms from each channel do_average_ref: bool If True, will compute the average reference based on the mean voltage across channels Returns ------- TimeSeries EEG timeseries object with dimensions channels x events x time (or bipolar_pairs x events x time) NOTE: The EEG data is returned with time buffer included. If you included a buffer and want to remove it, you may use the .remove_buffer() method. EXTRA NOTE: INPUT SECONDS FOR REMOVING BUFFER, NOT MS!! """ # add buffer is using if (buf_ms is not None) and not use_mirror_buf: actual_start = rel_start_ms - buf_ms actual_stop = rel_stop_ms + buf_ms else: actual_start = rel_start_ms actual_stop = rel_stop_ms # load eeg # Should auto convert to PTSA? Any reason not to? eeg = CMLReader(subject=events.iloc[0].subject).load_eeg( events, rel_start=actual_start, rel_stop=actual_stop, scheme=elec_scheme).to_ptsa() # compute average reference by subracting the mean across channels if do_average_ref: eeg = eeg - eeg.mean(dim='channel') # baseline correct subracting the mean within the baseline time range if demean: eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # add mirror buffer if using. PTSA is expecting this to be in seconds. if use_mirror_buf: eeg = eeg.add_mirror_buffer(buf_ms / 1000.) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # resample if desired. Note: can be a bit slow especially if have a lot of eeg data if resample_freq is not None: r_filter = ResampleFilter(eeg, resample_freq) eeg = r_filter.filter() # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) # reorder dims to make events first eeg = make_events_first_dim(eeg) return eeg
def test_ButterworthFilter(self): bfilter = ButterworthFilter(time_series = self.time_series,freq_range = [10.,20.],filt_type='stop',order=2) bfilter.filter() return True
def calc_subj_pep(subj, elecs=None, method='bip', relstart=300, relstop=1301, freq_specs=(2, 120, 30), percentthresh=.95, numcyclesthresh=3, load_eeg=False, save_eeg=False, save_result=False, plot=False, kind='r1', experiment='FR1', eeg_path='~/', result_path='~/'): """ Inputs: subj - subject string elecs - list of electrode pairs (strings) method - bip or avg depending on referencing scheme freq_specs - tuple of (low_freq, high_freq, num_freqs) for background fitting in BOSC. Returns: pep_all - average Pepisode for all words at each frequency pep_rec - average Pepisode for recalled words at each frequency pep_nrec - average Pepisode for non-recalled words at each frequency subj_tscores - t-score at each frequency, comparing rec and nrec across events ** Note that tscore is not itself meaningful because events are not independent. Comparing these tscores across subjects, however, is valid. """ if save_eeg and load_eeg: raise ('Cannot save and load eeg simultaneously.') print('Subject: ', subj) if elecs is None: good_subj = pd.read_pickle( '/home1/jrudoler/Theta_Project/hippo_subject_pairs.csv') elecs = good_subj[good_subj['Subject'] == subj]['hippo_pairs'].iloc[0] subj_pepisode = None subj_recalled = None subj_tscores = None if plot: plt.figure(figsize=(12, 6)) lowfreq, highfreq, numfreqs = freq_specs print(elecs) for pair_str in elecs: chans = pair_str.split('-') data = cml.get_data_index(kind=kind) data = data[data['experiment'] == experiment] sessions = data[data['subject'] == subj]['session'].unique() pepisodes = None # events, freqs recalled = None # events, freqs tscore = None for sess in sessions: try: print('Loading session {} EEG'.format(sess)) reader = cml.CMLReader(subject=subj, experiment=experiment, session=sess) all_events = reader.load('task_events') if not os.path.exists(eeg_path): os.makedirs(eeg_path) if load_eeg: eeg = TimeSeries.from_hdf(eeg_path + 'session_' + str(sess) + '_' + pair_str) bosc = P_episode(all_events, eeg, sr=eeg.samplerate.values, lowfreq=lowfreq, highfreq=highfreq, numfreqs=numfreqs) elif method == 'bip': pairs = reader.load("pairs") # bipolar eeg bip = reader.load_eeg( scheme=pairs[pairs.label == pair_str])\ .to_ptsa().mean(['event', 'channel']) bip = ButterworthFilter(bip, freq_range=[58., 62.], filt_type='stop', order=4).filter() print("Applying BOSC method!") if save_eeg: bip.to_hdf(eeg_path + 'session_' + str(sess) + '_' + pair_str) bosc = P_episode(all_events, bip, sr=bip.samplerate.values, lowfreq=lowfreq, highfreq=highfreq, numfreqs=numfreqs) elif method == 'avg': contacts = reader.load("contacts") # average eeg eeg = reader.load_eeg( scheme=contacts).to_ptsa().mean('event') # all zeros from a broken lead leads to -inf power, # which results in a LinAlg error for log-log fit # TODO: verify this channel exclusion doesn't cause any # problems. Maybe print a message or raise an error? bad_chan_mask = ~np.all(eeg.values == 0, axis=1) contacts = contacts[bad_chan_mask] eeg = eeg[bad_chan_mask, :] avg = (eeg[contacts.label.str.contains(chans[0]) | \ contacts.label.str.contains(chans[1]), :] - eeg.mean('channel') ).mean('channel') avg = ButterworthFilter(avg, freq_range=[58., 62.], filt_type='stop', order=4).filter() if save_eeg: avg.to_hdf(eeg_path + '/session_' + str(sess) + '_' + pair_str) bosc = P_episode(all_events, avg, sr=avg.samplerate.values, lowfreq=lowfreq, highfreq=highfreq, numfreqs=numfreqs) if plot: bosc.background_fit(plot_type='session') if pepisodes is None: pepisodes = bosc.Pepisode # be careful to only use events from lists that have eeg data. # [np.isin(bosc.interest_events.list, self.lists)] recalled = bosc.interest_events.recalled.values tscore, _ = scp.ttest_ind(pepisodes[recalled], pepisodes[~recalled], axis=0) elif np.isnan(tscore).all(): tscore, _ = scp.ttest_ind(pepisodes[recalled], pepisodes[~recalled], axis=0) else: pepisodes = np.vstack([pepisodes, bosc.Pepisode]) recalled = np.hstack( [recalled, bosc.interest_events.recalled.values]) t, _ = scp.ttest_ind(pepisodes[recalled], pepisodes[~recalled], axis=0) tscore = np.vstack([tscore, t]) print("Proportion recalled:", recalled.mean()) except IndexError: print('IndexError for subject {} session {}'.format( subj, sess)) except FileNotFoundError: print('FileNotFoundError for {} session {}'.format(subj, sess)) continue if pepisodes is None: raise Exception('No working sessions') subj_pepisode = pepisodes if subj_pepisode is None else np.dstack( [subj_pepisode, pepisodes]) subj_recalled = recalled if subj_recalled is None else np.vstack( [subj_recalled, recalled]) subj_tscores = tscore if subj_tscores is None else np.vstack( [subj_tscores, tscore]) if np.isnan(subj_tscores).all(): raise Exception('Too many nan in T-scores. This problem can arise \ when there are no recalled events.') if subj_pepisode.ndim > 2: # if multiple electrode pairs, average over pairs print("Averaging over {} electrodes for subject {}".format( subj_pepisode.shape[2], subj)) subj_pepisode = subj_pepisode.mean(2) subj_recalled = subj_recalled.mean(0) subj_recalled = subj_recalled.astype(bool) if subj_tscores.ndim > 1: print(len(sessions), 'sessions') subj_tscores = np.nanmean(subj_tscores, axis=0) print('{} total events: {} recalled \ and {} non-recalled'.format(len(subj_recalled), sum(subj_recalled), sum(~subj_recalled))) pep_rec = subj_pepisode[subj_recalled, :].mean(0) pep_nrec = subj_pepisode[~subj_recalled, :].mean(0) pep_all = subj_pepisode.mean(0) if save_result: if not os.path.exists(result_path): os.makedirs(result_path) np.save(result_path + '{}_all_{}'.format(subj, method), pep_all) np.save(result_path + '{}_rec_{}'.format(subj, method), pep_rec) np.save(result_path + '{}_nrec_{}'.format(subj, method), pep_nrec) np.save(result_path + '{}_tscore_{}'.format(subj, method), subj_tscores) return pep_all, pep_rec, pep_nrec, subj_tscores
def test_butterworth(self, time_series): bfilter = ButterworthFilter(timeseries=time_series, freq_range=[10., 20.], filt_type='stop', order=2) bfilter.filter()
def power_spectra_from_spike_times(s_times, clust_nums, channel_file, rel_start_ms, rel_stop_ms, freqs, noise_freq=[58., 62.], downsample_freq=250, mean_over_spikes=True): """ Function to compute power relative to spike times. This computes power at given frequencies for the ENTIRE session and then bins it relative to spike times. You WILL run out of memory if you don't let it downsample first. Default downsample is to 250 Hz. Parameters ---------- s_times: np.ndarray Array (or list) of timestamps of when spikes occured. EEG will be loaded relative to these times. clust_nums: s_times: np.ndarray Array (or list) of cluster IDs, same size as s_times channel_file: str Path to Ncs file from which to load eeg. rel_start_ms: int Initial time (in ms), relative to the onset of each spike rel_stop_ms: int End time (in ms), relative to the onset of each spike freqs: np.ndarray array of frequencies at which to compute power noise_freq: list Stop filter will be applied to the given range. Default=[58. 62] downsample_freq: int or float Frequency to downsample the data. Use decimate, so we will likely not reach the exact frequency. mean_over_spikes: bool After computing the spike x frequency array, do we mean over spikes and return only the mean power spectra Returns ------- dict dict of either spike x frequency array of power values or just frequencies, if mean_over_spikes. Keys are cluster numbers """ # make a df with 'stTime' column for epoching events = pd.DataFrame(data=np.stack([s_times, clust_nums], -1), columns=['stTime', 'cluster_num']) # load channel data signals, timestamps, sr = load_ncs(channel_file) # downsample the session if downsample_freq is not None: signals, timestamps, sr = _my_downsample(signals, timestamps, sr, downsample_freq) else: print('I HIGHLY recommend you downsample the data before computing power across the whole session...') print('You will probably run out of memory.') # make into timeseries eeg = TimeSeries.create(signals, samplerate=sr, dims=['time'], coords={'time': timestamps / 1e6}) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # compute power wave_pow = MorletWaveletFilter(eeg, freqs, output='power', width=5, cpus=12, verbose=False).filter() # log the power data = wave_pow.data wave_pow.data = numexpr.evaluate('log10(data)') # get start and stop relative to the spikes epochs = _compute_epochs(events, rel_start_ms, rel_stop_ms, timestamps, sr) bad_epochs = (np.any(epochs < 0, 1)) | (np.any(epochs > len(signals), 1)) epochs = epochs[~bad_epochs] events = events[~bad_epochs].reset_index(drop=True) # mean over time within epochs spikes_x_freqs = np.stack([np.mean(wave_pow.data[:, x[0]:x[1]], axis=1) for x in epochs]) # make dict with keys being cluster numbers. Mean over spikes if desired. pow_spect_dict = {} for this_cluster in events.cluster_num.unique(): if mean_over_spikes: pow_spect_dict[this_cluster] = spikes_x_freqs[events.cluster_num == this_cluster].mean(axis=0) else: pow_spect_dict[this_cluster] = spikes_x_freqs[events.cluster_num == this_cluster] return pow_spect_dict
def load_eeg_from_event_times(events, rel_start_ms, rel_stop_ms, channel_list, buf_ms=0, noise_freq=[58., 62.], downsample_freq=1000, resample_freq=None, pass_band=None, demean=False, do_average_ref=False): """ Returns an EEG TimeSeries object. Parameters ---------- events: pandas.DataFrame An events dataframe rel_start_ms: int Initial time (in ms), relative to the onset of each event rel_stop_ms: int End time (in ms), relative to the onset of each event channel_list: list list of paths to channels (ncs files) buf_ms: int Amount of time (in ms) of buffer to add to both the begining and end of the time interval noise_freq: list Stop filter will be applied to the given range. Default=(58. 62) resample_freq: float Sampling rate to resample to after loading eeg. pass_band: list If given, the eeg will be band pass filtered in the given range. demean: bool If True, will subject the mean voltage between rel_start_ms and rel_stop_ms from each channel do_average_ref: bool If True, will compute the average reference based on the mean voltage across channels Returns ------- TimeSeries EEG timeseries object with dimensions event x time x channel """ # eeg is a PTSA timeseries eeg = _load_eeg_timeseries(events, rel_start_ms, rel_stop_ms, channel_list, buf_ms, downsample_freq) # compute average reference by subracting the mean across channels if do_average_ref: eeg = eeg - eeg.mean(dim='channel') # baseline correct subracting the mean within the baseline time range if demean: eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # resample if desired. Note: can be a bit slow especially if have a lot of eeg data if resample_freq is not None: r_filter = ResampleFilter(eeg, resample_freq) eeg = r_filter.filter() # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) return eeg
def load_eeg_from_event_times(events, rel_start_ms, rel_stop_ms, channel_list, buf_ms=0, noise_freq=[58., 62.], downsample_freq=1000, resample_freq=None, pass_band=None, demean=False, do_average_ref=False): """ Returns an EEG TimeSeries object. Parameters ---------- events: pandas.DataFrame An events dataframe rel_start_ms: int Initial time (in ms), relative to the onset of each event rel_stop_ms: int End time (in ms), relative to the onset of each event channel_list: list list of paths to channels (ncs files) buf_ms: int Amount of time (in ms) of buffer to add to both the begining and end of the time interval noise_freq: list Stop filter will be applied to the given range. Default=(58. 62) resample_freq: float Sampling rate to resample to after loading eeg. pass_band: list If given, the eeg will be band pass filtered in the given range. demean: bool If True, will subject the mean voltage between rel_start_ms and rel_stop_ms from each channel do_average_ref: bool If True, will compute the average reference based on the mean voltage across channels Returns ------- TimeSeries EEG timeseries object with dimensions event x time x channel """ # eeg is a PTSA timeseries eeg = _load_eeg_timeseries(events, rel_start_ms, rel_stop_ms, channel_list, buf_ms, downsample_freq) # compute average reference by subracting the mean across channels if do_average_ref: eeg = eeg - eeg.mean(dim='channel') # baseline correct subracting the mean within the baseline time range if demean: eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: b_filter = ButterworthFilter(eeg, this_noise_freq, filt_type='stop', order=4) eeg = b_filter.filter() # resample if desired. Note: can be a bit slow especially if have a lot of eeg data if resample_freq is not None: r_filter = ResampleFilter(eeg, resample_freq) eeg = r_filter.filter() # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) return eeg
def load_eeg(events, rel_start_ms, rel_stop_ms, buf_ms=0, elec_scheme=None, noise_freq=[58., 62.], resample_freq=None, pass_band=None, use_mirror_buf=False, demean=False, do_average_ref=False): """ Returns an EEG TimeSeries object. Parameters ---------- events: pandas.DataFrame An events dataframe that contains eegoffset and eegfile fields rel_start_ms: int Initial time (in ms), relative to the onset of each event rel_stop_ms: int End time (in ms), relative to the onset of each event buf_ms: Amount of time (in ms) of buffer to add to both the begining and end of the time interval elec_scheme: pandas.DataFrame A dataframe of electrode information, returned by load_elec_info(). If the column 'contact' is in the dataframe, monopolar electrodes will be loads. If the columns 'contact_1' and 'contact_2' are in the df, bipolar will be loaded. You may pass in a subset of rows to only load data for electrodes in those rows. If you do not enter an elec_scheme, all monopolar channels will be loaded (but they will not be labeled with correct channel tags). Entering a scheme is recommended. noise_freq: list Stop filter will be applied to the given range. Default=(58. 62) resample_freq: float Sampling rate to resample to after loading eeg. pass_band: list If given, the eeg will be band pass filtered in the given range. use_mirror_buf: bool If True, the buffer will be data taken from within the rel_start_ms to rel_stop_ms interval, mirrored and prepended and appended to the timeseries. If False, data outside the rel_start_ms and rel_stop_ms interval will be read. demean: bool If True, will subject the mean voltage between rel_start_ms and rel_stop_ms from each channel do_average_ref: bool If True, will compute the average reference based on the mean voltage across channels Returns ------- TimeSeries EEG timeseries object with dimensions channels x events x time (or bipolar_pairs x events x time) NOTE: The EEG data is returned with time buffer included. If you included a buffer and want to remove it, you may use the .remove_buffer() method. EXTRA NOTE: INPUT SECONDS FOR REMOVING BUFFER, NOT MS!! """ # check if monopolar is possible for this subject if 'contact' in elec_scheme: eegfile = np.unique(events.eegfile)[0] if os.path.splitext(eegfile)[1] == '.h5': eegfile = f'/protocols/r1/subjects/{events.iloc[0].subject}/experiments/{events.iloc[0].experiment}/sessions/{events.iloc[0].session}/ephys/current_processed/noreref/{eegfile}' with h5py.File(eegfile, 'r') as f: if not np.array(f['monopolar_possible'])[0] == 1: print('Monopolar referencing not possible for {}'.format( events.iloc[0].subject)) return # add buffer is using if (buf_ms is not None) and not use_mirror_buf: actual_start = rel_start_ms - buf_ms actual_stop = rel_stop_ms + buf_ms else: actual_start = rel_start_ms actual_stop = rel_stop_ms # load eeg eeg = CMLReader(subject=events.iloc[0].subject).load_eeg( events, rel_start=actual_start, rel_stop=actual_stop, scheme=elec_scheme).to_ptsa() # now auto cast to float32 to help with memory issues with high sample rate data eeg.data = eeg.data.astype('float32') # baseline correct subracting the mean within the baseline time range if demean: eeg = eeg.baseline_corrected([rel_start_ms, rel_stop_ms]) # compute average reference by subracting the mean across channels if do_average_ref: eeg = eeg - eeg.mean(dim='channel') # add mirror buffer if using. PTSA is expecting this to be in seconds. if use_mirror_buf: eeg = eeg.add_mirror_buffer(buf_ms / 1000.) # filter line noise if noise_freq is not None: if isinstance(noise_freq[0], float): noise_freq = [noise_freq] for this_noise_freq in noise_freq: for this_chan in range(eeg.shape[1]): b_filter = ButterworthFilter(eeg[:, this_chan:this_chan + 1], this_noise_freq, filt_type='stop', order=4) eeg[:, this_chan:this_chan + 1] = b_filter.filter() # resample if desired. Note: can be a bit slow especially if have a lot of eeg data # pdb.set_trace() # if resample_freq is not None: # for this_chan in range(eeg.shape[1]): # r_filter = ResampleFilter(eeg[:, this_chan:this_chan+1], resample_freq) # eeg[:, this_chan:this_chan + 1] = r_filter.filter() if resample_freq is not None: eeg_resamp = [] for this_chan in range(eeg.shape[1]): r_filter = ResampleFilter(eeg[:, this_chan:this_chan + 1], resample_freq) eeg_resamp.append(r_filter.filter()) coords = {x: eeg[x] for x in eeg.coords.keys()} coords['time'] = eeg_resamp[0]['time'] coords['samplerate'] = resample_freq dims = eeg.dims eeg = TimeSeries.create(np.concatenate(eeg_resamp, axis=1), resample_freq, coords=coords, dims=dims) # do band pass if desired. if pass_band is not None: eeg = band_pass_eeg(eeg, pass_band) # reorder dims to make events first eeg = make_events_first_dim(eeg) return eeg