Пример #1
0
def compute_spectrum_medfilt(sig, fs, filt_len=1., f_range=None):
    """Estimate the power spectral densitry as a smoothed FFT.

    Parameters
    ----------
    sig : 1d or 2d array
        Time series of measurement values.
    fs : float
        Sampling rate, in Hz.
    filt_len : float, optional, default: 1.
        Length of the median filter, in Hz.
    f_range : list of [float, float] optional
        Frequency range to sub-select from the power spectrum.

    Returns
    -------
    freqs : 1d array
        Array of sample frequencies.
    spectrum : 1d or 2d array
        Power spectral density.
    """

    # Take the positive half of the spectrum since it's symmetrical
    ft = np.fft.fft(sig)[:int(np.ceil(len(sig) / 2.))]
    freqs = np.fft.fftfreq(len(sig), 1. /
                           fs)[:int(np.ceil(len(sig) / 2.))]  # get freq axis

    # Convert median filter length from Hz to samples
    filt_len_samp = int(int(filt_len / (freqs[1] - freqs[0])) / 2 * 2 + 1)
    spectrum = medfilt(np.abs(ft)**2. / (fs * len(sig)), filt_len_samp)

    if f_range:
        freqs, spectrum = trim_spectrum(freqs, spectrum, f_range)

    return freqs, spectrum
Пример #2
0
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
Пример #3
0
def compute_spectrum_medfilt(sig, fs, filt_len=1., f_range=None):
    """Compute the power spectral density as a smoothed FFT.

    Parameters
    ----------
    sig : 1d or 2d array
        Time series.
    fs : float
        Sampling rate, in Hz.
    filt_len : float, optional, default: 1
        Length of the median filter, in Hz.
    f_range : list of [float, float], optional
        Frequency range to sub-select from the power spectrum.

    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 as a smoothed FFT:

    >>> 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_medfilt(sig, fs=500)
    """

    # Take the positive half of the spectrum, since it's symmetrical
    ft = np.fft.fft(sig)[:int(np.ceil(len(sig) / 2.))]
    freqs = np.fft.fftfreq(len(sig), 1. / fs)[:int(np.ceil(len(sig) / 2.))]

    # Convert median filter length from Hz to samples, and make sure it is odd
    filt_len_samp = int(filt_len / (freqs[1] - freqs[0]))
    if filt_len_samp % 2 == 0:
        filt_len_samp += 1

    spectrum = medfilt(np.abs(ft)**2. / (fs * len(sig)), filt_len_samp)

    if f_range:
        freqs, spectrum = trim_spectrum(freqs, spectrum, f_range)

    return freqs, spectrum
Пример #4
0
def compute_spectrum_medfilt(sig, fs, filt_len=1., f_range=None):
    """Compute the power spectral density as a smoothed FFT.

    Parameters
    ----------
    sig : 1d or 2d array
        Time series.
    fs : float
        Sampling rate, in Hz.
    filt_len : float, optional, default: 1
        Length of the median filter, in Hz.
    f_range : list of [float, float], optional
        Frequency range to sub-select from the power spectrum.

    Returns
    -------
    freqs : 1d array
        Frequencies at which the measure was calculated.
    spectrum : 1d or 2d array
        Power spectral density.
    """

    # Take the positive half of the spectrum since it's symmetrical
    ft = np.fft.fft(sig)[:int(np.ceil(len(sig) / 2.))]
    freqs = np.fft.fftfreq(len(sig), 1. / fs)[:int(np.ceil(len(sig) / 2.))]

    # Convert median filter length from Hz to samples, and make sure it is odd
    filt_len_samp = int(int(filt_len / (freqs[1] - freqs[0])))
    if filt_len_samp % 2 == 0:
        filt_len_samp += 1

    spectrum = medfilt(np.abs(ft)**2. / (fs * len(sig)), filt_len_samp)

    if f_range:
        freqs, spectrum = trim_spectrum(freqs, spectrum, f_range)

    return freqs, spectrum
Пример #5
0
def compute_spectral_hist(sig,
                          fs,
                          window='hann',
                          nperseg=None,
                          noverlap=None,
                          nbins=50,
                          f_range=[0., 100.],
                          cut_pct=[0., 100.]):
    """Compute the distribution of log10 power at each frequency from the signal spectrogram.

    Parameters
    -----------
    sig : 1d array
        Time series of measurement values.
    fs : float
        Sampling rate, in Hz.
    window : str or tuple or array_like, optional, default='hann'
        Desired window to use. Defaults to a Hann window.
        See scipy.signal.get_window for a list of windows and required parameters.
        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 // 2.
    nbins : int, optional, default: 50
        Number of histogram bins to use.
    f_range : list of [float, float], optional, default: [0, 100]
        Frequency range of the spectrogram to compute the histograms, as [start, end], in Hz.
    cut_pct : list of [float, float], optional, default: [0, 100]
        Power percentile at which to draw the lower and upper bin limits, as [low, high], in Hz.

    Returns
    -------
    freqs : 1d array
        Array of frequencies.
    power_bins : 1d array
        Histogram bins used to compute the distribution.
    spectral_hist : 2d array
        Power distribution at every frequency, nbins x fs 2D matrix.

    Notes
    -----
    The histogram bins are the same for every frequency, thus evenly spacing the global min and max power.
    """

    # Compute spectrogram of data
    nperseg, noverlap = check_spg_settings(fs, window, nperseg, noverlap)
    freqs, _, spg = spectrogram(sig,
                                fs,
                                window,
                                nperseg,
                                noverlap,
                                return_onesided=True)

    # Get log10 power & limit to frequency range of interest before binning
    ps = np.transpose(np.log10(spg))
    freqs, ps = trim_spectrum(freqs, ps, f_range)

    # Prepare bins for power - min and max of bins determined by power cutoff percentage
    power_min, power_max = np.percentile(np.ndarray.flatten(ps), cut_pct)
    power_bins = np.linspace(power_min, power_max, nbins + 1)

    # Compute histogram of power for each frequency
    spectral_hist = np.zeros((len(ps[0]), nbins))
    for ind in range(len(ps[0])):
        spectral_hist[ind], _ = np.histogram(ps[:, ind], power_bins)
        spectral_hist[ind] = spectral_hist[ind] / sum(spectral_hist[ind])

    # Flip output for more sensible plotting direction
    spectral_hist = np.transpose(spectral_hist)
    spectral_hist = np.flipud(spectral_hist)

    return freqs, power_bins, spectral_hist
Пример #6
0
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