def test_fft_spectrum_04(): f, t, Sxx = _spectral_helper(x, x, fs=s_freq, window='hann', nperseg=x.shape[0], noverlap=0, nfft=None, return_onesided=False, mode='psd', scaling='spectrum') f0, Sxx0 = _fft(x, s_freq, detrend=None, taper='hann', scaling='energy', sides='two') assert_array_equal(f0, f) assert_array_almost_equal(Sxx0, Sxx[:, 0] * CORRECTION_FACTOR)
def test_fft_spectrum_01(): f, t, Sxx = _spectral_helper(x, x, fs=s_freq, window='hann', nperseg=x.shape[0], noverlap=0, nfft=None, return_onesided=True, mode='psd', scaling='density') f0, Sxx0 = _fft(x, s_freq, detrend=None, taper='hann', scaling='power', sides='one') assert_array_equal(f0, f) assert_array_almost_equal(Sxx0, Sxx[:, 0])
def test_fft_spectrum_06(): f, t, Sxx = _spectral_helper(x, x, fs=s_freq, window='hann', nperseg=x.shape[0], noverlap=0, nfft=None, return_onesided=False, mode='stft', scaling='spectrum') f0, Sxx0 = _fft(x, s_freq, detrend=None, taper='hann', scaling='energy', output='complex', sides='two') assert_array_equal(f0, f) # in scipy, the extra dim is time, in wonambi it's the taper assert_array_almost_equal(Sxx0[:, 0], Sxx[:, 0] * sqrt(CORRECTION_FACTOR))
def test_fft_spectrum_02(): """Scipy does not correct the energy with the spectrum scaling when windowing.""" f, t, Sxx = _spectral_helper(x, x, fs=s_freq, window='hann', nperseg=x.shape[0], noverlap=0, nfft=None, return_onesided=True, mode='psd', scaling='spectrum') f0, Sxx0 = _fft(x, s_freq, detrend=None, taper='hann', scaling='energy', sides='one') assert_array_equal(f0, f) assert_array_almost_equal(Sxx0, Sxx[:, 0] * CORRECTION_FACTOR)
def bandpower(data, bands, epoch_size, epoch_overlap, fs=None, scaling='density', relative=False): # Note: add to doc that epoch_size and epoch_overlap is in seconds # also, # TODO: add on documentation of estimate_rate that the output is in Hz if isinstance(data, (pd.DataFrame, pd.Series)) and isinstance(data.index, (pd.TimedeltaIndex, pd.DatetimeIndex)): datetime_index = True fs = fs or int(estimate_rate(data)) if isinstance(data, pd.DataFrame): columns = data.columns x = np.asarray(data.values) else: columns = [data.name] x = np.asarray(data.values)[:, np.newaxis] else: datetime_index = False x = np.asarray(data) if x.ndim not in (1, 2): raise ValueError('bandpower only supports 1- or 2-dimensional data') elif x.ndim == 1: x = x[:, np.newaxis] columns = [f'x{i+1}' for i in range(x.shape[1])] fs = fs or 1 logger.debug('Calculating %s spectra with fs=%dHz on data shaped as %s', 'relative' if relative else 'absolute', fs, data.shape) x = x.T nsamples = x.shape[1] nperseg = int(epoch_size * fs) noverlap = int(epoch_overlap * fs) if nperseg > nsamples: raise ValueError('Epoch size is larger than data') # To whom it may concern: according to the _spectral_helper code, the # difference between scaling='density' and 'spectrum' is just how the # window adjusts the final value. One uses the sum of squared values, the # other the square of the sum. The 'density' also divides by the fs (which # is why the units change to V^2 / Hz). # In my opinion, this is not very important as long as we are consistent freqs, t, psd = _spectral_helper(x, x, fs=fs, window='hann', nperseg=nperseg, noverlap=noverlap, detrend='constant', return_onesided=True, scaling=scaling, mode='psd') # TODO: psd or stft ??? # Manage index if datetime_index: index = data.index[0] + pd.to_timedelta(t, unit='s') else: # index = pd.Index((t * (nperseg - noverlap)).astype(int), name='sample') n = index.shape[0] powers = [] rel_suffix = '_rel' if relative else '_abs' for name, (f_start, f_stop) in bands.items(): f_start = f_start or 0 f_stop = f_stop or fs / 2 idx = (freqs >= f_start) & (freqs < f_stop) if idx.sum() == 0: logger.warning('Band %s is empty for fs=%d and nperseg=%d', name, fs, nperseg) result = pd.DataFrame(data=[[np.nan] * len(columns)] * n, columns=columns, index=index) else: logger.debug('Calculating band power for %s with %d bins', name, idx.sum()) if idx.sum() <= 1: logger.warning('Band power for %s will be zero because there ' 'are not enough frequency points to calculate an ' 'integral', name) # TODO: we should manage the 1-bin case, but how ? # pxx axes: (column, freq, time) # bp = psd[:, idx, :].sum(axis=1) bp = simps(psd[:, idx, :], freqs[idx], axis=1) if relative: bp /= simps(psd, freqs, axis=1) # power_sum axes: (column, time) result = pd.DataFrame(data=bp.T, # (time, column) columns=columns, index=index) powers.append(result.add_suffix(f'_{name}{rel_suffix}')) return pd.concat(powers, axis='columns')
def calc_csd(self): """ Calculate the cross spectral density using Scipy csd function. csd utilizes Welch's method to estimate spectral density. Data is split into overlapping segments. Each segment is windowed, then the cross spectral density is calculated using Fourier transforms. The results from all windows are averaged together to produce a lower variance estimate of the spectral density. A segment overlap factor of 2 is used (50% overlap). A one-sided spectrum is returned for real inputs The cross spectral density (units V**2/Hz) is calculated, as opposed to the cross spectrum (units V**2). csd is a 2D array containing the cross spectral density. Axis 0 is the frequency axis and axis 1 is the time axis. Entries in the times array are the center values for each time bin. """ # If the number of points per segement is not specified, calculate the # number that gives approximately equal time and frequency resolution. if self.nperseg is None: self.nperseg = int(np.sqrt(2 * self.numpnts)) # Use next power of 2 for nperseg if specified. FFT algorithm is most # efficient when nperseg is a power of 2. if self.forcepower2 is True: self.nperseg = np.power(2, int(np.log2(self.nperseg - 1)) + 1) # Calculate cross spectral density self.freqs, self.times, self.csd = _spectral_helper( self.signal1, self.signal2, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd') # Calculate auto spectral density of signal 1 _, _, self.asd1 = _spectral_helper(self.signal1, self.signal1, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd') # Calculate auto spectral density of signal 2 _, _, self.asd2 = _spectral_helper(self.signal2, self.signal2, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd') # Shift time bins to correspond to original data window self.times += (self.signal1time[0] + self.signal2time[0]) / 2 # Remove bins with sawtooth crashes if self.sawteethtimes is not None: self.remove_sawteeth() # Record number of bins (aka # segments or # realizations) in the ffts self.numbins = np.shape(self.csd)[-1] # Calculate time bin averaged spectral densities self.csd_binavg = np.mean(self.csd, axis=-1) self.asd1_binavg = np.mean(self.asd1, axis=-1) self.asd2_binavg = np.mean(self.asd2, axis=-1) # Convert frequency units from Hz to kHz self.freqs /= 1000
def calc_csd(self): """ Calculate the cross spectral density using Scipy csd function. csd utilizes Welch's method to estimate spectral density. Data is split into overlapping segments. Each segment is windowed, then the cross spectral density is calculated using Fourier transforms. The results from all windows are averaged together to produce a lower variance estimate of the spectral density. A segment overlap factor of 2 is used (50% overlap). A one-sided spectrum is returned for real inputs The cross spectral density (units V**2/Hz) is calculated, as opposed to the cross spectrum (units V**2). csd is a 2D array containing the cross spectral density. Axis 0 is the frequency axis and axis 1 is the time axis. Entries in the times array are the center values for each time bin. """ # If the number of points per segement is not specified, calculate the # number that gives approximately equal time and frequency resolution. if self.nperseg is None: self.nperseg = int(np.sqrt(2 * self.numpnts)) # Use next power of 2 for nperseg if specified. FFT algorithm is most # efficient when nperseg is a power of 2. if self.forcepower2 is True: self.nperseg = np.power(2, int(np.log2(self.nperseg - 1)) + 1) # Calculate cross spectral density self.freqs, self.times, self.csd = _spectral_helper( self.signal1, self.signal2, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd' ) # Calculate auto spectral density of signal 1 _, _, self.asd1 = _spectral_helper( self.signal1, self.signal1, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd' ) # Calculate auto spectral density of signal 2 _, _, self.asd2 = _spectral_helper( self.signal2, self.signal2, fs=self.fSample, window=self.window, nperseg=self.nperseg, detrend=self.detrend, scaling='density', mode='psd' ) # Shift time bins to correspond to original data window self.times += (self.signal1time[0] + self.signal2time[0]) / 2 # Remove bins with sawtooth crashes if self.sawteethtimes is not None: self.remove_sawteeth() # Record number of bins (aka # segments or # realizations) in the ffts self.numbins = np.shape(self.csd)[-1] # Calculate time bin averaged spectral densities self.csd_binavg = np.mean(self.csd, axis=-1) self.asd1_binavg = np.mean(self.asd1, axis=-1) self.asd2_binavg = np.mean(self.asd2, axis=-1) # Convert frequency units from Hz to kHz self.freqs /= 1000
# plt.colorbar() # plt.get_current_fig_manager().window.raise_() # plt.show() plt.savefig('{}/{} spectragram {}.png'.format(dir_temp_fig, filename_common, data_neuro['signal_info'][j]['name'])) plt.close() plt.ion() # ========== # coherence from scipy.signal import spectral ch_x=05-1 ch_y=38-1 [spcg_f,spcg_t,spcg_xy] = spectral._spectral_helper(data_neuro['data'][:,:,ch_x], data_neuro['data'][:,:,ch_y], window=signal.hann(128), nperseg=128, nfft=256,fs=data_neuro['signal_info'][0][2], axis=1, noverlap=96) [_,_,spcg_xx] = spectral._spectral_helper(data_neuro['data'][:,:,ch_x], data_neuro['data'][:,:,ch_x], window=signal.hann(128), nperseg=128, nfft=256,fs=data_neuro['signal_info'][0][2], axis=1, noverlap=96) [_,_,spcg_yy] = spectral._spectral_helper(data_neuro['data'][:,:,ch_y], data_neuro['data'][:,:,ch_y], window=signal.hann(128), nperseg=128, nfft=256,fs=data_neuro['signal_info'][0][2], axis=1, noverlap=96) spcg_t = np.array(spcg_t) + np.array( data_neuro['ts'][0] ) data_neuro = signal_align.neuro_sort(data_df, ['stim_familiarized', 'mask_opacity_int'], [], data_neuro ) data_neuro_full = data_neuro if False: data_neuro = signal_align.neuro_sort(data_df, ['stim_names'], np.logical_and(data_df['mask_opacity_int']==0, data_df['stim_familiarized']==0), data_neuro ) indx_used = data_neuro['cdtn_indx'][temp['cdtn'][0]] [spcg_f,spcg_t,spcg_xy] = spectral._spectral_helper(data_neuro['data'][indx_used,:,ch_x], data_neuro['data'][indx_used,:,ch_y], window=signal.hann(128), nperseg=128, nfft=256,fs=data_neuro['signal_info'][0][2], axis=1, noverlap=96) # by condition plt.figure(figsize=(16,9)) spcg_cdtn = []