def compute_spectrum_wavelet(sig, fs, freqs, avg_type='mean', **kwargs): """Estimate the power spectral densitry using wavelets. Parameters ---------- sig : 1d or 2d array Time series of measurement values. fs : float Sampling rate, in Hz. avg_type : {'mean', 'median'}, optional Method to average across the windows. **kwargs Optional inputs for using wavelets. Returns ------- freqs : 1d array Array of sample frequencies. spectrum : 1d or 2d array Power spectral density. """ mwt = morlet_transform(sig, fs, freqs, **kwargs) spectrum = get_avg_func(avg_type)(mwt, axis=0) return freqs, spectrum
def compute_spectrum_wavelet(sig, fs, freqs, avg_type='mean', **kwargs): """Compute the power spectral density using wavelets. Parameters ---------- sig : 1d or 2d array Time series. fs : float Sampling rate, in Hz. freqs : 1d array or list of float If array, frequency values to estimate with morlet wavelets. If list, define the frequency range, as [freq_start, freq_stop, freq_step]. The `freq_step` is optional, and defaults to 1. Range is inclusive of `freq_stop` value. avg_type : {'mean', 'median'}, optional Method to average across the windows. **kwargs Optional inputs for using wavelets. Returns ------- freqs : 1d array Frequencies at which the measure was calculated. spectrum : 1d or 2d array Power spectral density. """ if isinstance(freqs, (tuple, list)): freqs = create_freqs(*freqs) mwt = compute_wavelet_transform(sig, fs, freqs, **kwargs) spectrum = get_avg_func(avg_type)(mwt, axis=0) return freqs, spectrum
def compute_spectrum_welch(sig, fs, avg_type='mean', window='hann', nperseg=None, noverlap=None, f_range=None, outlier_percent=None): """Compute the power spectral density using Welch's method. Parameters ----------- sig : 1d or 2d array Time series. fs : float Sampling rate, in Hz. avg_type : {'mean', 'median'}, optional Method to average across the windows: * 'mean' is the same as Welch's method, taking the mean across FFT windows. * 'median' uses median across FFT windows instead of the mean, to minimize outlier effect. window : str or tuple or array_like, optional, default: 'hann' Desired window to use. See scipy.signal.get_window for a list of available windows. If array_like, the array will be used as the window and its length must be nperseg. nperseg : int, optional Length of each segment, in number of samples. If None, and window is str or tuple, is set to 1 second of data. If None, and window is array_like, is set to the length of the window. noverlap : int, optional Number of points to overlap between segments. If None, noverlap = nperseg // 8. f_range : list of [float, float] optional Frequency range to sub-select from the power spectrum. outlier_percent : float, optional The percentage of outlier values to be removed. Must be between 0 and 100. Returns ------- freqs : 1d array Frequencies at which the measure was calculated. spectrum : 1d or 2d array Power spectral density. """ # Calculate the short time fourier transform with signal.spectrogram nperseg, noverlap = check_spg_settings(fs, window, nperseg, noverlap) freqs, _, spg = spectrogram(sig, fs, window, nperseg, noverlap) # Throw out outliers if indicated if outlier_percent is not None: spg = discard_outliers(spg, outlier_percent) # Average across windows spectrum = get_avg_func(avg_type)(spg, axis=-1) # Trim spectrum, if requested if f_range: freqs, spectrum = trim_spectrum(freqs, spectrum, f_range) return freqs, spectrum
def compute_spectrum_wavelet(sig, fs, freqs, avg_type='mean', **kwargs): """Compute the power spectral density using wavelets. Parameters ---------- sig : 1d or 2d array Time series. fs : float Sampling rate, in Hz. freqs : 1d array or list of float If array, frequency values to estimate with morlet wavelets. If list, define the frequency range, as [freq_start, freq_stop, freq_step]. The `freq_step` is optional, and defaults to 1. Range is inclusive of `freq_stop` value. avg_type : {'mean', 'median'}, optional Method to average across the windows. **kwargs Optional inputs for using wavelets. Returns ------- freqs : 1d array Frequencies at which the measure was calculated. spectrum : 1d or 2d array Power spectral density. Examples -------- Compute the power spectrum of a simulated time series using wavelets: >>> from neurodsp.sim import sim_combined >>> sig = sim_combined(n_seconds=10, fs=500, ... components={'sim_powerlaw': {}, 'sim_oscillation' : {'freq': 10}}) >>> freqs, spectrum = compute_spectrum_wavelet(sig, fs=500, freqs=[1, 30]) """ if isinstance(freqs, (tuple, list)): freqs = create_freqs(*freqs) # Compute the wavelet transform mwt = compute_wavelet_transform(sig, fs, freqs, **kwargs) # Convert the wavelet coefficient outputs to units of power mwt_power = abs(mwt)**2 # Create the power spectrum by averaging across the time dimension spectrum = get_avg_func(avg_type)(mwt_power, axis=1) return freqs, spectrum
def detect_bursts_dual_threshold(sig, fs, dual_thresh, f_range=None, min_n_cycles=3, min_burst_duration=None, avg_type='median', magnitude_type='amplitude', **filter_kwargs): """Detect bursts in a signal using the dual threshold algorithm. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. dual_thresh : tuple of (float, float) Low and high threshold values for burst detection. Units are normalized by the average signal magnitude. f_range : tuple of (float, float), optional Frequency range, to filter signal to, before running burst detection. If f_range is None, then no filtering is applied prior to running burst detection. min_n_cycles : float, optional, default: 3 Minimum burst duration in to keep. Only used if `f_range` is defined, and is used as the number of cycles at f_range[0]. min_burst_duration : float, optional, default: None Minimum length of a burst, in seconds. Must be defined if not filtering. Only used if `f_range` is not defined, or if `min_n_cycles` is set to None. avg_type : {'median', 'mean'}, optional Averaging method to use to normalize the magnitude that is used for thresholding. magnitude_type : {'amplitude', 'power'}, optional Metric of magnitude used for thresholding. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- is_burst : 1d array Boolean indication of where bursts are present in the input signal. True indicates that a burst was detected at that sample, otherwise False. Notes ----- The dual-threshold burst detection algorithm was originally proposed in [1]_. References ---------- .. [1] Feingold, J., Gibson, D. J., DePasquale, B., & Graybiel, A. M. (2015). Bursts of beta oscillation differentiate postperformance activity in the striatum and motor cortex of monkeys performing movement tasks. Proceedings of the National Academy of Sciences, 112(44), 13687–13692. DOI: https://doi.org/10.1073/pnas.1517629112 Examples -------- Detect bursts using the dual threshold algorithm: >>> from neurodsp.sim import sim_combined >>> sig = sim_combined(n_seconds=10, fs=500, ... components={'sim_synaptic_current': {}, ... 'sim_bursty_oscillation' : {'freq': 10}}, ... component_variances=[0.1, 0.9]) >>> is_burst = detect_bursts_dual_threshold(sig, fs=500, dual_thresh=(1, 2), f_range=(8, 12)) """ if len(dual_thresh) != 2: raise ValueError( "Invalid number of elements in 'dual_thresh' parameter") # Compute amplitude time series sig_magnitude = amp_by_time(sig, fs, f_range, remove_edges=False, **filter_kwargs) # Set magnitude as power or amplitude: square if power, leave as is if amplitude check_param_options(magnitude_type, 'magnitude_type', ['amplitude', 'power']) if magnitude_type == 'power': sig_magnitude = sig_magnitude**2 # Calculate normalized magnitude sig_magnitude = sig_magnitude / get_avg_func(avg_type)(sig_magnitude) # Identify time periods of bursting using the 2 thresholds is_burst = _dual_threshold_split(sig_magnitude, dual_thresh[1], dual_thresh[0]) # Remove bursts detected that are too short # Use a number of cycles defined on the frequency range, if available if f_range is not None and min_n_cycles is not None: min_burst_samples = int(np.ceil(min_n_cycles * fs / f_range[0])) # Otherwise, make sure minimum duration is set, and use that else: if min_burst_duration is None: raise ValueError( "Minimum burst duration must be defined if not filtering " "and using a number of cycles threshold.") min_burst_samples = int(np.ceil(min_burst_duration * fs)) is_burst = _rmv_short_periods(is_burst, min_burst_samples) return is_burst.astype(bool)
def detect_bursts_dual_threshold(sig, fs, dual_thresh, f_range=None, min_n_cycles=3, min_burst_duration=None, avg_type='median', magnitude_type='amplitude', **filter_kwargs): """Detect bursts in a signal using the dual threshold algorithm. Parameters ---------- sig : 1d array Time series. fs : float Sampling rate, in Hz. dual_thresh : tuple of (float, float) Low and high threshold values for burst detection. Units are normalized by the average signal magnitude. f_range : tuple of (float, float), optional Frequency range, to filter signal to, before running burst detection. If f_range is None, then no filtering is applied prior to running burst detection. min_n_cycles : float, optional, default=3 Minimum burst duration in to keep. Only used if `f_range` is defined, and is used as the number of cycles at f_range[0]. min_burst_duration : float, optional, default=None Minimum length of a burst, in seconds. Must be defined if not filtering. Only used if `f_range` is not defined, or if `min_n_cycles` is set to None. avg_type : {'median', 'mean'}, optional Averaging method to use to normalize the magnitude that is used for thresholding. magnitude_type : {'amplitude', 'power'}, optional Metric of magnitude used for thresholding. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- is_burst : 1d array Boolean indication of where bursts are present in the input signal. True indicates that a burst was detected at that sample, otherwise False. """ if len(dual_thresh) != 2: raise ValueError( "Invalid number of elements in 'dual_thresh' parameter") # Compute amplitude time series sig_magnitude = amp_by_time(sig, fs, f_range, remove_edges=False, **filter_kwargs) # Set magnitude as power or amplitude: square if power, leave as is if amplitude if magnitude_type not in ['amplitude', 'power']: raise ValueError("Invalid input for 'magnitude_type'") if magnitude_type == 'power': sig_magnitude = sig_magnitude**2 # Calculate normalized magnitude sig_magnitude = sig_magnitude / get_avg_func(avg_type)(sig_magnitude) # Identify time periods of bursting using the 2 thresholds is_burst = _dual_threshold_split(sig_magnitude, dual_thresh[1], dual_thresh[0]) # Remove bursts detected that are too short # Use a number of cycles defined on the frequency range, if available if f_range and min_n_cycles: min_burst_samples = int(np.ceil(min_n_cycles * fs / f_range[0])) # Otherwise, make sure minimum duration is set, and use that else: if min_burst_duration is None: raise ValueError( "Minimum burst duration must be defined if not filtering" "and using a number of cycles threshold.") min_burst_samples = int(np.ceil(min_burst_duration * fs)) is_burst = _rmv_short_periods(is_burst, min_burst_samples) return is_burst.astype(bool)
def detect_bursts_dual_threshold(sig, fs, f_range, dual_thresh, min_cycles=3, avg_type='median', magnitude_type='amplitude', **filter_kwargs): """Detect bursts in a neural signal using the dual threshold algorithm. 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. dual_thresh : tuple of (float, float) Low and high threshold values for burst detection. Units are normalized by the average signal magnitude. min_cycles : float, optional, default=3 Minimum burst duration in terms of number of cycles of f_range[0]. avg_type : {'median', 'mean'}, optional Averaging method to use to normalize the magnitude that is used for thresholding. magnitude_type : {'amplitude', 'power'}, optional Metric of magnitude used for thresholding. **filter_kwargs Keyword parameters to pass to `filter_signal`. Returns ------- is_burst : 1d array Boolean indication of where bursts are present in the input signal. True indicates that a burst was detected at that sample, otherwise False. """ if len(dual_thresh) != 2: raise ValueError( "Invalid number of elements in 'dual_thresh' parameter") # Compute amplitude time series sig_magnitude = amp_by_time(sig, fs, f_range, remove_edges=False, **filter_kwargs) # Set magnitude as power or amplitude: square if power, leave as is if amplitude if magnitude_type not in ['amplitude', 'power']: raise ValueError("Invalid input for 'magnitude_type'") if magnitude_type == 'power': sig_magnitude = sig_magnitude**2 # Calculate normalized magnitude sig_magnitude = sig_magnitude / get_avg_func(avg_type)(sig_magnitude) # Identify time periods of bursting using the 2 thresholds is_burst = _dual_threshold_split(sig_magnitude, dual_thresh[1], dual_thresh[0]) # Remove bursts detected that are too short min_period_length = int(np.ceil(min_cycles * fs / f_range[0])) is_burst = _rmv_short_periods(is_burst, min_period_length) return is_burst.astype(bool)
def compute_spectrum_welch(sig, fs, avg_type='mean', window='hann', nperseg=None, noverlap=None, f_range=None, outlier_percent=None): """Compute the power spectral density using Welch's method. Parameters ---------- sig : 1d or 2d array Time series. fs : float Sampling rate, in Hz. avg_type : {'mean', 'median'}, optional Method to average across the windows: * 'mean' is the same as Welch's method, taking the mean across FFT windows. * 'median' uses median across FFT windows instead of the mean, to minimize outlier effects. window : str or tuple or array_like, optional, default: 'hann' Desired window to use. See scipy.signal.get_window for a list of available windows. If array_like, the array will be used as the window and its length must be nperseg. nperseg : int, optional Length of each segment, in number of samples. If None, and window is str or tuple, is set to 1 second of data. If None, and window is array_like, is set to the length of the window. noverlap : int, optional Number of points to overlap between segments. If None, noverlap = nperseg // 8. f_range : list of [float, float], optional Frequency range to sub-select from the power spectrum. outlier_percent : float, optional The percentage of outlier values to be removed. Must be between 0 and 100. Returns ------- freqs : 1d array Frequencies at which the measure was calculated. spectrum : 1d or 2d array Power spectral density. Notes ----- - Welch's method ([1]_) computes a power spectra by averaging over windowed FFTs. References ---------- .. [1] Welch, P. (1967). The use of fast Fourier transform for the estimation of power spectra: A method based on time averaging over short, modified periodograms. IEEE Transactions on Audio and Electroacoustics, 15(2), 70–73. DOI: https://doi.org/10.1109/TAU.1967.1161901 Examples -------- Compute the power spectrum of a simulated time series using Welch's method: >>> from neurodsp.sim import sim_combined >>> sig = sim_combined(n_seconds=10, fs=500, ... components={'sim_powerlaw': {}, 'sim_oscillation': {'freq': 10}}) >>> freqs, spec = compute_spectrum_welch(sig, fs=500) """ # Calculate the short time Fourier transform with signal.spectrogram nperseg, noverlap = check_spg_settings(fs, window, nperseg, noverlap) freqs, _, spg = spectrogram(sig, fs, window, nperseg, noverlap) # Throw out outliers if indicated if outlier_percent is not None: spg = discard_outliers(spg, outlier_percent) # Average across windows spectrum = get_avg_func(avg_type)(spg, axis=-1) # Trim spectrum, if requested if f_range: freqs, spectrum = trim_spectrum(freqs, spectrum, f_range) return freqs, spectrum