def filterEEG(eeg_data, fs=250, f_range=(1, 50)): sig_filt = filt.filter_signal(eeg_data, fs, 'bandpass', f_range, filter_type='iir', butterworth_order=2) test_sig_filt = filt.filter_signal(sig_filt, fs, 'bandstop', (58, 62), n_seconds=1) num_nans = sum(np.isnan(test_sig_filt)) sig_filt = np.concatenate(([0]*(num_nans // 2), sig_filt, [0]*(num_nans // 2))) sig_filt = filt.filter_signal(sig_filt, fs, 'bandstop', (58, 62), n_seconds=1) sig_filt = sig_filt[~np.isnan(sig_filt)] return sig_filt
def get_bandpass_filter_signal(data, fs, f_range, passtype='bandpass'): sig_filt = filt.filter_signal(data, fs, passtype, f_range, remove_edges=False) return sig_filt
def bandpass_bandstop_filter(data,fs=250, lowcut=1, highcut=50, order = 2): nyq = 0.5 * fs low = lowcut / nyq high = highcut / nyq sos = butter(order, [low, high], analog = False, btype = 'band', output = 'sos') filted_data = sosfiltfilt(sos, data) filted_data = filt.filter_signal(filted_data, fs, 'bandstop', (58, 62), n_seconds=1) filted_data = filted_data[~np.isnan(filted_data)] return filted_data
def phase_by_time(sig, fs, f_range=None, hilbert_increase_n=False, remove_edges=True, **filter_kwargs): """Compute the instantaneous phase of a time series. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. f_range : tuple of float or None, optional default: None Filter range, in Hz, as (low, high). If None, no filtering is applied. hilbert_increase_n : bool, optional, default: False If True, zero pad the signal's length to the next power of 2 for the Hilbert transform. This is because ``scipy.signal.hilbert`` can be very slow for some lengths of x. remove_edges : bool, optional, default: True If True, replace samples that are within half of the filter's length to the edge with nan. This removes edge artifacts from the filtered signal. Only used if `f_range` is defined. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- pha : 1d array Instantaneous phase time series. Examples -------- Compute the instantaneous phase, for the alpha range: >>> from neurodsp.sim import sim_combined >>> sig = sim_combined(n_seconds=10, fs=500, ... components={'sim_powerlaw': {}, 'sim_oscillation': {'freq': 10}}) >>> pha = phase_by_time(sig, fs=500, f_range=(8, 12)) """ if f_range: sig, filter_kernel = filter_signal(sig, fs, infer_passtype(f_range), f_range=f_range, remove_edges=False, return_filter=True, **filter_kwargs) pha = np.angle(robust_hilbert(sig, increase_n=hilbert_increase_n)) if f_range and remove_edges: pha = remove_filter_edges(pha, len(filter_kernel)) return pha
def sim_powerlaw(n_seconds, fs, exponent=-2.0, f_range=None, **filter_kwargs): """Generate a power law time series with specified exponent by spectrally rotating white noise. Parameters ---------- n_seconds : float Simulation time, in seconds. fs : float Sampling rate of simulated signal, in Hz. exponent : float Desired power-law exponent, of the form P(f)=f^exponent. f_range : list of [float, float] or None, optional Frequency range to filter simulated data, as [f_lo, f_hi], in Hz. **filter_kwargs : kwargs, optional Keyword arguments to pass to `filter_signal`. Returns ------- sig: 1d array Time-series with the desired power-law exponent. """ n_samples = int(n_seconds * fs) sig = np.random.randn(n_samples) # Compute the FFT fft_output = np.fft.fft(sig) freqs = np.fft.fftfreq(len(sig), 1. / fs) # Rotate spectrum and invert, zscore to normalize. # Note: the delta exponent to be applied is divided by two, as # the FFT output is in units of amplitude not power fft_output_rot = rotate_powerlaw(freqs, fft_output, -exponent/2) sig = zscore(np.real(np.fft.ifft(fft_output_rot))) if f_range is not None: filter_signal(sig, fs, infer_passtype(f_range), f_range, **filter_kwargs) return sig
def sim_powerlaw(n_seconds, fs, exponent=-2.0, f_range=None, **filter_kwargs): """Simulate a power law time series, with a specified exponent. Parameters ---------- n_seconds : float Simulation time, in seconds. fs : float Sampling rate of simulated signal, in Hz. exponent : float, optional, default: -2 Desired power-law exponent, of the form P(f)=f^exponent. f_range : list of [float, float] or None, optional Frequency range to filter simulated data, as [f_lo, f_hi], in Hz. **filter_kwargs : kwargs, optional Keyword arguments to pass to `filter_signal`. Returns ------- sig: 1d array Time-series with the desired power law exponent. """ # Get the number of samples to simulate for the signal # If filter is to be filtered, with FIR, add extra to compensate for edges if f_range and filter_kwargs.get('filter_type', None) != 'iir': pass_type = infer_passtype(f_range) filt_len = compute_filter_length(fs, pass_type, *check_filter_definition(pass_type, f_range), n_seconds=filter_kwargs.get('n_seconds', None), n_cycles=filter_kwargs.get('n_cycles', 3)) n_samples = int(n_seconds * fs) + filt_len + 1 else: n_samples = int(n_seconds * fs) sig = _create_powerlaw(n_samples, fs, exponent) if f_range is not None: sig = filter_signal(sig, fs, infer_passtype(f_range), f_range, remove_edges=True, **filter_kwargs) # Drop the edges, that were compensated for, if not using IIR (using FIR) if not filter_kwargs.get('filter_type', None) == 'iir': sig, _ = remove_nans(sig) return sig
def amp_by_time(sig, fs, f_range=None, remove_edges=True, **filter_kwargs): """Compute the instantaneous amplitude of a time series. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. f_range : tuple of float or None, optional default: None Filter range, in Hz, as (low, high). If None, no filtering is applied. remove_edges : bool, optional, default: True If True, replace samples that are within half of the filter's length to the edge with nan. This removes edge artifacts from the filtered signal. Only used if `f_range` is defined. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- amp : 1d array Instantaneous amplitude time series. Examples -------- Compute the instantaneous amplitude, for the alpha range: >>> from neurodsp.sim import sim_combined >>> sig = sim_combined(n_seconds=10, fs=500, ... components={'sim_powerlaw': {}, 'sim_oscillation' : {'freq': 10}}) >>> amp = amp_by_time(sig, fs=500, f_range=(8, 12)) """ if f_range: sig, filter_kernel = filter_signal(sig, fs, infer_passtype(f_range), f_range=f_range, remove_edges=False, return_filter=True, **filter_kwargs) amp = np.abs(robust_hilbert(sig)) if f_range and remove_edges: amp = remove_filter_edges(amp, len(filter_kernel)) return amp
def amp_by_time(sig, fs, f_range=None, hilbert_increase_n=False, remove_edges=True, **filter_kwargs): """Compute the instantaneous amplitude of a time series. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. f_range : tuple of float or None, optional default: None Filter range, in Hz, as (low, high). If None, no filtering is applied. hilbert_increase_n : bool, optional, default: False If True, zero pad the signal to length the next power of 2 when doing the Hilbert transform. This is because :func:`scipy.signal.hilbert` can be very slow for some lengths of sig. remove_edges : bool, optional, default: True If True, replace samples that are within half of the filters length to the edge with np.nan. This removes edge artifacts from the filtered signal. Only used if `f_range` is defined. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- amp : 1d array Instantaneous amplitude time series. """ if f_range: sig, filter_kernel = filter_signal(sig, fs, infer_passtype(f_range), f_range=f_range, remove_edges=False, return_filter=True, **filter_kwargs) amp = np.abs(robust_hilbert(sig, increase_n=hilbert_increase_n)) if f_range and remove_edges: amp = remove_filter_edges(amp, len(filter_kernel)) return amp
def amp_by_time(sig, fs, f_range, hilbert_increase_n=False, remove_edges=True, **filter_kwargs): """Calculate the amplitude time series. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. f_range : tuple of float The frequency filtering range, in Hz, as (low, high). hilbert_increase_n : bool, optional, default: False If True, zeropad the signal to length the next power of 2 when doing the hilbert transform. This is because scipy.signal.hilbert can be very slow for some lengths of sig. remove_edges : bool, optional, default: True If True, replace the samples that are within half a kernel's length to the signal edge with np.nan. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- amp : 1d array Time series of amplitude. """ sig_filt, kernel = filter_signal(sig, fs, infer_passtype(f_range), f_range=f_range, remove_edges=False, return_filter=True, **filter_kwargs) amp = np.abs(robust_hilbert(sig_filt, increase_n=hilbert_increase_n)) if remove_edges: amp = remove_filter_edges(amp, len(kernel)) return amp
def test_detect_bursts_df_amp(): """Test amplitude-threshold burst detection.""" # Load signal sig = np.load(DATA_PATH + 'sim_bursting.npy') fs = 1000 f_range = (6, 14) sig_filt = filter_signal(sig, fs, 'lowpass', 30, n_seconds=.3, remove_edges=False) # Compute cycle-by-cycle df without burst detection column df = compute_features(sig_filt, fs, f_range, burst_detection_method='amp', burst_detection_kwargs={ 'amp_threshes': (1, 2), 'filter_kwargs': { 'n_seconds': .5 } }) df.drop('is_burst', axis=1, inplace=True) # Apply consistency burst detection df_burst_amp = detect_bursts_df_amp(df, sig_filt, fs, f_range, amp_threshes=(.5, 1), n_cycles_min=4, filter_kwargs={'n_seconds': .5}) # Make sure that burst detection is only boolean assert df_burst_amp.dtypes['is_burst'] == 'bool' assert df_burst_amp['is_burst'].mean() > 0 assert df_burst_amp['is_burst'].mean() < 1 assert np.min([sum(1 for _ in group) for key, group \ in itertools.groupby(df_burst_amp['is_burst']) if key]) >= 4
def find_extrema(sig, fs, f_range, boundary=0, first_extrema='peak', filter_kwargs=None, pass_type='bandpass', pad=True): """Identify peaks and troughs in a time series. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. f_range : tuple of (float, float) Frequency range, in Hz, to narrowband filter the signal, used to find zero-crossings. boundary : int, optional, default: 0 Number of samples from edge of the signal 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, optional, default: None Keyword arguments to :func:`~neurodsp.filt.filter.filter_signal`, such as 'n_cycles' or 'n_seconds' to control filter length. pass_type : str, optional, default: 'bandpass' Which kind of filter pass_type is consistent with the frequency definition provided. pad : bool, optional, default: True Whether to pad ``sig`` with zeros to prevent missed cyclepoints at the edges. Returns ------- peaks : 1d array Indices at which oscillatory peaks occur in the input ``sig``. troughs : 1d array Indices at which oscillatory troughs occur in the input ``sig``. 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. Examples -------- Find the locations of peaks and burst in a signal: >>> from neurodsp.sim import sim_bursty_oscillation >>> fs = 500 >>> sig = sim_bursty_oscillation(10, fs, freq=10) >>> peaks, troughs = find_extrema(sig, fs, f_range=(8, 12)) """ # Ensure arguments are within valid range check_param_range(fs, 'fs', (0, np.inf)) # Set default filtering parameters if filter_kwargs is None: filter_kwargs = {} # Get the original signal and filter lengths sig_len = len(sig) filt_len = 0 # Pad beginning of signal with zeros to prevent missing cyclepoints if pad: filt_len = compute_filter_length( fs, pass_type, f_range[0], f_range[1], n_seconds=filter_kwargs.get('n_seconds', None), n_cycles=filter_kwargs.get('n_cycles', 3)) # Pad the signal sig = np.pad(sig, int(np.ceil(filt_len / 2)), mode='constant') # Narrowband filter signal sig_filt = filter_signal(sig, fs, pass_type, f_range, remove_edges=False, **filter_kwargs) # Find rising and decaying zero-crossings (narrowband) rise_xs = find_flank_zerox(sig_filt, 'rise') decay_xs = find_flank_zerox(sig_filt, 'decay') # Compute number of peaks and troughs if rise_xs[-1] > decay_xs[-1]: n_peaks = len(rise_xs) - 1 n_troughs = len(decay_xs) else: n_peaks = len(rise_xs) n_troughs = len(decay_xs) - 1 # Calculate peak samples peaks = np.zeros(n_peaks, dtype=int) for p_idx in range(n_peaks): # Calculate the sample range between the most recent zero rise and the next zero decay last_rise = rise_xs[p_idx] next_decay = decay_xs[decay_xs > last_rise][0] # Identify time of peak peaks[p_idx] = np.argmax(sig[last_rise:next_decay]) + last_rise # Calculate trough samples troughs = np.zeros(n_troughs, dtype=int) for t_idx in range(n_troughs): # Calculate the sample range between the most recent zero decay and the next zero rise last_decay = decay_xs[t_idx] next_rise = rise_xs[rise_xs > last_decay][0] # Identify time of trough troughs[t_idx] = np.argmin(sig[last_decay:next_rise]) + last_decay # Remove padding peaks = peaks - int(np.ceil(filt_len / 2)) troughs = troughs - int(np.ceil(filt_len / 2)) # Remove peaks and trough outside the boundary limit peaks = peaks[np.logical_and(peaks > boundary, peaks < sig_len - boundary)] troughs = troughs[np.logical_and(troughs > boundary, troughs < sig_len - boundary)] # Force the first extrema to be as desired & assure equal # of peaks and troughs if first_extrema == 'peak': troughs = troughs[1:] if peaks[0] > troughs[0] else troughs peaks = peaks[:-1] if peaks[-1] > troughs[-1] else peaks elif first_extrema == 'trough': peaks = peaks[1:] if troughs[0] > peaks[0] else peaks troughs = troughs[:-1] if troughs[-1] > peaks[-1] else troughs elif first_extrema is None: pass else: raise ValueError('Parameter "first_extrema" is invalid') return peaks, troughs
fs = 1000 # sampling frequency in Hz times = rawdata['tx_14'] # time vector times = (times).T print(f"DATA: (n_trials, n_times)={data.shape}; SAMPLING FREQUENCY={fs}Hz; " f"TIME VECTOR: n_times={len(times)}") # Define a frequency range to filter the data sig = np.mean(data, axis=0) f_range = (2, 12) # Bandpass filter the data, across the band of interest sig_filt = filter_signal(sig, fs, 'bandpass', f_range, filter_type='iir', butterworth_order=4) # Plot filtered signal # fig = plt.figure(figsize=(14, 6)) plot_time_series(times, [sig, sig_filt], ['Raw', 'Filtered']) # plt.show() fig = plt.figure(figsize=(14, 6)) # adding the mean PSD over trials # plt.subplot(1, 2, 1) plt.plot(times, sig, color='black') plt.plot(times, sig_filt, color='red', linewidth=5.0) plt.ylim([-50, 50]) # plt.xlim([-1000, 0])
#################################################################################################### # # Load simulated experiment of 10 patients and 10 controls # -------------------------------------------------------- #################################################################################################### # Load experimental data sigs = np.load('data/sim_experiment.npy') fs = 1000 # Sampling rate # Apply lowpass filter to each signal for idx in range(len(sigs)): sigs[idx] = filter_signal(sigs[idx], fs, 'lowpass', 30, n_seconds=.2, remove_edges=False) #################################################################################################### # Plot an example signal n_signals = len(sigs) n_seconds = len(sigs[0]) / fs times = np.arange(0, n_seconds, 1 / fs) plot_time_series(times, sigs[0], lw=2) #################################################################################################### # # Compute cycle-by-cycle features
def find_extrema(sig, fs, f_range, boundary=None, first_extrema='peak', filter_kwargs=None): """Identify peaks and troughs in a time series. Parameters ---------- sig : 1d array Voltage time series. fs : float Sampling rate, in Hz. f_range : tuple of (float, float) Frequency range, in Hz, for narrowband signal of interest, used to find zero-crossings of the oscillation. boundary : int, optional 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, optional Keyword arguments to :func:`~neurodsp.filt.filter.filter_signal`, such as 'n_cycles' or 'n_seconds' to control filter length. Returns ------- ps : 1d array Indices at which oscillatory peaks occur in the input ``sig``. ts : 1d array Indices at which oscillatory troughs occur in the input ``sig``. 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 sig_filt = filter_signal(sig, fs, 'bandpass', f_range, remove_edges=False, **filter_kwargs) # Find rising and falling zero-crossings (narrowband) zerorise_n = _fzerorise(sig_filt) zerofall_n = _fzerofall(sig_filt) # Compute number of peaks and troughs if zerorise_n[-1] > zerofall_n[-1]: pl = len(zerorise_n) - 1 tl = len(zerofall_n) else: pl = len(zerorise_n) tl = len(zerofall_n) - 1 # Calculate peak samples ps = np.zeros(pl, dtype=int) for p_idx in range(pl): # Calculate the sample range between the most recent zero rise and the next zero fall mrzerorise = zerorise_n[p_idx] nfzerofall = zerofall_n[zerofall_n > mrzerorise][0] # Identify time of peak ps[p_idx] = np.argmax(sig[mrzerorise:nfzerofall]) + mrzerorise # Calculate trough samples ts = np.zeros(tl, dtype=int) for t_idx in range(tl): # Calculate the sample range between the most recent zero fall and the next zero rise mrzerofall = zerofall_n[t_idx] nfzerorise = zerorise_n[zerorise_n > mrzerofall][0] # Identify time of trough ts[t_idx] = np.argmin(sig[mrzerofall:nfzerorise]) + mrzerofall # Remove peaks and troughs within the boundary limit ps = ps[np.logical_and(ps > boundary, ps < len(sig) - boundary)] ts = ts[np.logical_and(ts > boundary, ts < len(sig) - 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
# ################################################################################################### # Using filter_signal # ~~~~~~~~~~~~~~~~~~~ # # In the above, we did a step-by-step procedure of designing, evaluating, and applying our filter. # # Note that all of these elements can also be done directly through the # :func:`~.filter_signal` function. # ################################################################################################### sig_filt, sos = filter_signal(sig, fs, pass_type, f_range, filter_type='iir', butterworth_order=butterworth_order, plot_properties=True, print_transitions=True, return_filter=True) ################################################################################################### # Example Application: Line Noise Removal # --------------------------------------- # # A common application of IIR filters is for line noise removal. # # In this example, a 3rd order Butterworth filter is applied to remove 60Hz noise. # ################################################################################################### # Generate a signal, with a low frequency oscillation and 60 Hz line noise components = {'sim_oscillation' : [{'freq' : 6}, {'freq' : 60}]}
# Generate a signal for this example, with an oscillation and 60 Hz line noise components = {'sim_oscillation' : [{'freq' : 6}, {'freq' : 60}]} variances = [1, 0.2] sig = sim_combined(n_seconds, fs, components, variances) ################################################################################################### # Define filter settings f_range = (58, 62) passtype = 'bandstop' ################################################################################################### # Apply a short filter, one that won't achieve our desired attenuation sig_filt_short = filter_signal(sig, fs, passtype, f_range, n_seconds=0.25, plot_properties=True) ################################################################################################### # # Notice that when we apply the filter above, with a short filter length, we get a # warning about the filter attenuation. The filter we have defined does not get to a # sufficient attenuation level. # ###################################################################################################v # This user warning disappears if we elongate the filter sig_filt_long = filter_signal(sig, fs, passtype, f_range, n_seconds=1, plot_properties=True) ###################################################################################################
# Simulation settings n_seconds = 10 fs = 1000 components = {'sim_bursty_oscillation': {'freq': 10, 'enter_burst': .1, 'leave_burst': .1, 'cycle': 'asine', 'rdsym': 0.3}, 'sim_powerlaw': {'f_range': (2, None)}} sig = sim_combined(n_seconds, fs, components=components, component_variances=(2, 1)) # Filter settings f_alpha = (8, 12) n_seconds_filter = .5 # Compute amplitude and phase sig_filt = filter_signal(sig, fs, 'bandpass', f_alpha, n_seconds=n_seconds_filter) theta_amp = amp_by_time(sig, fs, f_alpha, n_seconds=n_seconds_filter) theta_phase = phase_by_time(sig, fs, f_alpha, n_seconds=n_seconds_filter) # Plot signal times = create_times(n_seconds, fs) xlim = (2, 6) tidx = np.logical_and(times >= xlim[0], times < xlim[1]) fig, axes = plt.subplots(figsize=(15, 9), nrows=3) # Plot the raw signal plot_time_series(times[tidx], sig[tidx], ax=axes[0], ylabel='Voltage (mV)', xlabel='', lw=2, labels='raw signal') # Plot the filtered signal and oscillation amplitude
# # Extract signal within a specific frequency range (e.g. theta, 4-8 Hz). # ################################################################################################### # Generate an oscillation with noise fs = 1000 times = create_times(4, 1000) sig = np.random.randn(len(times)) + 5 * np.sin(times * 2 * np.pi * 6) ################################################################################################### # Filter the data, across a frequency band of interest f_range = (4, 8) sig_filt = filt.filter_signal(sig, fs, 'bandpass', f_range) ################################################################################################### # Plot filtered signal plot_time_series(times, [sig, sig_filt], ['Raw', 'Filtered']) ################################################################################################### # # Notice that the edges of the filtered signal are clipped (no red). # # Edge artifact removal is done by default in :func:`filter_signal`, because # the signal samples at the edges only experienced part of the filter. # # To bypass this feature, set `remove_edge_artifacts=False`, but at your own risk! #
def get_bandpass_filter_signal(data, fs, passtype, f_range): sig_filt = filt.filter_signal(data, fs, passtype, f_range) return sig_filt
def run_all(session): # get data session path from mat file path = get_session_path(session) # load position data from .mat file df = load_position(session) # load xml which has channel & fs info channels, fs, shank = loadXML(path) # get good channels good_ch = get_good_channels(shank) # load .lfp lfp, ts = loadLFP(glob.glob(path + '\*.lfp')[0], n_channels=channels, channel=good_ch, frequency=fs, precision='int16') # interp speed of the animal speed = np.interp(ts, df.ts, df.speed) speed[np.isnan(speed)] = 0 # get filtered signal print('filtering signal') filtered_lfps = np.stack([ filter_signal(lfp_, fs, 'bandpass', (80, 250), remove_edges=False) for lfp_ in lfp.T ]) filtered_lfps = filtered_lfps.T # detect ripples print('detecting ripples') ripple_times = Karlsson_ripple_detector(ts, filtered_lfps, speed, fs) # find ripple duration ripple_times[ 'ripple_duration'] = ripple_times.end_time - ripple_times.start_time # check against emg (< 0.85) ripple_times = emg_filter(session, ripple_times, shank) # add ripple channel and peak amp print('getting ripple channel') ripple_times = get_ripple_channel(ripple_times, stats.zscore(filtered_lfps, axis=0), ts, fs) # get instant phase, amp, and freq print('get instant phase, amp, and freq') phase, amp, freq = get_phase_amp_freq(filtered_lfps, fs) # get ripple_map print('getting ripple maps') ripple_maps = get_ripple_maps(ripple_times, ts, lfp, filtered_lfps, phase, amp, freq, fs) # get ripple frequency print('getting ripple frequency') ripple_times['peak_freq'] = [ map[len(map) // 2] for map in ripple_maps['freq_map'] ] # filter out cliped signal ripple_times, ripple_maps = clip_filter(ripple_times, ripple_maps) # filter out very high amplitude ripples #ripple_times,ripple_maps = filter_high_amp(ripple_times,ripple_maps) # find ripples with a single large jump #ripple_times,ripple_maps = filter_single_peaks(ripple_times,ripple_maps) # save ripples for neuroscope inspection save_ripples(ripple_times, path) return ripple_times, lfp, filtered_lfps, ts, ripple_maps
def sim_powerlaw(n_seconds, fs, exponent=-2.0, f_range=None, **filter_kwargs): """Simulate a power law time series, with a specified exponent. Parameters ---------- n_seconds : float Simulation time, in seconds. fs : float Sampling rate of simulated signal, in Hz. exponent : float, optional, default: -2 Desired power-law exponent, of the form P(f)=f^exponent. f_range : list of [float, float] or None, optional Frequency range to filter simulated data, as [f_lo, f_hi], in Hz. **filter_kwargs : kwargs, optional Keyword arguments to pass to `filter_signal`. Returns ------- sig : 1d array Time-series with the desired power law exponent. Notes ----- - Powerlaw data with exponents is created by spectrally rotating white noise [1]_. References ---------- .. [1] Timmer, J., & Konig, M. (1995). On Generating Power Law Noise. Astronomy and Astrophysics, 300, 707–710. Examples -------- Simulate a power law signal, with an exponent of -2 (brown noise): >>> sig = sim_powerlaw(n_seconds=1, fs=500, exponent=-2.0) Simulate a power law signal, with a highpass filter applied at 2 Hz: >>> sig = sim_powerlaw(n_seconds=1, fs=500, exponent=-1.5, f_range=(2, None)) """ # Compute the number of samples for the simulated time series n_samples = compute_nsamples(n_seconds, fs) # Get the number of samples to simulate for the signal # If signal is to be filtered, with FIR, add extra to compensate for edges if f_range and filter_kwargs.get('filter_type', None) != 'iir': pass_type = infer_passtype(f_range) filt_len = compute_filter_length( fs, pass_type, *check_filter_definition(pass_type, f_range), n_seconds=filter_kwargs.get('n_seconds', None), n_cycles=filter_kwargs.get('n_cycles', 3)) n_samples += filt_len + 1 # Simulate the powerlaw data sig = _create_powerlaw(n_samples, fs, exponent) if f_range is not None: sig = filter_signal(sig, fs, infer_passtype(f_range), f_range, remove_edges=True, **filter_kwargs) # Drop the edges, that were compensated for, if not using FIR filter if not filter_kwargs.get('filter_type', None) == 'iir': sig, _ = remove_nans(sig) return sig
#################################################################################################### # Only keep 60 seconds of data n_seconds = 60 ca1_raw = ca1_raw[:int(n_seconds * fs)] ec3_raw = ec3_raw[:int(n_seconds * fs)] #################################################################################################### # Apply a lowpass filter at 25Hz fc = 25 filter_seconds = .5 ca1 = filter_signal(ca1_raw, fs, 'lowpass', fc, n_seconds=filter_seconds, remove_edges=False) ec3 = filter_signal(ec3_raw, fs, 'lowpass', fc, n_seconds=filter_seconds, remove_edges=False) #################################################################################################### # Compute cycle-by-cycle features # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ####################################################################################################
pd.options.display.max_columns = 10 #################################################################################################### # Load data sig = np.load('data/ca1.npy') / 1000 sig = sig[:125000] fs = 1250 f_theta = (4, 10) f_lowpass = 30 n_seconds = .1 # Lowpass filter sig_low = filter_signal(sig, fs, 'lowpass', f_lowpass, n_seconds=n_seconds, remove_edges=False) # Plot signal times = np.arange(0, len(sig) / fs, 1 / fs) xlim = (2, 5) tidx = np.logical_and(times >= xlim[0], times < xlim[1]) plot_time_series(times[tidx], [sig[tidx], sig_low[tidx]], colors=['k', 'k'], alpha=[.5, 1], lw=2) #################################################################################################### #
'freq': 10 }, { 'freq': 60 }] } comp_vars = [0.5, 1, 1] # Simulate a time series sig = sim_combined(n_seconds, fs, components, comp_vars) ################################################################################################### # Bandstop filter the signal to remove line noise frequencies sig_filt = filter_signal(sig, fs, 'bandstop', (57, 63), n_seconds=2, remove_edges=False) ################################################################################################### # Compute a power spectrum of the simulated signal freqs, powers_pre = trim_spectrum(*compute_spectrum(sig, fs), [3, 75]) freqs, powers_post = trim_spectrum(*compute_spectrum(sig_filt, fs), [3, 75]) ################################################################################################### # Plot the spectrum of the data, pre and post bandstop filtering plot_spectra(freqs, [powers_pre, powers_post], log_powers=True, labels=['Pre-Filter', 'Post-Filter'])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # Now that we have a simulated signal, let's filter it into each of our frequency bands. # # To do so, we will loop across our band definitions, and plot the filtered version # of the signal. # ################################################################################################### # Apply band-by-band filtering of our signal into each defined frequency band _, axes = plt.subplots(len(bands), 1, figsize=(12, 15)) for ax, (label, f_range) in zip(axes, bands): # Filter the signal to the current band definition band_sig = filter_signal(sig, s_rate, 'bandpass', f_range) # Plot the time series of the current band, and adjust plot aesthetics plot_time_series(times, band_sig, title=label + ' ' + str(f_range), ax=ax, xlim=(0, n_seconds), ylim=(-1, 1), xlabel='') ################################################################################################### # # As we can see, filtering a signal with aperiodic activity into arbitrary # frequency ranges returns filtered signals that look like rhythmic activity. # # Also, because our simulated signal has some random variation, the filtered components # also exhibit some fluctuations. # # Overall, we can see from filtering this signal that: #
'sim_oscillation': { 'freq': 6 } } variances = [0.1, 1] # Simulate our signal sig = sim_combined(n_seconds, fs, components, variances) ################################################################################################### # Define a frequency range to filter the data f_range = (4, 8) # Bandpass filter the data, across the band of interest sig_filt = filter_signal(sig, fs, 'bandpass', f_range) ################################################################################################### # Plot filtered signal plot_time_series(times, [sig, sig_filt], ['Raw', 'Filtered']) ################################################################################################### # # Notice that the edges of the filtered signal are clipped (no red). # # Edge artifact removal is done by default in NeuroDSP filtering, because # the signal samples at the edges only experienced part of the filter. # # To bypass this feature, set `remove_edges=False`, but at your own risk! #
# Using filter_signal # ~~~~~~~~~~~~~~~~~~~ # # In the above, we did a step-by-step procedure of designing, evaluating, and applying our filter. # # Note that all of these elements can also be done directly through the # :func:`~.filter_signal` function. # ################################################################################################### # Filter our signal, using the main filter function, with extra options sig_filt2, filter_kernel = filter_signal(sig, fs, pass_type, f_range, filter_type='fir', print_transitions=True, plot_properties=True, return_filter=True) ################################################################################################### # Plot the signal and filtered version plot_time_series(times, [sig, sig_filt2], ['Raw', 'Filtered']) ################################################################################################### # # You might notice in the above plot, the edges of the filtered version have been removed. # This is done to remove edge artifacts. Data points at the edge of the signal don't get fully # processed by the filter, and may contain some filtering artifacts. #