def test_return_filtered(): """Test return filtered option.""" # Check return_filtered # Simulated more noise data and with broader freqquency than the desired X, _, _ = simulate_data(SNR=0.9, freqs_sig=[4, 13]) sf = 250 n_channels = X.shape[0] info = create_info(ch_names=n_channels, sfreq=sf, ch_types='eeg') filt_params_signal = dict(l_freq=freqs_sig[0], h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) # return filtered to true ssd = SSD(info, filt_params_signal, filt_params_noise, sort_by_spectral_ratio=False, return_filtered=True) ssd.fit(X) out = ssd.transform(X) psd_out, freqs = psd_array_welch(out[0], sfreq=250, n_fft=250) freqs_up = int(freqs[psd_out > 0.5][0]), int(freqs[psd_out > 0.5][-1]) assert (freqs_up == freqs_sig) # return filtered to false ssd = SSD(info, filt_params_signal, filt_params_noise, sort_by_spectral_ratio=False, return_filtered=False) ssd.fit(X) out = ssd.transform(X) psd_out, freqs = psd_array_welch(out[0], sfreq=250, n_fft=250) freqs_up = int(freqs[psd_out > 0.5][0]), int(freqs[psd_out > 0.5][-1]) assert (freqs_up != freqs_sig)
def test_psd_nan(): """Test handling of NaN in psd_array_welch.""" n_samples, n_fft, n_overlap = 2048, 1024, 512 x = np.random.RandomState(0).randn(1, n_samples) psds, freqs = psd_array_welch(x[:, :n_fft + n_overlap], float(n_fft), n_fft=n_fft, n_overlap=n_overlap) x[:, n_fft + n_overlap:] = np.nan # what Raw.get_data() will give us psds_2, freqs_2 = psd_array_welch(x, float(n_fft), n_fft=n_fft, n_overlap=n_overlap) assert_allclose(freqs, freqs_2) assert_allclose(psds, psds_2) # 1-d psds_2, freqs_2 = psd_array_welch(x[0], float(n_fft), n_fft=n_fft, n_overlap=n_overlap) assert_allclose(freqs, freqs_2) assert_allclose(psds[0], psds_2) # defaults with catch_logging() as log: psd_array_welch(x, float(n_fft), verbose='debug') log = log.getvalue() assert 'using 256-point FFT on 256 samples with 0 overlap' in log assert 'hamming window' in log
def test_psd_nan(): """Test handling of NaN in psd_array_welch.""" n_samples, n_fft, n_overlap = 2048, 1024, 512 x = np.random.RandomState(0).randn(1, n_samples) psds, freqs = psd_array_welch( x[:n_fft + n_overlap], float(n_fft), n_fft=n_fft, n_overlap=n_overlap) x[n_fft + n_overlap:] = np.nan # what Raw.get_data() will give us psds_2, freqs_2 = psd_array_welch( x, float(n_fft), n_fft=n_fft, n_overlap=n_overlap) assert_allclose(freqs, freqs_2) assert_allclose(psds, psds_2)
def get_avg_band_power_single_channel(signal, fs): # PSD # calculate PSD using Welch's method. Although better approach is to use multitaper algorithm, # but firstly, it is much slower than Welch's method. Secondly, our data is already # cleaned and preprocessed, so both methods probably will give similar results psds, freqs = psd_array_welch(signal, sfreq=fs, n_per_seg=7, n_fft=np.shape(signal)[0]) plt.plot(freqs[1:np.shape(freqs)[0] - 1], psds[1:np.shape(psds)[0] - 1]) plt.show() # 1. find average band powers (for alpha, beta, gamma and theta bands) freq_bands = { # upper and lower limits of all the needed bands 'alpha': [8, 13], 'beta': [13, 30], 'gamma': [30, 40], 'theta': [4, 8] } freq_res = freqs[1] - freqs[0] # frequency resolution avg_power = [] # we have to store the avg band power somewhere... # calculate avg band power (absolute, not relative - we don't need percents) in each of the bands # The absolute delta power is equal to the area under the plot of already calculated PSD. # And as we know, this can be obtained by integrating. As we don't have any formula, which we can # integrate to obtain this area, we need to approximate it. As suggested in # https://raphaelvallat.com/bandpower.html, let's choose Simpson's method, which is relying on # decomposition of the area into several parabola and then summing up the area of these parabola. for band in ['alpha', 'beta', 'gamma', 'theta']: # maybe strange, but definitely readable xD # find indices, which satisfy the limits of the specific band idx = np.logical_and(freqs >= freq_bands[band][0], freqs <= freq_bands[band][1]) avg = simps(psds[idx], dx=freq_res) # integrals, sweet integrals <3 avg_power.append(avg) # success, save the computed value! return avg_power
def spectral_ratio_ssd(self, ssd_sources): """Spectral ratio measure for best n_components selection See Nikulin 2011, Eq. (24) Parameters ---------- ssd_sources : data projected on the SSD space. output of transform """ psd, freqs = psd_array_welch(ssd_sources, sfreq=self.sampling_freq, n_fft=self.n_fft) sig_idx = _time_mask(freqs, *self.freqs_signal) noise_idx = _time_mask(freqs, *self.freqs_noise) if psd.ndim == 3: spec_ratio = psd[:, :, sig_idx].mean(axis=2).mean( axis=0) / psd[:, :, noise_idx].mean(axis=2).mean(axis=0) else: spec_ratio = psd[:, sig_idx].mean(axis=1) / psd[:, noise_idx].mean( axis=1) sorter_spec = spec_ratio.argsort()[::-1] return spec_ratio, sorter_spec
def welch_p(X, sfreq, fmin, fmax, n_fft, n_overlap, n_per_seg): ''' Use welch method to estimate signal power spectral density Basic function is mne.psd_array_welch :param X: input data array (n_events, n_epochs, n_times) :param sfreq: the sampling frequency :param fmin, fmax: the lower(upper) frequency of interest :param n_fft: the length of FFT used, must be >= n_per_seg :param n_overlap: the number of points of overlap between segments :param n_per_seg: length of each welch segment, usually = n_fft :param psds: power spectral density array (n_events, n_epochs, n_freqs) :param freqs: frequencies used in psd analysis ''' num_freqs = int(math.floor((fmax - fmin) / (sfreq / n_fft)) + 1) psds = np.zeros((X.shape[0], X.shape[1], num_freqs)) freqs = np.zeros((X.shape[0], X.shape[1], num_freqs)) for i in range(X.shape[0]): for j in range(X.shape[1]): psds[i, j, :], freqs[i, j, :] = psd_array_welch(X[i, j, :], sfreq=sfreq, fmin=fmin, fmax=fmax, n_fft=n_fft, n_overlap=n_overlap, n_per_seg=n_per_seg) return psds, freqs
def eeg_power_band(epochs): """EEG relative power band feature extraction. This function takes an ``mne.Epochs`` object and creates EEG features based on relative power in specific frequency bands that are compatible with scikit-learn. Parameters ---------- epochs : Epochs The data. Returns ------- X : numpy array of shape [n_samples, 5] Transformed data. """ # specific frequency bands FREQ_BANDS = { "delta": [0.5, 4.5], "theta": [4.5, 8.5], "alpha": [8.5, 11.5], "sigma": [11.5, 15.5], "beta": [15.5, 30] } EEG_CHANNELS = ["EEG Fpz-Cz", "EEG Pz-Oz"] sfreq = epochs.info['sfreq'] data = epochs.load_data().pick_channels(EEG_CHANNELS).get_data() psds, freqs = psd_array_welch(data, sfreq, fmin=0.5, fmax=30., n_fft=512, n_overlap=256) # Normalize the PSDs psds /= np.sum(psds, axis=-1, keepdims=True) X = [] for _, (fmin, fmax) in FREQ_BANDS.items(): psds_band = psds[:, :, (freqs >= fmin) & (freqs < fmax)].mean(axis=-1) X.append(psds_band.reshape(len(psds), -1)) return np.concatenate(X, axis=1)
def prepare_psd(times, t_win, ch_group, meta, X, freq_band, info, psd_params): """ df.data is average PSD across ch_group and times between t_lo and t_hi psd is non-averaged PSD returned for plotting/inspection """ df, X = _drop_by_confidence(meta, X, 0, 30, 70, 100) df = add_condition(df, 30, 70) time_mask, ch_inds = _get_masks(times, t_win[0], t_win[1], ch_group, info) psd, freqs = psd_array_welch(X[:, ch_inds, time_mask], sfreq=info["sfreq"], **psd_params) freq_mask = np.logical_and(freqs > freq_band[0], freqs < freq_band[1]) df["data"] = psd[:, :, freq_mask].mean(axis=(1, 2)) return df, psd, freqs
def prepare_band_power(times, t_win, meta, X, freq_band, info, psd_params): """ df.data is average PSD across ch_group and times between t_lo and t_hi psd is non-averaged PSD returned for plotting/inspection """ df, X = _drop_by_confidence(meta, X, 0, 30, 70, 100) df = add_condition(df, 30, 70) time_mask, _ = _get_masks(times, t_win[0], t_win[1], "parietal", info) psd, freqs = psd_array_welch(X[:, :, time_mask.squeeze()], sfreq=info["sfreq"], **psd_params) # freq_mask = np.logical_and(freqs > freq_band[0], freqs < freq_band[1]) # power = ( # psd[:, :, freq_mask].mean(axis=2, keepdims=True).transpose((0, 2, 1)) # ) power = psd.transpose((0, 2, 1)) return df, power, freqs
def eeg_power_band(epochs): """EEG relative power band feature extraction. This function takes an ``mne.Epochs`` object and creates EEG features based on relative power in specific frequency bands that are compatible with scikit-learn. Parameters ---------- epochs : Epochs The data. Returns ------- X : numpy array of shape [n_samples, 5] Transformed data. """ # specific frequency bands FREQ_BANDS = {"delta": [0.5, 4.5], "theta": [4.5, 8.5], "alpha": [8.5, 11.5], "sigma": [11.5, 15.5], "beta": [15.5, 30]} EEG_CHANNELS = ["EEG Fpz-Cz", "EEG Pz-Oz"] sfreq = epochs.info['sfreq'] data = epochs.load_data().pick_channels(EEG_CHANNELS).get_data() psds, freqs = psd_array_welch(data, sfreq, fmin=0.5, fmax=30., n_fft=512, n_overlap=256) # Normalize the PSDs psds /= np.sum(psds, axis=-1, keepdims=True) X = [] for _, (fmin, fmax) in FREQ_BANDS.items(): psds_band = psds[:, :, (freqs >= fmin) & (freqs < fmax)].mean(axis=-1) X.append(psds_band.reshape(len(psds), -1)) return np.concatenate(X, axis=1)
def plot_extended(S, sfreq, f_scale, powers, ica_mne, sort=True, plot_env=True, number=False, h_freq_filt=2., save=None, t_min=1, y_lim=None, t_max=10, pdf_from_sources=True, extension='pdf'): n_plots = 10 n_s = S.shape[0] if sort is True: order = np.argsort(kurtosis(S, axis=1))[::-1] elif type(sort) is list: order = sort n_s = len(order) else: order = np.arange(n_s) if plot_env: n_col = 3 width_ratios = [1.5, 1, .7] figsize = (3, 8) else: n_col = 2 width_ratios = [1, .3] figsize = (3, 8) n_f = max(n_s // n_plots, 1) for i in range(n_f): f, ax = plt.subplots(n_plots, n_col, figsize=figsize, gridspec_kw=dict(width_ratios=width_ratios, top=0.95, bottom=0.11, left=0.11, right=0.9, hspace=0.18, wspace=0.04)) for j in range(n_plots): if i * n_plots + j >= n_s: break idx = order[i * n_plots + j] ax_idx = 0 axe = ax[j] if plot_env: s = S[idx] s *= np.sign(np.mean(s**3)) # amp = np.abs(hilbert(s)) # # to_plot = amp[100:-100] # to_plot = filter_data(to_plot, sfreq=sfreq, l_freq=None, # h_freq=h_freq_filt) n_s = 3000 idx_min = int(sfreq * t_min) idx_max = int(sfreq * t_max) slice_len = int((idx_max - idx_min) / n_s) + 1 to_plot = s[idx_min:idx_max:slice_len] # loc = np.argmax(to_plot) # n_points = 3000 # idx_loc = np.arange(max(0, loc - n_points), # min(len(to_plot), loc + n_points)) # to_plot = to_plot[idx_loc] n_s = len(to_plot) t = np.linspace(t_min, t_max, n_s) axe[0].plot(t, to_plot, linewidth=0.4, color='k') # axe[0].set_xlim([t_min, t_max]) axe[0].set_yticklabels([]) if j == n_plots - 1: times = np.arange(t_min, t_max + 1, dtype=int) axe[0].set_xticks(times) axe[0].set_xticklabels(['%d' % time for time in times]) axe[0].set_xlabel('time (sec.)') else: axe[0].set_xticklabels([]) axe[0].set_yticks([]) for axis in ['top', 'left', 'right']: axe[0].spines[axis].set_visible(False) ax_idx = 1 # axe[0].set_xlim([0, n_s]) freqs = np.arange(0, max(f_scale), 20, dtype=int) axe[ax_idx].set_xticks(freqs) if j == n_plots - 1: axe[ax_idx].set_xlabel('f (Hz)') axe[ax_idx].set_xticklabels(['%d' % freq for freq in freqs]) else: axe[ax_idx].set_xticklabels([]) if pdf_from_sources: s_ = s / np.sqrt(np.mean(s**2)) fmin = min(f_scale) fmax = max(f_scale) pows, f_ = psd_array_welch(s_, sfreq, fmin, fmax) print(min(pows), max(pows)) else: pows = powers[:, idx] f_ = f_scale axe[ax_idx].semilogy(f_, pows, color='k', linewidth=1) if y_lim is None: y_lim = np.min(powers), np.max(powers) axe[ax_idx].set_ylim(*y_lim) axe[ax_idx].grid() axe[ax_idx].set_yticklabels([]) axe[ax_idx].minorticks_off() ica_mne._ica_names[idx] = '' _plot_ica_topomap(ica_mne, idx=idx, axes=axe[ax_idx + 1], title='', sensors=False) if number is not False: if type(number) is np.ndarray: string = '%d, %.2f' % (i * n_plots + j, number[i * n_plots + j]) else: string = '%d' % (i * n_plots + j + 1) axe[0].set_ylabel(string, rotation=0) if j == 0: if plot_env: axe[0].set_title('Source') axe[ax_idx].set_title('Spectrum') axe[ax_idx + 1].set_title('Topo') if save is not None: plt.savefig('%s_%d.%s' % (save, i, extension)) plt.show()
def test_ssd(): """Test Common Spatial Patterns algorithm on raw data.""" X, A, S = simulate_data() sf = 250 n_channels = X.shape[0] info = create_info(ch_names=n_channels, sfreq=sf, ch_types='eeg') n_components_true = 5 # Init filt_params_signal = dict(l_freq=freqs_sig[0], h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) ssd = SSD(info, filt_params_signal, filt_params_noise) # freq no int freq = 'foo' filt_params_signal = dict(l_freq=freq, h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) with pytest.raises(TypeError, match='must be an instance '): ssd = SSD(info, filt_params_signal, filt_params_noise) # Wrongly specified noise band freq = 2 filt_params_signal = dict(l_freq=freq, h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) with pytest.raises(ValueError, match='Wrongly specified '): ssd = SSD(info, filt_params_signal, filt_params_noise) # filt param no dict filt_params_signal = freqs_sig filt_params_noise = freqs_noise with pytest.raises(ValueError, match='must be defined'): ssd = SSD(info, filt_params_signal, filt_params_noise) # Data type filt_params_signal = dict(l_freq=freqs_sig[0], h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) ssd = SSD(info, filt_params_signal, filt_params_noise) raw = io.RawArray(X, info) pytest.raises(TypeError, ssd.fit, raw) # More than 1 channel type ch_types = np.reshape([['mag'] * 10, ['eeg'] * 10], n_channels) info_2 = create_info(ch_names=n_channels, sfreq=sf, ch_types=ch_types) with pytest.raises(ValueError, match='At this point SSD'): ssd = SSD(info_2, filt_params_signal, filt_params_noise) # Number of channels info_3 = create_info(ch_names=n_channels + 1, sfreq=sf, ch_types='eeg') ssd = SSD(info_3, filt_params_signal, filt_params_noise) pytest.raises(ValueError, ssd.fit, X) # Fit n_components = 10 ssd = SSD(info, filt_params_signal, filt_params_noise, n_components=n_components) # Call transform before fit pytest.raises(AttributeError, ssd.transform, X) # Check outputs ssd.fit(X) assert (ssd.filters_.shape == (n_channels, n_channels)) assert (ssd.patterns_.shape == (n_channels, n_channels)) # Transform X_ssd = ssd.fit_transform(X) assert (X_ssd.shape[0] == n_components) # back and forward ssd = SSD(info, filt_params_signal, filt_params_noise, n_components=None, sort_by_spectral_ratio=False) ssd.fit(X) X_denoised = ssd.inverse_transform(X) assert_array_almost_equal(X_denoised, X) # Power ratio ordering spec_ratio, _ = ssd.get_spectral_ratio(ssd.transform(X)) # since we now that the number of true components is 5, the relative # difference should be low for the first 5 components and then increases index_diff = np.argmax(-np.diff(spec_ratio)) assert index_diff == n_components_true - 1 # Check detected peaks # fit ssd n_components = n_components_true filt_params_signal = dict(l_freq=freqs_sig[0], h_freq=freqs_sig[1], l_trans_bandwidth=1, h_trans_bandwidth=1) filt_params_noise = dict(l_freq=freqs_noise[0], h_freq=freqs_noise[1], l_trans_bandwidth=1, h_trans_bandwidth=1) ssd = SSD(info, filt_params_signal, filt_params_noise, n_components=n_components, sort_by_spectral_ratio=False) ssd.fit(X) out = ssd.transform(X) psd_out, _ = psd_array_welch(out[0], sfreq=250, n_fft=250) psd_S, _ = psd_array_welch(S[0], sfreq=250, n_fft=250) corr = np.abs(np.corrcoef((psd_out, psd_S))[0, 1]) assert np.abs(corr) > 0.95 # Check pattern estimation # Since there is no exact ordering of the recovered patterns # a pair-wise greedy search will be done error = list() for ii in range(n_channels): corr = np.abs(np.corrcoef(ssd.patterns_[ii, :].T, A[:, 0])[0, 1]) error.append(1 - corr) min_err = np.min(error) assert min_err < 0.3 # threshold taken from SSD original paper
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))
def generate_welch(data, fmin=1, fmax=70, fs=250, n_jobs=1): psds, freqs = psd_array_welch(data, fs, fmin, fmax, n_per_seg=int(fs/2), n_overlap=int(fs/4), n_jobs=n_jobs) return np.expand_dims(psds, 1)
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) psds, freqs = func(raw, proj=False, **kws) 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) # -- 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)))
time_window = (0.4, 0.8) ch_group = "parietal" ch_type = "grad" freq_band = (4, 8) baseline = (-0.2, 0) X, meta, times, info = assemble_epochs_new(ch_type=ch_type, baseline=baseline) df, data = prepare_erp(times, time_window, ch_group, meta, X, info) psd_params = dict(fmin=0, fmax=30, n_fft=128) time_mask = np.logical_and(times >= time_window[0], times < time_window[1]) psd, freqs = psd_array_welch(data[:, :, time_mask], sfreq=info["sfreq"], **psd_params) freq_mask = np.logical_and(freqs >= freq_band[0], freqs < freq_band[1]) df.data = psd[:, :, freq_mask].mean(axis=(1, 2)) * 1e13**2 md = smf.mixedlm("data ~ cond", data=df, groups="subj") mdf = md.fit() print(mdf.summary()) legend = [] for cond in np.unique(df.cond): plt.plot(freqs, psd[df.cond == cond, :, :].mean(axis=(0, 1))) legend.append(cond) plt.legend(legend) plt.show()
def _estimate_line_freq(raw, verbose=False): """Estimate power line noise from a given BaseRaw. Uses 5 channels of either meg, eeg, ecog, or seeg to estimate the line frequency. Parameters ---------- raw : mne.io.BaseRaw Returns ------- line_freq : int | None Either 50, or 60 Hz depending if European, or USA data recording. """ sfreq = raw.info['sfreq'] # if sampling is not high enough, line_freq does not matter if sfreq < 100: return None # setup picks of the data to get at least 5 channels pick_dict = {"meg": True} picks = list(pick_types(raw.info, exclude='bads', **pick_dict)) if len(picks) < 5: pick_dict = {"eeg": True} picks = pick_types(raw.info, exclude='bads', **pick_dict) if len(picks) < 5: pick_dict = {"ecog": True} picks = pick_types(raw.info, exclude='bads', **pick_dict) if len(picks) < 5: pick_dict = {"seeg": True} picks = pick_types(raw.info, exclude='bads', **pick_dict) if len(picks) < 5: warn("Estimation of line frequency only " "supports 'meg', 'eeg', 'ecog', or 'seeg'.") return None # only sample first 10 seconds, or whole time series tmin = 0 tmax = int(min(len(raw.times), 10 * sfreq)) # get just five channels of data to estimate on data = raw.get_data(start=tmin, stop=tmax, picks=picks, return_times=False)[0:5, :] # run a multi-taper FFT between Power Line Frequencies of interest psds, freqs = psd_array_welch(data, fmin=49, fmax=61, sfreq=sfreq, average="mean") usa_ind = np.where(freqs == min(freqs, key=lambda x: abs(x - 60)))[0] eu_ind = np.where(freqs == min(freqs, key=lambda x: abs(x - 50)))[0] # get the average power within those frequency bands usa_psd = np.mean((psds[..., usa_ind])) eu_psd = np.mean((psds[..., eu_ind])) if verbose is True: print("EU (i.e. 50 Hz) PSD is {} and " "USA (i.e. 60 Hz) PSD is {}".format(eu_psd, usa_psd)) if usa_psd > eu_psd: line_freq = 60 else: line_freq = 50 return line_freq
def plot_mean_spectrum(self, channel_desc, fmin=2, fmax=90, plot_hits=1, plot_cr=1, plot_omissions=0, plot_fa=0): channel_desc = np.array(channel_desc) if channel_desc.dtype.char == 'U': channel_num = np.atleast_1d( self.channel_info.get_channel_pos(channel_desc)) else: channel_num = np.atleast_1d(channel_desc) def plot_mean_spectrum_subplot(freqs, psds, color, ax, plot_ci=1): psd_mean, psd_std = (10 * np.log10(psds)).mean(0), ( 10 * np.log10(psds)).std(0) ci95_up_pre = psd_mean - 2 * psd_std / psds.shape[0] ci95_down_pre = psd_mean + 2 * psd_std / psds.shape[0] ax.plot(freqs, psd_mean, c=color) if plot_ci: ax.fill_between(freqs, ci95_up_pre, ci95_down_pre, color=color, alpha=0.4) for chan_num_i in channel_num: data_i = self.data[chan_num_i, :, :] nfft = min(self.n_pnts, 2048) f = plt.figure() ax = f.add_subplot(111) legend_str = [] if plot_hits: psds_i, freqs = psd_array_welch(data_i[:, self.hits].T, self.srate, fmin=fmin, fmax=fmax, n_fft=nfft) plot_mean_spectrum_subplot(freqs, psds_i, self.colors_dict['hits'], ax) legend_str.append('Hits') if plot_cr: psds_i, freqs = psd_array_welch(data_i[:, self.correct_rejects].T, self.srate, fmin=fmin, fmax=fmax, n_fft=nfft) plot_mean_spectrum_subplot(freqs, psds_i, self.colors_dict['cr'], ax) legend_str.append('Correct Rejects') if plot_omissions: psds_i, freqs = psd_array_welch(data_i[:, self.omissions].T, self.srate, fmin=fmin, fmax=fmax, n_fft=nfft) plot_mean_spectrum_subplot(freqs, psds_i, self.colors_dict['omissions'], ax) legend_str.append('Omissions') if plot_fa: psds_i, freqs = psd_array_welch(data_i[:, self.false_alarms].T, self.srate, fmin=fmin, fmax=fmax, n_fft=nfft) plot_mean_spectrum_subplot(freqs, psds_i, self.colors_dict['fa'], ax) legend_str.append('False Alarms') chan_name_i = self.channel_info.get_channel_name(chan_num_i) ax.legend(legend_str) ax.autoscale(axis='both', tight=True) ax.set( xlabel='Frequency (Hz)', ylabel='Gain (dB)', title='Mean Power Spectral Density - {}'.format(chan_name_i))