def test_bandpass(fc_sigerr): """Test bandpass filter.""" fc = fc_sigerr[0] sig_err = fc_sigerr[1] # Load signal signal = np.load(DATA_PATH + 'sim_bursting.npy') if sig_err: signal = signal[:5] Fs = 1000 # Test output same length as input N_seconds = 0.5 signal_filt = filt.bandpass_filter(signal, Fs, fc, N_seconds=N_seconds) assert len(signal) == len(signal_filt) # Test edge artifacts removed appropriately N_samples_filter = int(np.ceil(Fs * N_seconds)) if N_samples_filter % 2 == 0: N_samples_filter = int(N_samples_filter + 1) N_samples_NaN = int(np.ceil(N_samples_filter / 2)) assert np.all(np.isnan(signal_filt[:N_samples_NaN])) assert np.all(np.isnan(signal_filt[-N_samples_NaN:])) assert np.all( np.logical_not(np.isnan(signal_filt[N_samples_NaN:-N_samples_NaN]))) # Test edge artifacts are not removed if desired signal_filt = filt.bandpass_filter(signal, Fs, fc, N_seconds=N_seconds, remove_edge_artifacts=False, plot_frequency_response=True, print_transition_band=True) assert np.all(np.logical_not(np.isnan(signal_filt))) # Test returns kernel and signal out = filt.bandpass_filter(signal, Fs, fc, N_seconds=N_seconds, return_kernel=True) assert len(out) == 2 # Test same result if N_cycle and N_seconds used filt1 = filt.bandpass_filter(signal, Fs, fc, N_seconds=1, remove_edge_artifacts=False) filt2 = filt.bandpass_filter(signal, Fs, fc, N_cycles=8, remove_edge_artifacts=False) np.testing.assert_allclose(filt1, filt2)
def test_amp(): """Test phase time series functionality""" # Load signal signal = np.load(data_path + 'sim_bursting.npy') Fs = 1000 # Sampling rate f_range = (6, 14) # Frequency range # Test output same length as input amp = filt.amp_by_time(signal, Fs, f_range, filter_kwargs={'N_seconds': .5}) assert len(signal) == len(amp) # Test results are the same if add NaNs to the side signal_nan = np.pad(signal, 10, mode='constant', constant_values=(np.nan,)) amp_nan = filt.amp_by_time(signal_nan, Fs, f_range, filter_kwargs={'N_seconds': .5}) np.testing.assert_allclose(amp_nan[10:-10], amp) # Test NaN is in same places as filtered signal signal_filt = filt.bandpass_filter(signal, Fs, (6, 14), N_seconds=.5) assert np.all(np.logical_not( np.logical_xor(np.isnan(amp), np.isnan(signal_filt)))) # Test works fine if input signal already has NaN signal_low = filt.lowpass_filter(signal, Fs, 30, N_seconds=.3) amp = filt.amp_by_time(signal_low, Fs, f_range, filter_kwargs={'N_seconds': .5}) assert len(signal) == len(amp) # Test option to not remove edge artifacts amp = filt.amp_by_time(signal, Fs, f_range, filter_kwargs={'N_seconds': .5}, remove_edge_artifacts=False) assert np.all(np.logical_not(np.isnan(amp)))
def test_lowpass(): """Test lowpass filter functionality""" # Load signal signal = np.load(data_path + 'sim_bursting.npy') Fs = 1000 # Sampling rate # Test output same length as input N_seconds = 0.5 signal_filt = filt.lowpass_filter(signal, Fs, 30, N_seconds=N_seconds) assert len(signal) == len(signal_filt) # Test edge artifacts removed appropriately N_samples_filter = int(np.ceil(Fs * N_seconds)) if N_samples_filter % 2 == 0: N_samples_filter = int(N_samples_filter + 1) N_samples_NaN = int(np.ceil(N_samples_filter / 2)) assert np.all(np.isnan(signal_filt[:N_samples_NaN])) assert np.all(np.isnan(signal_filt[-N_samples_NaN:])) assert np.all(np.logical_not(np.isnan( signal_filt[N_samples_NaN:-N_samples_NaN]))) # Test edge artifacts are not removed if desired signal_filt = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=N_seconds, remove_edge_artifacts=False) assert np.all(np.logical_not(np.isnan(signal_filt)))
def test_lowpass(): """Test lowpass filter.""" # Load signal signal = np.load(DATA_PATH + 'sim_bursting.npy') Fs = 1000 # Test output same length as input N_seconds = 0.5 signal_filt, _ = filt.lowpass_filter(signal, Fs, 30, return_kernel=True) signal_filt = filt.lowpass_filter(signal, Fs, 30, N_seconds=N_seconds, plot_frequency_response=True) assert len(signal) == len(signal_filt) # Test edge artifacts removed appropriately N_samples_filter = int(np.ceil(Fs * N_seconds)) if N_samples_filter % 2 == 0: N_samples_filter = int(N_samples_filter + 1) N_samples_NaN = int(np.ceil(N_samples_filter / 2)) assert np.all(np.isnan(signal_filt[:N_samples_NaN])) assert np.all(np.isnan(signal_filt[-N_samples_NaN:])) assert np.all( np.logical_not(np.isnan(signal_filt[N_samples_NaN:-N_samples_NaN]))) # Test edge artifacts are not removed if desired signal_filt = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=N_seconds, remove_edge_artifacts=False) assert np.all(np.logical_not(np.isnan(signal_filt)))
def test_bandpass(): """Test bandpass filter functionality""" # Load signal signal = np.load(data_path + 'sim_bursting.npy') Fs = 1000 # Sampling rate # Test output same length as input N_seconds = 0.5 signal_filt = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=N_seconds) assert len(signal) == len(signal_filt) # Test edge artifacts removed appropriately N_samples_filter = int(np.ceil(Fs * N_seconds)) if N_samples_filter % 2 == 0: N_samples_filter = int(N_samples_filter + 1) N_samples_NaN = int(np.ceil(N_samples_filter / 2)) assert np.all(np.isnan(signal_filt[:N_samples_NaN])) assert np.all(np.isnan(signal_filt[-N_samples_NaN:])) assert np.all(np.logical_not(np.isnan( signal_filt[N_samples_NaN:-N_samples_NaN]))) # Test edge artifacts are not removed if desired signal_filt = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=N_seconds, remove_edge_artifacts=False) assert np.all(np.logical_not(np.isnan(signal_filt))) # Test returns kernel and signal out = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=N_seconds, return_kernel=True) assert len(out) == 2 # Test same result if N_cycle and N_seconds used filt1 = filt.bandpass_filter(signal, Fs, (8, 12), N_seconds=1, remove_edge_artifacts=False) filt2 = filt.bandpass_filter(signal, Fs, (8, 12), N_cycles=8, remove_edge_artifacts=False) np.testing.assert_allclose(filt1, filt2)
def test_phase(): """Test phase time series.""" # Load signal signal = np.load(DATA_PATH + 'sim_bursting.npy') Fs = 1000 f_range = (6, 14) # Test output same length as input pha = filt.phase_by_time(signal, Fs, f_range, hilbert_increase_N=True) pha = filt.phase_by_time(signal, Fs, f_range, filter_kwargs={'N_seconds': .5}) assert len(signal) == len(pha) # Test results are the same if add NaNs to the side signal_nan = np.pad(signal, 10, mode='constant', constant_values=(np.nan, )) pha_nan = filt.phase_by_time(signal_nan, Fs, f_range, filter_kwargs={'N_seconds': .5}) np.testing.assert_allclose(pha_nan[10:-10], pha) # Test NaN is in same places as filtered signal signal_filt = filt.bandpass_filter(signal, Fs, (6, 14), N_seconds=.5) assert np.all( np.logical_not(np.logical_xor(np.isnan(pha), np.isnan(signal_filt)))) # Test works fine if input signal already has NaN signal_low = filt.lowpass_filter(signal, Fs, 30, N_seconds=.3) pha = filt.phase_by_time(signal_low, Fs, f_range, filter_kwargs={'N_seconds': .5}) assert len(signal) == len(pha) # Test option to not remove edge artifacts pha = filt.phase_by_time(signal, Fs, f_range, filter_kwargs={'N_seconds': .5}, remove_edge_artifacts=False) assert np.all(np.logical_not(np.isnan(pha)))
tlim = (2, 5) tidx = np.logical_and(t>=tlim[0], t<tlim[1]) plt.figure(figsize=(12, 2)) plt.plot(t[tidx], signal[tidx], '.5') plt.plot(t[tidx], signal_low[tidx], 'k') plt.xlim(tlim) #%% from bycycle.filt import bandpass_filter # for attenuation problems bandpass_filter(signal, Fs, (4, 10), N_seconds=.75, plot_frequency_response=True) N_seconds = [0.1, 0.2, 0.5, 1, 2] for jj in range(len(N_seconds)): # for every channel with pac for ii in range(10,15): # for every channel that has peaks if ~np.isnan(features_df['CF'][ii]): # define phase providing band CF = features_df['CF'][ii] BW = features_df['BW'][ii]
def find_extrema(x, Fs, f_range, boundary=None, first_extrema='peak', filter_kwargs=None): """ Identify peaks and troughs in a time series. Parameters ---------- x : 1d array voltage time series Fs : float sampling rate, Hz f_range : tuple of (float, float) frequency range (Hz) for narrowband signal of interest, used to find zerocrossings of the oscillation boundary : int number of samples from edge of recording to ignore first_extrema: {'peak', 'trough', None} if 'peak', then force the output to begin with a peak and end in a trough if 'trough', then force the output to begin with a trough and end in peak if None, force nothing filter_kwargs : dict keyword arguments to the filt.bandpass_filter(), such as 'N_cycles' or 'N_seconds' to control filter length Returns ------- Ps : 1d array indices at which oscillatory peaks occur in the input signal x Ts : 1d array indices at which oscillatory troughs occur in the input signal x Notes ----- This function assures that there are the same number of peaks and troughs if the first extrema is forced to be either peak or trough. """ # Set default filtering parameters if filter_kwargs is None: filter_kwargs = {} # Default boundary value as 1 cycle length of low cutoff frequency if boundary is None: boundary = int(np.ceil(Fs / float(f_range[0]))) # Narrowband filter signal x_filt = bandpass_filter(x, Fs, f_range, remove_edge_artifacts=False, **filter_kwargs) # Find rising and falling zerocrossings (narrowband) zeroriseN = _fzerorise(x_filt) zerofallN = _fzerofall(x_filt) # Compute number of peaks and troughs if zeroriseN[-1] > zerofallN[-1]: P = len(zeroriseN) - 1 T = len(zerofallN) else: P = len(zeroriseN) T = len(zerofallN) - 1 # Calculate peak samples Ps = np.zeros(P, dtype=int) for p in range(P): # Calculate the sample range between the most recent zero rise # and the next zero fall mrzerorise = zeroriseN[p] nfzerofall = zerofallN[zerofallN > mrzerorise][0] # Identify time fo peak Ps[p] = np.argmax(x[mrzerorise:nfzerofall]) + mrzerorise # Calculate trough samples Ts = np.zeros(T, dtype=int) for tr in range(T): # Calculate the sample range between the most recent zero fall # and the next zero rise mrzerofall = zerofallN[tr] nfzerorise = zeroriseN[zeroriseN > mrzerofall][0] # Identify time of trough Ts[tr] = np.argmin(x[mrzerofall:nfzerorise]) + mrzerofall # Remove peaks and troughs within the boundary limit Ps = Ps[np.logical_and(Ps > boundary, Ps < len(x) - boundary)] Ts = Ts[np.logical_and(Ts > boundary, Ts < len(x) - boundary)] # Force the first extrema to be as desired # Assure equal # of peaks and troughs if first_extrema == 'peak': if Ps[0] > Ts[0]: Ts = Ts[1:] if Ps[-1] > Ts[-1]: Ps = Ps[:-1] elif first_extrema == 'trough': if Ts[0] > Ps[0]: Ps = Ps[1:] if Ts[-1] > Ps[-1]: Ts = Ts[:-1] elif first_extrema is None: pass else: raise ValueError('Parameter "first_extrema" is invalid') return Ps, Ts
# ----------------------------- # # In order to characterize the oscillation, it is useful to know the precise times of peaks and # troughs. For one, this will allow us to compute the periods and rise-decay symmetries of the # individual cycles. To do this, the signal is first narrow-bandpass filtered in order to estimate # "zero-crossings." Then, in between these zerocrossings, the absolute maxima and minima are found # and labeled as the peaks and troughs, respectively. from bycycle.filt import bandpass_filter from bycycle.cyclepoints import _fzerorise, _fzerofall, find_extrema # Narrowband filter signal N_seconds_theta = .75 signal_narrow = bandpass_filter(signal, Fs, f_theta, remove_edge_artifacts=False, N_seconds=N_seconds_theta) # Find rising and falling zerocrossings (narrowband) zeroriseN = _fzerorise(signal_narrow) zerofallN = _fzerofall(signal_narrow) #################################################################################################### # Find peaks and troughs (this function also does the above) Ps, Ts = find_extrema(signal_low, Fs, f_theta, filter_kwargs={'N_seconds': N_seconds_theta})
# plt.show() plt.plot(t[tidx], signal_low[tidx], 'k') plt.xlim(tlim) plt.title('lowpass 100 Hz signal - trial 1') plt.show() #localizing peaks and troughs from bycycle.filt import bandpass_filter from bycycle.cyclepoints import _fzerorise, _fzerofall, find_extrema # Narrowband filter signal N_seconds_theta = 0.02 signal_narrow = bandpass_filter(signal, Fs, f_theta, remove_edge_artifacts=False, N_seconds=N_seconds_theta) # plt.plot(t[tidx], signal[tidx], '.5') # plt.plot(t[tidx], signal_narrow[tidx], 'k') # plt.xlim(tlim) # plt.title('bandpass compared to raw') # plt.show() #Find rising and falling zerocrossings (narrowband) zeroriseN = _fzerorise(signal_narrow) zerofallN = _fzerofall(signal_narrow) Ps, Ts = find_extrema(signal_low, Fs,
# --------------------------------------------------------------- import numpy as np import scipy as sp from scipy import signal as spsignal import matplotlib.pyplot as plt from bycycle.filt import amp_by_time, phase_by_time, bandpass_filter from bycycle.sim import sim_noisy_bursty_oscillator signal = np.load('data/sim_bursting_more_noise.npy') Fs = 1000 # Sampling rate f_alpha = (8, 12) N_seconds_filter = .5 # Compute amplitude and phase signal_filt = bandpass_filter(signal, Fs, f_alpha, N_seconds=N_seconds_filter) theta_amp = amp_by_time(signal, Fs, f_alpha, filter_kwargs={'N_seconds': N_seconds_filter}) theta_phase = phase_by_time(signal, Fs, f_alpha, filter_kwargs={'N_seconds': N_seconds_filter}) # Plots signal t = np.arange(0, len(signal) / Fs, 1 / Fs) tlim = (2, 6) tidx = np.logical_and(t >= tlim[0], t < tlim[1]) plt.figure(figsize=(12, 6))