def extract_frequency_features(X): from mne.time_frequency import psd_array_multitaper from pywt import wavedec psds, freqs = psd_array_multitaper(X, SAMPLE_RATE, fmax=110) bands = [(name, 0) for (name, _) in FREQ_BANDS] num_bands = len(bands) total = psds.sum(axis=-1) total_no_zeros = np.where(total > 0, total, 1) for i in range(num_bands): fmin = FREQ_BANDS[i][1] fmax = (freqs[-1] + 1) if i == (num_bands - 1) else FREQ_BANDS[i + 1][1] bands[i][1] = (psds[:, :, (freqs >= fmin) & (freqs < fmax)].sum() / total_no_zeros) bands.append(["energy", total]) dwt_level = 7 wavelet = "db4" coeff_approx, coeff_detail = wavedec(X, wavelet, level=dwt_level)[:2] X_enriched = np.concatenate( [band for (_, band) in bands] + [ coeff_approx.swapaxes(2, 1).reshape(X.shape[0], -1), coeff_detail.swapaxes(2, 1).reshape(X.shape[0], -1), ], axis=-1, ) features = [band for (band, _) in bands] features += [f"cA{dwt_level}_{i}" for i in range(coeff_approx.shape[-1])] features += [f"cD{dwt_level}_{i}" for i in range(coeff_detail.shape[-1])] extractor = {"wavedec": {"wavelet": wavelet, "level": dwt_level}} return X_enriched, extractor, features
def hfb_to_psd(hfb, start=500, stop=None, duration=20, tmin=-0.1, tmax=20, preload=True, baseline=None, fmin=0.1, fmax=20, adaptive=True, bandwidth=0.5, sfreq=500): events = mne.make_fixed_length_events(hfb, start=start, stop=stop, duration=duration) epochs = mne.Epochs(hfb, events, tmin=tmin, tmax=tmax, baseline=None, preload=True) X = epochs.copy().get_data() (n_trials, n_chans, n_times) = X.shape psd, freqs = psd_array_multitaper(X, sfreq, fmin=fmin, fmax=fmax, bandwidth=bandwidth, adaptive=adaptive) psd = np.mean(psd, axis=0) # average over channels return psd, freqs
def bandpower(data, sf, band, method='welch', window_sec=None, relative=False): """Compute the average power of the signal x in a specific frequency band. Requires MNE-Python >= 0.14. Parameters ---------- data : 1d-array Input signal in the time-domain. sf : float Sampling frequency of the data. band : list Lower and upper frequencies of the band of interest. method : string Periodogram method: 'welch' or 'multitaper' window_sec : float Length of each window in seconds. Useful only if method == 'welch'. If None, window_sec = (1 / min(band)) * 2. relative : boolean If True, return the relative power (= divided by the total power of the signal). If False (default), return the absolute power. Return ------ bp : float Absolute or relative band power. """ from scipy.signal import welch from scipy.integrate import simps from mne.time_frequency import psd_array_multitaper import numpy as np band = np.asarray(band) low, high = band # Compute the modified periodogram (Welch) if method == 'welch': if window_sec is not None: nperseg = window_sec * sf else: nperseg = (2 / low) * sf freqs, psd = welch(data, sf, nperseg=nperseg) elif method == 'multitaper': psd, freqs = psd_array_multitaper(data, sf, adaptive=True, normalization='full', verbose=0) # Frequency resolution freq_res = freqs[1] - freqs[0] # Find index of band in frequency vector idx_band = np.logical_and(freqs >= low, freqs <= high) # Integral approximation of the spectrum using parabola (Simpson's rule) bp = simps(psd[idx_band], dx=freq_res) if relative: bp /= simps(psd, dx=freq_res) return bp
def get_psd(data, rate, win_size): ### Demo cells: 583 (strong periodicity) and 48 (low periodicity) pcf = pipe.load_pcf(r'W:\Neurophysiology-Storage1\Wahl\Hendrik\PhD\Data\Batch3\M41\20200625') neurons = [583, 49] rate = 1/5 # samples per centimeter fig, ax = plt.subplots(len(neurons), 3, sharex='col') for i, neuron in enumerate(neurons): data = pcf.bin_avg_activity[neuron] psd, freqs = psd_array_multitaper(data, rate, adaptive=True, normalization='full') # Plot activity ax[i, 0].plot(np.arange(0, 400, 5), data) ax[i, 0].set_ylabel(f'mean dF/F') # Plot frequencies ax[i, 1].plot(freqs, psd) ax[i, 1].set_ylabel(f'PSD') # Plot periods ax[i, 2].plot(1/freqs, psd) ax[i, 2].set_ylabel(f'PSD') ax[0, 0].set_title(f'Spatial activity map') ax[0, 1].set_title(f'Frequency power density') ax[0, 2].set_title(f'Period power density') ax[1, 0].set_xlabel(f'VR position [cm]') ax[1, 1].set_xlabel(f'Frequency [1/cm]') ax[1, 2].set_xlabel(f'Period [cm]') plt.tight_layout()
def psd_multitaper(arr, **mne_kwargs): if not mne_kwargs: mne_kwargs = DEFAULT_PSD_KWARGS psd, freqs = psd_array_multitaper(arr.T, **mne_kwargs) psd = np.expand_dims(psd.T, axis=0) return psd, freqs
def multitaper_diagram(self, data, channel, plot=False): psd, freq = psd_array_multitaper(data, self.sf, adaptive=True, normalization='full', verbose=0) if plot: if channel is "CH1": self.ch1_d_line.set_data(freq, psd) self.ch1_d_fig.set_xlim(left=min(freq), right=max(freq)) self.ch1_d_fig.set_ylim(min(psd), max(psd)) pyplot.draw() pyplot.pause(0.0001) if channel is "CH2": self.ch2_d_line.set_data(freq, psd) self.ch2_d_fig.set_xlim(left=min(freq), right=max(freq)) self.ch2_d_fig.set_ylim(min(psd), max(psd)) pyplot.draw() pyplot.pause(0.0001) return freq, psd
def calc_psd_mt(self, bandwidth): """ Calculate multitaper power spectrum Params ------ bandwidth: float frequency resolution (NW) Returns ------- psd_mt: dict 'freqs': ndarray 'psd': ndarray. power spectral density in (V^2/Hz). 10log10 to convert to dB. """ self.metadata['analysis_info']['psd_method'] = 'multitaper' self.metadata['analysis_info']['psd_bandwidth'] = bandwidth sf_interp = self.metadata['analysis_info']['s_freq_interp'] pwr, freqs = psd_array_multitaper(self.ii_interp, sf_interp, adaptive=True, bandwidth=bandwidth, normalization='full', verbose=0) self.psd_mt = {'freqs': freqs, 'pwr': pwr} self.metadata['analysis_info']['psd_method'] = 'multitaper'
def _pca15_fft(flip, data): U, s, V = linalg.svd(data, full_matrices=False) maxeig = 15 # use average power in label for scaling epoch_spectra, freq_bins = psd_array_multitaper( V[0:maxeig], 300, #!!!!################ HardCodede fmin=1, fmax=45, bandwidth=2, n_jobs=1, adaptive=True, low_bias=True, normalization='full') eigval_weighted_spectra = s[0:maxeig, np.newaxis] * epoch_spectra # Reject top and bottom 10% using trimmed mean output_spectra = trim_mean(eigval_weighted_spectra, 0.1, axis=0) # Normalize by number of samples normalized_spectra = output_spectra / np.sqrt(len(data)) return output_spectra
print('epoch_event_DONE') #baseline_chunk_around_event = ch_downsampled[baseline_idx-offset : baseline_idx+offset] for b, base in enumerate(baseline_idx): baseline_chunk_around_event[b,:] = ch_downsampled[base-offset : base+offset] #print(b) print('epoch_baseline_DONE') ch_downsampled = None chunk_lenght = offset*2 p_ch, f_ch = time_frequency.psd_array_multitaper(chunk_around_event, sfreq= 1000, fmin = 1, fmax = 60, bandwidth = 2.5, n_jobs = 8) p_base, f_base = time_frequency.psd_array_multitaper(baseline_chunk_around_event, sfreq= 1000, fmin = 1, fmax = 60, bandwidth = 2.5, n_jobs = 8) # # # test = np.sum(p_base) # # # for t in arange(len(downsampled_event_idx)): # # if t < 5: # figure_name= str(t)+'.png' # plt.plot(f_base, p_base[t], color = 'k',alpha=1, label = 'touch', linewidth= 1) # plt.title('base') # else: # plt.plot(f_base, p_base[t], color = '#228B22',alpha=0.3, label = 'touch', linewidth= 1)
raw = mne.concatenate_raws(raws) bem_fname = op.join(bem_dir, subject + '-20480-bem-sol.fif') bem = mne.read_bem_solution(bem_fname) src = mne.setup_source_space(subject, spacing='ico5', add_dist=False, subjects_dir=subjects_mri_dir) fwd = mne.make_forward_solution(raw.info, trans=trans_file, src=src, bem=bem, meg=True, eeg=False, n_jobs=2) inv = mne.minimum_norm.make_inverse_operator(raw.info, fwd, cov, loose=0.2, depth=0.8) labels = mne.read_labels_from_annot(subject, 'aparc', subjects_dir=subjects_mri_dir) labels_name = np.array([label.name for label in labels]) stcs = mne.minimum_norm.apply_inverse_epochs(epochs, inv, lambda2=1. / 9., pick_ori='normal', return_generator=True) label_ts = np.array(mne.extract_label_time_course(stcs, labels, inv['src'], return_generator=False)) psds, freqs = psd_array_multitaper(label_ts, epochs.info['sfreq'], fmin=2, fmax=55) tfr_alpha = tfr_array_multitaper(label_ts, epochs.info['sfreq'], freqs=np.arange(8, 13), output='avg_power', n_jobs=4) tfr_beta = tfr_array_multitaper(label_ts, epochs.info['sfreq'], freqs=np.arange(16, 30), output='avg_power', n_jobs=4) tfr_lgamma = tfr_array_multitaper(label_ts, epochs.info['sfreq'], freqs=np.arange(30, 55), output='avg_power', n_jobs=4) tfr_hgamma = tfr_array_multitaper(label_ts, epochs.info['sfreq'], freqs=np.arange(65, 100), output='avg_power', n_jobs=4) for ix, inds in enumerate(np.split(np.arange(68), 4)): plt.figure(figsize=(15, 20)) plt.rc('xtick', labelsize=25) plt.rc('ytick', labelsize=25) lineObjects = plt.plot(freqs, 20 * np.log10(psds.mean(0).T)[:, inds], linewidth=4) plt.xlabel('Frequency (Hz)', fontsize=30) plt.ylabel('Power (20*log10)', fontsize=30) plt.xlim(2, 55)
def main(): count = 600 sn = socket.socket() hostn = 'localhost' portn = 14564 sn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sn.bind((hostn, portn)) print('Server Started') sn.listen(5) cn, addrn = sn.accept() print('Got connection from', addrn) mapping = {0: 'Wake', 1: 'N1', 2: 'N2', 3: 'N3', 4: 'REM'} # hyp = {0: 4, 1: 2, 2: 1, 3: 0, 4: 3} channels = { 0: 'EEG-FPZ-CZ', 1: 'EEG-PZ-OZ', 2: 'EOG', 3: 'Resp-Oro-Nasal', 4: 'EMG', 5: 'Temp' } npz_file = 'SleepEDF_NPZ/SC4001E0.npz' with np.load(npz_file) as f: data = f["x"] labels = f["y"] save_path = 'data/epoch_custom.npz' save_dict = {"x": data[count, :, :], "y": labels[count]} np.savez(save_path, **save_dict) rt = RepeatedTimer(30, func, qu) # it auto-starts, no need of rt.start() while True: print("Epoch Number: ", count) save_dict = {'x': data[count, :, :], 'y': labels[count]} np.savez(save_path, **save_dict) dict_temp = qu.get() grads = dict_temp["grads"] sleepstage = dict_temp["Y_pred"] print(sleepstage) json_data = data[count, :, :] from datetime import datetime now = datetime.now() print("now =", now) root_time = strftime("%b-%d-%Y %H:%M") just_time = strftime("%H:%M:%S") json_data_eeg_fpzcz = np.transpose(np.transpose(json_data)[0][:][:]) json_data_eeg_pzoz = np.transpose(np.transpose(json_data)[1][:][:]) json_data_eeg_fpzcz = json_data_eeg_fpzcz.reshape(3000, ) json_data_eeg_pzoz = json_data_eeg_pzoz.reshape(3000, ) plt.specgram(json_data_eeg_fpzcz, Fs=100, cmap='viridis') plt.ylabel('Frequency [Hz]') plt.xlabel('Time [sec]') plt.savefig('../frontend/views/eeg_fpzcz_specgram.png', bbox_inches='tight') sf = 100 # Define window length (4 seconds) win = 4 * sf # Apply the detection using yasa.spindles_detect sp = yasa.spindles_detect(json_data_eeg_fpzcz, sf) sw = sw_detect(json_data_eeg_fpzcz, sf, include=(2, 3), freq_sw=(0.3, 2), dur_neg=(0.3, 1.5), dur_pos=(0.1, 1), amp_neg=(40, 300), amp_pos=(10, 150), amp_ptp=(75, 400), remove_outliers=False, coupling=False, freq_sp=(12, 16)) if sp is None: mask_spindles = np.zeros(len(json_data_eeg_fpzcz)) else: mask_spindles = sp.get_mask() spindles_highlight = json_data_eeg_fpzcz * mask_spindles spindles_highlight[spindles_highlight == 0] = np.nan if sw is None: mask_sw = np.zeros(len(json_data_eeg_fpzcz)) else: mask_sw = sw.get_mask() sw_highlight = json_data_eeg_fpzcz * mask_sw sw_highlight[sw_highlight == 0] = np.nan all_rows = [] for i in range(len(json_data_eeg_fpzcz)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row["EEG_FPZ_CZ"] = json_data_eeg_fpzcz[i] if (np.isnan(spindles_highlight[i])): row["EEG_FPZ_CZ_Spindle"] = None else: row["EEG_FPZ_CZ_Spindle"] = json_data_eeg_fpzcz[i] if (np.isnan(sw_highlight[i])): row["EEG_FPZ_CZ_Slow Waves"] = None else: row["EEG_FPZ_CZ_Slow Waves"] = json_data_eeg_fpzcz[i] all_rows.append(row) with open('../frontend/data/EEG-FPZ-CZ.json', 'w') as f: f.write(str(all_rows).replace('\'', '"').replace('None', 'null')) # Apply the detection using yasa.spindles_detect sp = yasa.spindles_detect(json_data_eeg_pzoz, sf) sw = sw_detect(json_data_eeg_pzoz, sf, include=(2, 3), freq_sw=(0.3, 2), dur_neg=(0.3, 1.5), dur_pos=(0.1, 1), amp_neg=(40, 300), amp_pos=(10, 150), amp_ptp=(75, 400), remove_outliers=False, coupling=False, freq_sp=(12, 16)) if sp is None: mask_spindles = np.zeros(len(json_data_eeg_pzoz)) else: mask_spindles = sp.get_mask() spindles_highlight = json_data_eeg_pzoz * mask_spindles spindles_highlight[spindles_highlight == 0] = np.nan if sw is None: mask_sw = np.zeros(len(json_data_eeg_pzoz)) else: mask_sw = sw.get_mask() sw_highlight = json_data_eeg_pzoz * mask_sw sw_highlight[sw_highlight == 0] = np.nan all_rows = [] for i in range(len(json_data_eeg_pzoz)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row["EEG_PZ_OZ"] = json_data_eeg_pzoz[i] if (np.isnan(spindles_highlight[i])): row["EEG_PZ_OZ_Spindle"] = None else: row["EEG_PZ_OZ_Spindle"] = json_data_eeg_pzoz[i] if (np.isnan(sw_highlight[i])): row["EEG_PZ_OZ_Slow Waves"] = None else: row["EEG_PZ_OZ_Slow Waves"] = json_data_eeg_pzoz[i] all_rows.append(row) with open('../frontend/data/EEG-PZ-OZ.json', 'w') as f: f.write(str(all_rows).replace('\'', '"').replace('None', 'null')) # EEG-FPZ-CZ-Grad all_rows = [] for i in range(len(json_data_eeg_fpzcz)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row["EEG-FPZ-CZ"] = json_data_eeg_fpzcz[i] if (grads[0, i] < 0.15): row["EEG-FPZ-CZ-Grad"] = None else: row["EEG-FPZ-CZ-Grad"] = json_data_eeg_fpzcz[i] all_rows.append(row) with open('../frontend/data/EEG-FPZ-CZ-Grad.json', 'w') as f: f.write(str(all_rows).replace('\'', '"').replace('None', 'null')) # EEG-PZ-OZ-Grad all_rows = [] for i in range(len(json_data_eeg_pzoz)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row["EEG-PZ-OZ"] = json_data_eeg_pzoz[i] if (grads[0, i] < 0.15): row["EEG-PZ-OZ-Grad"] = None else: row["EEG-PZ-OZ-Grad"] = json_data_eeg_pzoz[i] all_rows.append(row) with open('../frontend/data/EEG-PZ-OZ-Grad.json', 'w') as f: f.write(str(all_rows).replace('\'', '"').replace('None', 'null')) #Other channel grads for index in range(2, 6): json_data_tmp = np.transpose(np.transpose(json_data)[index][:][:]) json_data_tmp = json_data_tmp.reshape(3000, ) all_rows = [] for i in range(len(json_data_tmp)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row[channels[index]] = json_data_tmp[i] if (grads[0, i] < 0.15): row[channels[index] + "-Grad"] = None else: row[channels[index] + "-Grad"] = json_data_tmp[i] all_rows.append(row) with open('../frontend/data/' + channels[index] + '-Grad.json', 'w') as f: f.write( str(all_rows).replace('\'', '"').replace('None', 'null')) #Normal non-EEG channels for index in range(2, 6): json_data_tmp = np.transpose(np.transpose(json_data)[index][:][:]) json_data_tmp = json_data_tmp.reshape(3000, ) all_rows = [] for i in range(len(json_data_tmp)): current_time = root_time + ':{}:{}0'.format( str(math.floor(i / 100)).zfill(2), str(math.floor(i % 100)).zfill(2)) current_time = "{}".format(current_time).replace('\'', '') row = {} row["date"] = current_time row[channels[index]] = json_data_tmp[i] all_rows.append(row) with open('../frontend/data/' + channels[index] + '.json', 'w') as f: f.write( str(all_rows).replace('\'', '"').replace('None', 'null')) psd, freqs = psd_array_multitaper(json_data_eeg_fpzcz, sf, normalization='full', verbose=0) all_rows = [] for i in range(len(freqs[:1801])): #Upto 60Hz (Elaborate) row = {} row["Frequencies"] = freqs[i] row["PSD"] = psd[i] all_rows.append(row) with open('../frontend/data/PSD-FPZCZ.json', 'w') as f: f.write(str(all_rows).replace('\'', '"')) psd, freqs = psd_array_multitaper(json_data_eeg_pzoz, sf, normalization='full', verbose=0) all_rows = [] for i in range(len(freqs[:1801])): row = {} row["Frequencies"] = freqs[i] row["PSD"] = psd[i] all_rows.append(row) with open('../frontend/data/PSD-PZOZ.json', 'w') as f: f.write(str(all_rows).replace('\'', '"')) from scipy.fftpack import rfft, rfftfreq SAMPLE_RATE = 100 DURATION = 30 # Number of samples in normalized_tone N = SAMPLE_RATE * DURATION yf = rfft(json_data_eeg_fpzcz) xf = rfftfreq(N, 1 / SAMPLE_RATE) all_rows = [] for i in range(len(xf)): row = {} row["x"] = xf[i] row["y"] = np.abs(yf[i]) all_rows.append(row) with open('../frontend/data/FFT-FPZCZ.json', 'w') as f: f.write(str(all_rows).replace('\'', '"')) count_json = {} count_json["data"] = count with open('../frontend/data/count.json', 'w') as f: f.write(str(count_json).replace('\'', '"')) print("Sleepstage: ", mapping[sleepstage]) row = {} row["time"] = just_time row["stage"] = sleepstage + 1 print(row) with open('../frontend/data/sleepstage.json', 'w') as f: f.write(str(row).replace('\'', '"')) # nodeserv(hyp[int(sleepstage)], cn) nodeserv(int(sleepstage), cn) count += 1 qu.task_done() try: sleep(150) # your long-running job goes here finally: rt.stop( ) # better in a try/finally block to make sure the program ends
# #baseline_chunk_around_event = ch_downsampled[baseline_idx-offset : baseline_idx+offset] # for b, base in enumerate(baseline_idx): # # baseline_chunk_around_event[b,:] = ch_downsampled[base-offset : base+offset] # #print(b) # print('epoch_baseline_DONE') # print('epoch_event_DONE') # # #half size chunk double bandwidth ch_downsampled = None p_before, f_before = time_frequency.psd_array_multitaper(chunk_before, sfreq= 1000, fmin = 1, fmax = 300, bandwidth = 10, n_jobs = 8) p_after, f_after= time_frequency.psd_array_multitaper(chunk_after, sfreq= 1000, fmin = 1, fmax = 300, bandwidth = 10, n_jobs = 8) #p_base, f_base = time_frequency.psd_array_multitaper(baseline_chunk_around_event, sfreq= 1000, fmin = 1, fmax = 100, bandwidth = 2.5, n_jobs = 8) #p_base_avg = np.mean(p_base, axis =0) #[:len(downsampled_event_idx)] #p_base_sem = stats.sem(p_base, axis = 0) # tot baseline excluding noise and sum # baseline_tot = [i for i,v in enumerate(f_base) if v <45 or v>55 ] # baseline_sel = p_base_avg[baseline_tot] # baseline_sum_tot = np.sum(baseline_sel) # tot_base_sum.append(baseline_sum_tot) #
cross_population_hfb, populations = hf.ts_to_population_hfb( cross_ts, df_all_visual_chans, parcellation='group') (npop, m, N, ncat) = cross_population_hfb.shape new_shape = (npop, ncat, N, m) cross_population_hfb = np.transpose(cross_population_hfb, (0, 3, 2, 1)) #%% Compute psd fmin = 0.1 fmax = 100 adaptive = True bandwidth = 2 psd, freqs = psd_array_multitaper(cross_population_hfb, args.sfreq, fmin=fmin, fmax=fmax, bandwidth=bandwidth, adaptive=adaptive) average_psd = np.mean(psd, axis=2) max_psd = np.max(psd) #%% Plot psd in each conditions for each populations sns.set(font_scale=1.6) f, ax = plt.subplots(3, 1) cat = ['Rest', 'Face', 'Place'] bands = [4, 8, 16, 31] bands_name = [r'$\delta$', r'$\theta$', r'$\alpha$', r'$\beta$', r'$\gamma$'] xbands = [2, 6, 12, 18, 50] ybands = [12] * 5 for i in range(ncat): for j in range(npop):
def test_psd(): """Tests the welch and multitaper PSD.""" raw = read_raw_fif(raw_fname) picks_psd = [0, 1] # Populate raw with sinusoids rng = np.random.RandomState(40) data = 0.1 * rng.randn(len(raw.ch_names), raw.n_times) freqs_sig = [8., 50.] for ix, freq in zip(picks_psd, freqs_sig): data[ix, :] += 2 * np.sin(np.pi * 2. * freq * raw.times) first_samp = raw._first_samps[0] raw = RawArray(data, raw.info) tmin, tmax = 0, 20 # use a few seconds of data fmin, fmax = 2, 70 # look at frequencies between 2 and 70Hz n_fft = 128 # -- Raw -- kws_psd = dict(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax, picks=picks_psd) # Common to all kws_welch = dict(n_fft=n_fft) kws_mt = dict(low_bias=True) funcs = [(psd_welch, kws_welch), (psd_multitaper, kws_mt)] for func, kws in funcs: kws = kws.copy() kws.update(kws_psd) kws.update(verbose='debug') if func is psd_welch: kws.update(window='hann') with catch_logging() as log: psds, freqs = func(raw, proj=False, **kws) log = log.getvalue() if func is psd_welch: assert f'{n_fft}-point FFT on {n_fft} samples with 0 overl' in log assert 'hann window' in log psds_proj, freqs_proj = func(raw, proj=True, **kws) assert psds.shape == (len(kws['picks']), len(freqs)) assert np.sum(freqs < 0) == 0 assert np.sum(psds < 0) == 0 # Is power found where it should be ixs_max = np.argmax(psds, axis=1) for ixmax, ifreq in zip(ixs_max, freqs_sig): # Find nearest frequency to the "true" freq ixtrue = np.argmin(np.abs(ifreq - freqs)) assert (np.abs(ixmax - ixtrue) < 2) # Make sure the projection doesn't change channels it shouldn't assert_array_almost_equal(psds, psds_proj) # Array input shouldn't work pytest.raises(ValueError, func, raw[:3, :20][0]) # test n_per_seg in psd_welch (and padding) psds1, freqs1 = psd_welch(raw, proj=False, n_fft=128, n_per_seg=128, **kws_psd) psds2, freqs2 = psd_welch(raw, proj=False, n_fft=256, n_per_seg=128, **kws_psd) assert (len(freqs1) == np.floor(len(freqs2) / 2.)) assert (psds1.shape[-1] == np.floor(psds2.shape[-1] / 2.)) kws_psd.update(dict(n_fft=tmax * 1.1 * raw.info['sfreq'])) with pytest.raises(ValueError, match='n_fft is not allowed to be > n_tim'): psd_welch(raw, proj=False, n_per_seg=None, **kws_psd) kws_psd.update(dict(n_fft=128, n_per_seg=64, n_overlap=90)) with pytest.raises(ValueError, match='n_overlap cannot be greater'): psd_welch(raw, proj=False, **kws_psd) with pytest.raises(ValueError, match='No frequencies found'): psd_array_welch(np.zeros((1, 1000)), 1000., fmin=10, fmax=1) # -- psd_array_multitaper -- psd_complex, freq, weights = psd_array_multitaper(raw._data[:4, :500], raw.info['sfreq'], output='complex') psd, freq = psd_array_multitaper(raw._data[:4, :500], raw.info['sfreq'], output='power') assert psd_complex.ndim == 3 # channels x tapers x freqs psd_from_complex = _psd_from_mt(psd_complex, weights) assert_allclose(psd_from_complex, psd) # -- Epochs/Evoked -- events = read_events(event_fname) events[:, 0] -= first_samp tmin, tmax, event_id = -0.5, 0.5, 1 epochs = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks_psd, proj=False, preload=True, baseline=None) evoked = epochs.average() tmin_full, tmax_full = -1, 1 epochs_full = Epochs(raw, events[:10], event_id, tmin_full, tmax_full, picks=picks_psd, proj=False, preload=True, baseline=None) kws_psd = dict(tmin=tmin, tmax=tmax, fmin=fmin, fmax=fmax, picks=picks_psd) # Common to all funcs = [(psd_welch, kws_welch), (psd_multitaper, kws_mt)] for func, kws in funcs: kws = kws.copy() kws.update(kws_psd) psds, freqs = func(epochs[:1], proj=False, **kws) psds_proj, freqs_proj = func(epochs[:1], proj=True, **kws) psds_f, freqs_f = func(epochs_full[:1], proj=False, **kws) # this one will fail if you add for example 0.1 to tmin assert_array_almost_equal(psds, psds_f, 27) # Make sure the projection doesn't change channels it shouldn't assert_array_almost_equal(psds, psds_proj, 27) # Is power found where it should be ixs_max = np.argmax(psds.mean(0), axis=1) for ixmax, ifreq in zip(ixs_max, freqs_sig): # Find nearest frequency to the "true" freq ixtrue = np.argmin(np.abs(ifreq - freqs)) assert (np.abs(ixmax - ixtrue) < 2) assert (psds.shape == (1, len(kws['picks']), len(freqs))) assert (np.sum(freqs < 0) == 0) assert (np.sum(psds < 0) == 0) # Array input shouldn't work pytest.raises(ValueError, func, epochs.get_data()) # Testing evoked (doesn't work w/ compute_epochs_psd) psds_ev, freqs_ev = func(evoked, proj=False, **kws) psds_ev_proj, freqs_ev_proj = func(evoked, proj=True, **kws) # Is power found where it should be ixs_max = np.argmax(psds_ev, axis=1) for ixmax, ifreq in zip(ixs_max, freqs_sig): # Find nearest frequency to the "true" freq ixtrue = np.argmin(np.abs(ifreq - freqs_ev)) assert (np.abs(ixmax - ixtrue) < 2) # Make sure the projection doesn't change channels it shouldn't assert_array_almost_equal(psds_ev, psds_ev_proj, 27) assert (psds_ev.shape == (len(kws['picks']), len(freqs)))
def power_spectral_density(data: np.ndarray, band: Tuple[float, float], sampling_rate: float = 100.0, window_length: float = 4.0, plot: bool = False, method: PSD_TYPE = PSD_TYPE.WELCH, relative=False): """Power spectral density: Many thanks to: https://raphaelvallat.github.io/bandpower.html Parameters ---------- data: Numpy Array. Time series data in the form of a numpy ndarray used to estimate the power spectral density. band: tuple. frequency band psd to export. Note this must be within the Nyquist frequency for your data. Approximated as sampling rate / 2. sampling_rate: float Sampling rate of the data in # of samples per second. window_length: float Length in seconds of data window. plot: boolean. Whether of not to plot the PSD estimated. Helpful for debugging and exploration. method: PSD_TYPE Type of PSD estimation method to use during calculation. relative: boolean Whether or not to express the power in a frequency band as a percentage of the total power of the signal. """ band = np.asarray(band) low, high = band # Compute the modified periodogram (Welch) if method == PSD_TYPE.WELCH: nperseg = window_length * sampling_rate freqs, psd = welch(data, sampling_rate, nperseg=nperseg) # Compute the modified periodogram (MultiTaper) elif method == PSD_TYPE.MULTITAPER: psd, freqs = psd_array_multitaper(data, sampling_rate, adaptive=True, normalization='full', verbose=False) # Find index of band in frequency vector idx_band = np.logical_and(freqs >= low, freqs <= high) # Frequency resolution freq_res = freqs[1] - freqs[0] # Integral approximation of the spectrum using parabola (Simpson's rule) bp = simps(psd[idx_band], dx=freq_res) # Plot the power spectrum if plot: sns.set(font_scale=1.2, style='white') plt.figure(figsize=(8, 4)) plt.plot(freqs, psd, color='k', lw=2) plt.xlabel('Frequency (Hz)') plt.ylabel('Power spectral density (V^2 / Hz)') plt.ylim([0, psd.max() * 1.1]) plt.title(f'{method.value}') plt.xlim([0, sampling_rate / 2]) sns.despine() plt.show() # Whether or not to return PSD as a percentage of total power if relative: bp /= simps(psd, dx=freq_res) return bp
def extract_power_band_metrics(data_segment, **params): """ From a given EEG data segment, extract EEG features :param data_segment: DataFrame, table of electrode timeseries segment data :param eeg_metric: str, 'coherence' or 'power', for spectral coherence, or power band calculations :param channels: list, channel names to use in the feature calculations :param s_rate: float, data sampling rate :param bands: dict, dictionary of tuple of start and stop frequencies for desired power bands :return: (ndarray, list), calculated user features and corresponding names; or None if window was too small """ eeg_metric = params['eeg_metric'] _avail_metrics = ['power', 'asymmetry', 'coherence'] assert eeg_metric in _avail_metrics, f"Supported EEG metrics: {', '.join(_avail_metrics)}" channels = params['channels'] srate = params['s_rate'] data = data_segment.loc[:, channels].values bands = params['bands'] band_names = list(bands.keys()) band_names.sort() feature_names = [] metrics = [] freq = None pwr = None freq_mins = [band[0] for band in bands.values()] freq_maxs = [band[1] for band in bands.values()] # Enforce minimum of 1 seconds for data if len(data) / srate < 1.0: log = logging.getLogger(__name__) log.warning( "Extract Power Metrics: At least 1 second of data required") return None, None # Multitaper power band calculation if eeg_metric == 'power': for freq_min, freq_max in zip(freq_mins, freq_maxs): pwr, freq = psd_array_multitaper(data.T, srate, adaptive=True, normalization='full', verbose=False, fmin=freq_min, fmax=freq_max) metrics.append(np.log10(pwr.sum(axis=1))) for band in band_names: for chan in channels: feature_names.append("{:s}_{:s}".format(chan, band)) elif eeg_metric == 'asymmetry': n_chan = data.shape[1] // 2 data_left = data[:, :n_chan] data_right = data[:, n_chan:] for freq_min, freq_max in zip(freq_mins, freq_maxs): pwr_left, freq = psd_array_multitaper(data_left.T, srate, adaptive=True, normalization='full', verbose=False, fmin=freq_min, fmax=freq_max) pwr_right, freq = psd_array_multitaper(data_right.T, srate, adaptive=True, normalization='full', verbose=False, fmin=freq_min, fmax=freq_max) metrics.append( np.log10(pwr_right.sum(axis=1)) - np.log10(pwr_left.sum(axis=1))) for band in band_names: for chan_left, chan_right in zip(channels[:n_chan], channels[n_chan:]): feature_names.append("{:s}_{:s}_{:s}".format( chan_left, chan_right, band)) # Multitaper coherence calculations elif eeg_metric == 'coherence': c_idxs = np.stack( [c_pair for c_pair in combinations(range(len(channels)), 2)]) coh = spectral_connectivity([data.T], sfreq=srate, fmin=freq_mins, fmax=freq_maxs, faverage=True, method='coh', mode='multitaper', verbose=False) pwr, freq = coh[:2] for band in band_names: for c_pair in c_idxs: c0, c1 = c_pair feature_names.append("{:s}_{:s}_{:s}".format( channels[c0], channels[c1], band)) for _, band in enumerate(band_names): # Flatten coherence output container metrics = pwr.T[pwr.T > 0].reshape(-1) #coh_band = pwr[:, :, idx].T #metrics.append(coh_band[coh_band != 0].reshape(-1)) return np.hstack(metrics), feature_names
def extract(filepaths, ica=None, fit_ica=True): if ica is None: ica = get_ica_transformers("infomax") epochs, labels = list(), list() for filepath in filepaths: print("loading", filepath) try: gdf = read_raw_gdf( filepath, eog=["EOG-left", "EOG-central", "EOG-right"], exclude=["EOG-left", "EOG-central", "EOG-right"]) events = events_from_annotations(gdf, event_id={ "769": 0, "770": 1, "771": 2, "772": 3 }) epoch = Epochs(gdf, events[0], event_repeated="drop", reject_by_annotation=True, tmin=-.3, tmax=.7, reject=dict(eeg=1e-4)) epoch.drop_bad() except ValueError: print("Error in", filepath) continue epochs.append(epoch) labels.append(epoch.events[:, 2]) labels = np.concatenate(labels) n_epochs, n_channels, n_times = epochs[0].get_data().shape ica_vec = [ epoch.get_data().transpose(1, 0, 2).reshape(n_channels, -1).T for epoch in epochs ] ica_vec = np.concatenate(ica_vec, axis=0) if fit_ica: ica.fit(ica_vec) transformed = ica.transform(ica_vec) transformed = ica_vec transformed = transformed.T.reshape(n_channels, -1, n_times).transpose(1, 0, 2) features, freqs = psd_array_multitaper(transformed, 250., fmin=0, fmax=20, bandwidth=2) n_epochs, _, _ = features.shape # features = features.reshape(n_epochs, -1) features = features.mean(axis=2) labels_placeholder = np.zeros((len(labels), 4)) for i, l in enumerate(labels): labels_placeholder[i, l] = 1 labels = labels_placeholder return features, labels, ica
def power_spectrum(sfreq, data, fmin=0., fmax=256., psd_method='welch', welch_n_fft=256, welch_n_per_seg=None, welch_n_overlap=0, verbose=False): """Power Spectral Density (PSD). Utility function to compute the (one-sided) Power Spectral Density which acts as a wrapper for :func:`mne.time_frequency.psd_array_welch` (if ``method='welch'``) or :func:`mne.time_frequency.psd_array_multitaper` (if ``method='multitaper'``). The multitaper method, although more computationally intensive than Welch's method or FFT, should be prefered for 'short' windows. Welch's method is more suitable for 'long' windows. Parameters ---------- sfreq : float Sampling rate of the data. data : ndarray, shape (..., n_times). fmin : float (default: 0.) Lower bound of the frequency range to consider. fmax : float (default: 256.) Upper bound of the frequency range to consider. psd_method : str (default: 'welch') Method used to estimate the PSD from the data. The valid values for the parameter ``method`` are: ``'welch'``, ``'fft'`` or ``'multitaper'``. welch_n_fft : int (default: 256) The length of the FFT used. The segments will be zero-padded if `welch_n_fft > welch_n_per_seg`. This parameter will be ignored if `method = 'fft'` or `method = 'multitaper'`. welch_n_per_seg : int or None (default: None) Length of each Welch segment (windowed with a Hamming window). If None, `welch_n_per_seg` is equal to `welch_n_fft`. This parameter will be ignored if `method = 'fft'` or `method = 'multitaper'`. welch_n_overlap : int (default: 0) The number of points of overlap between segments. Should be `<= welch_n_per_seg`. This parameter will be ignored if `method = 'fft'` or `method = 'multitaper'`. verbose : bool (default: False) Verbosity parameter. If True, info and warnings related to :func:`mne.time_frequency.psd_array_welch` or :func:`mne.time_frequency.psd_array_multitaper` are printed. Returns ------- psd : ndarray, shape (..., n_freqs) Estimated PSD. freqs : ndarray, shape (n_freqs,) Array of frequency bins. """ _verbose = 40 * (1 - int(verbose)) _fmin, _fmax = max(0, fmin), min(fmax, sfreq / 2) if psd_method == 'welch': _n_fft = min(data.shape[-1], welch_n_fft) return psd_array_welch(data, sfreq, fmin=_fmin, fmax=_fmax, n_fft=_n_fft, verbose=_verbose, n_per_seg=welch_n_per_seg, n_overlap=welch_n_overlap) elif psd_method == 'multitaper': return psd_array_multitaper(data, sfreq, fmin=_fmin, fmax=_fmax, verbose=_verbose) elif psd_method == 'fft': n_times = data.shape[-1] m = np.mean(data, axis=-1) _data = data - m[..., None] spect = np.fft.rfft(_data, n_times) mag = np.abs(spect) freqs = np.fft.rfftfreq(n_times, 1. / sfreq) psd = np.power(mag, 2) / (n_times**2) psd *= 2. psd[..., 0] /= 2. if n_times % 2 == 0: psd[..., -1] /= 2. mask = np.logical_and(freqs >= _fmin, freqs <= _fmax) return psd[..., mask], freqs[mask] else: raise ValueError('The given method (%s) is not implemented. Valid ' 'methods for the computation of the PSD are: ' '`welch`, `fft` or `multitaper`.' % str(psd_method))
chunk_around_event[e,:] = ch_downsampled[event-offset : event+offset] print(e) for b, base in enumerate(baseline_idx): baseline_chunk_around_event[b,:] = ch_downsampled[base-offset : base+offset] print(b) ch_downsampled = None chunk_lenght = offset*2 p_ch, f_ch = time_frequency.psd_array_multitaper(chunk_around_event, sfreq= 1000, fmin = 1, fmax = 100, bandwidth = 2.5, n_jobs = 8) p_base, f_base = time_frequency.psd_array_multitaper(baseline_chunk_around_event, sfreq= 1000, fmin = 1, fmax = 100, bandwidth = 2.5, n_jobs = 8) p_ch_avg = np.mean(p_ch, axis =0) p_ch_sem = stats.sem(p_ch, axis = 0) p_base_avg = np.mean(p_base, axis =0) p_base_sem = stats.sem(p_base) sns.set() fig = plt.figure() plt.plot(f_ch, p_ch_avg, color = '#1E90FF',alpha=0.3, label = 'touch', linewidth= 1)
def plot_spectrum_methods(data, sf, window_sec, band=None, dB=False): """Plot the periodogram, Welch's and multitaper PSD. Requires MNE-Python >= 0.14. Parameters ---------- data : 1d-array Input signal in the time-domain. sf : float Sampling frequency of the data. band : list Lower and upper frequencies of the band of interest. window_sec : float Length of each window in seconds for Welch's PSD dB : boolean If True, convert the power to dB. """ from mne.time_frequency import psd_array_multitaper from scipy.signal import welch, periodogram sns.set(style="white", font_scale=1.2) # Compute the PSD freqs, psd = periodogram(data, sf) freqs_welch, psd_welch = welch(data, sf, nperseg=window_sec * sf) psd_mt, freqs_mt = psd_array_multitaper(data, sf, adaptive=True, normalization='full', verbose=0) sharey = False # Optional: convert power to decibels (dB = 10 * log10(power)) if dB: psd = 10 * np.log10(psd) psd_welch = 10 * np.log10(psd_welch) psd_mt = 10 * np.log10(psd_mt) sharey = True # Start plot fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4), sharex=True, sharey=sharey) # Stem sc = 'slategrey' ax1.stem(freqs, psd, linefmt=sc, basefmt=" ", markerfmt=" ") ax2.stem(freqs_welch, psd_welch, linefmt=sc, basefmt=" ", markerfmt=" ") ax3.stem(freqs_mt, psd_mt, linefmt=sc, basefmt=" ", markerfmt=" ") # Line lc, lw = 'k', 2 ax1.plot(freqs, psd, lw=lw, color=lc) ax2.plot(freqs_welch, psd_welch, lw=lw, color=lc) ax3.plot(freqs_mt, psd_mt, lw=lw, color=lc) # Labels and axes ax1.set_xlabel('Frequency (Hz)') if not dB: ax1.set_ylabel('Power spectral density (V^2/Hz)') else: ax1.set_ylabel('Decibels (dB / Hz)') ax1.set_title('Periodogram') ax2.set_title('Welch') ax3.set_title('Multitaper') if band is not None: ax1.set_xlim(band) ax1.set_ylim(ymin=0) ax2.set_ylim(ymin=0) ax3.set_ylim(ymin=0) sns.despine()