def test_sim_peak_oscillation():

    sig_ap = sim_powerlaw(N_SECONDS, FS)
    sig = sim_peak_oscillation(sig_ap, FS, FREQ1, bw=5, height=10)

    check_sim_output(sig)

    # Ensure the peak is at or close (+/- 5hz) to FREQ1
    _, powers_ap = compute_spectrum(sig_ap, FS)
    _, powers = compute_spectrum(sig, FS)

    assert abs(np.argmax(powers - powers_ap) - FREQ1) < 5
Beispiel #2
0
def ft(subset, **ft_kwargs):
    """
    Decomposition in time over both summed and non summed neurons
    returns: freqs, powers_summed, powers_chans (2d array n_chans x n_freqs)
    """
    if not isinstance(subset, np.ndarray):
        subset = np.array(subset)
    summed_neurons = subset.sum(axis= 1) # summing data for ft decomp.
    freqs, powers_summed = compute_spectrum(summed_neurons, **ft_kwargs)   #powers_sum is an array
    freqs, powers_chans  = compute_spectrum(subset.T, **ft_kwargs)   #returns a matrix! #TODO make sure transpose

    return freqs, powers_summed, powers_chans
def plot_power_spectrum_neurodsp(dt, rate_conn, rate_noconn, analysis):
    fs = 1 / dt

    # Plot the results for L23
    freq_mean_L23_conn, P_mean_L23_conn = spectral.compute_spectrum(
        rate_conn[0, :, 0], fs, method='mean')
    freq_mean_L23_noconn, P_mean_L23_noconn = spectral.compute_spectrum(
        rate_noconn[0, :, 0], fs, method='mean')

    plt.figure()
    plt.loglog(freq_mean_L23_conn,
               P_mean_L23_conn,
               label='Coupled',
               linewidth=2,
               color='g')
    plt.loglog(freq_mean_L23_noconn,
               P_mean_L23_noconn,
               label='Uncoupled',
               linewidth=2,
               color='k')
    plt.xlim([1, 100])
    plt.ylim([10**-4, 10**-2])
    plt.ylabel('Power')
    plt.xlabel('Frequency (Hz)')
    plt.legend()

    # Plot the results for L56
    freq_mean_L56_conn, P_mean_L56_conn = spectral.compute_spectrum(
        rate_conn[2, :, 0], fs, method='mean')
    freq_mean_L56_noconn, P_mean_L56_noconn = spectral.compute_spectrum(
        rate_noconn[2, :, 0], fs, method='mean')

    plt.figure()
    plt.loglog(freq_mean_L56_conn,
               P_mean_L56_conn,
               label='Coupled',
               linewidth=2,
               color='#FF7F50')
    plt.loglog(freq_mean_L56_noconn,
               P_mean_L56_noconn,
               label='Uncoupled',
               linewidth=2,
               color='k')
    plt.xlim([1, 100])
    plt.ylim([10**-5, 10**-0])
    plt.ylabel('Power')
    plt.xlabel('Frequency (Hz)')
    plt.legend()
Beispiel #4
0
def calc_spectral_psd(data, srate, method_type="median"):
    # (f, psd) = spectral.compute_spectrum(sig = data, fs = srate, method = method_type, nperseg = srate*2)
    (f, psd) = spectral.compute_spectrum(sig=data,
                                         fs=srate,
                                         avg_type=method_type,
                                         nperseg=srate * 2)
    return (f, psd)
Beispiel #5
0
def getMeanFreqPSD(eeg_data, fs=eeg_fs):
    freq_mean, psd_mean = compute_spectrum(eeg_data,
                                           fs,
                                           method='welch',
                                           avg_type='mean',
                                           nperseg=fs * 2)
    return freq_mean, psd_mean
Beispiel #6
0
 def get_spectrum(self, samples):
     spectrums = []
     for i in range(self.NDIMS):
         freqs, spectre = spectral.compute_spectrum(samples[i, :],
                                                    self.SAMPLE_RATE)
         spectrums.append(spectre)
     return freqs, np.asarray(spectrums)
Beispiel #7
0
def test_data(sim_sig):

    # Load data
    sig_1d = sim_sig['sig']
    fs = sim_sig['fs']
    f_range = sim_sig['f_range']

    # Duplicate sig to create 2d/3d arrays
    sig_2d = np.array([sig_1d] * 2)
    sig_3d = np.array([sig_2d] * 2)
    sig_4d = np.array([sig_3d] * 2)

    # FFT
    freqs, powers_1d = compute_spectrum(sig_1d, fs, f_range=f_range)

    # Create a (2, 100) array
    powers_2d = np.array([powers_1d for dim1 in range(2)])

    # Create a (2, 2, 100) array
    powers_3d =  np.array([[powers_1d for dim1 in range(2)] for dim2 in range(2)])

    # Create a (2, 2, 2, 100) array
    powers_4d = np.array([[[powers_1d for dim1 in range(2)] for dim2 in range(2)] \
        for dim3 in range(2)])

    yield {'sig_1d': sig_1d, 'sig_2d': sig_2d, 'sig_3d': sig_3d, 'sig_4d': sig_4d,
           'fs': fs, 'f_range': f_range, 'freqs': freqs,  'powers_1d': powers_1d,
           'powers_2d': powers_2d, 'powers_3d': powers_3d, 'powers_4d': powers_4d}
Beispiel #8
0
def test_sim_knee():

    # Build the signal and run a smoke test
    sig = sim_knee(N_SECONDS, FS, EXP1, EXP2, KNEE)
    check_sim_output(sig)

    # Check against the power spectrum when you take the Fourier transform
    sig_len = int(FS * N_SECONDS)
    freqs = np.linspace(0, FS / 2, num=sig_len // 2, endpoint=True)

    # Ignore the DC component to avoid division by zero in the Lorentzian
    freqs = freqs[1:]
    true_psd = np.array(
        [1 / (freq**-EXP1 * (freq**(-EXP2 - EXP1) + KNEE)) for freq in freqs])

    # Only look at the frequencies (ignoring DC component) up to the nyquist rate
    sig_hat = np.fft.fft(sig)[1:sig_len // 2]
    numerical_psd = np.abs(sig_hat)**2

    np.allclose(true_psd, numerical_psd, atol=EPS)

    # Accuracy test for a single exponent
    sig = sim_knee(n_seconds=N_SECONDS, fs=FS, chi1=0, chi2=EXP2, knee=KNEE)

    freqs, powers = compute_spectrum(sig, FS, f_range=(1, 200))

    def _estimate_single_knee(xs, offset, knee, exponent):
        return np.zeros_like(xs) + offset - np.log10(xs**exponent + knee)

    ap_params, _ = curve_fit(_estimate_single_knee, freqs, np.log10(powers))
    _, _, EXP2_hat = ap_params[:]

    assert -round(EXP2_hat) == EXP2
Beispiel #9
0
def get_fft(data, fs):
    sp = np.abs(np.fft.fft(data))
    freq = np.fft.fftfreq(len(data))*fs
    # This breaks the data up into two-second windows (nperseg=fs*2) and applies a hanning window 
    # to the time-series windows (window='hann'). It then FFTs each hanning'd window, and then 
    # averages all those FFTs (method='mean') mean of spectrogram (Welch)
    freq_mean, P_mean = spectral.compute_spectrum(data, fs, method='mean', window='hann', nperseg=fs*2) 
    return freq, sp, freq_mean, P_mean
def ft_on_data(subset, fs, nperseg, noverlap):
    """
    Decomposition in time
    """
    summed_neurons = subset.sum(axis= 1) # summing data for ft decomp.
    freqs, powers = compute_spectrum(summed_neurons, fs = fs, nperseg = nperseg, noverlap = noverlap)   #making these parameters now

    return freqs, powers
Beispiel #11
0
def test_compute_irasa(tsig_comb):

    # Estimate periodic and aperiodic components with IRASA
    f_range = [1, 30]
    freqs, psd_ap, psd_pe = compute_irasa(tsig_comb, FS, f_range, noverlap=int(2*FS))

    assert len(freqs) == len(psd_ap) == len(psd_pe)

    # Compute r-squared for the full model, comparing to a standard power spectrum
    _, powers = trim_spectrum(*compute_spectrum(tsig_comb, FS, nperseg=int(4*FS)), f_range)
    r_sq = np.corrcoef(np.array([powers, psd_ap+psd_pe]))[0][1]
    assert r_sq > .95
Beispiel #12
0
    def get_spectrum(
        self, samples
    ):  #Recibe DESDE Data_Manager las muestras tomadas y guardadas en el BUFFER tras ser filtradas

        spectrums = []
        # Trata la señal almacenada en cada fila de la muestra, proveniente de cada sensor
        for i in range(self.constants.NDIMS):
            # Calcula la frecuencia y el espectro de potencia de la muestra y la añade al vector de espectros
            freqs, spectre = spectral.compute_spectrum(
                samples[i, :], self.constants.SAMPLE_RATE
            )  #Analiza las muestras a la frecuencia de muestreo
            spectrums.append(spectre)
        return freqs, np.asarray(
            spectrums)  #Devuelve la frecuencia y una matriz de espectros
Beispiel #13
0
    def plot_spectra(self, result_index, f_range=(1, 100), figsize=(8, 8)):

        if self.results[result_index].sig_pe is None or self.results[
                result_index].sig_ap is None:
            self.decompose()

        # Compute spectra
        freqs, powers = compute_spectrum(self.sig, self.fs, f_range=f_range)

        freqs_pe, powers_pe = compute_spectrum(
            self.results[result_index].sig_pe, self.fs, f_range=f_range)

        freqs_ap, powers_ap = compute_spectrum(
            self.results[result_index].sig_ap, self.fs, f_range=f_range)

        # Plot
        _, ax = plt.subplots(figsize=figsize)

        plot_power_spectra(freqs, [powers, powers_pe, powers_ap],
                           title="Reconstructed Components",
                           labels=['Orig', 'PE Recon', 'AP Recon'],
                           ax=ax,
                           alpha=[0.7, 0.7, 0.7],
                           lw=3)
Beispiel #14
0
def build_all(sig, fs, n_build=np.inf, sleep=0.05, label='viz', save=False):
    """Build all plots together."""

    size = 750
    step = 2
    start = 2000

    sig_yielder = yield_sig(sig, start=start, size=size, step=step)

    for ind in range(n_build):

        clear_output(wait=True)

        fig, axes = make_axes()

        spect_sig = sig[start + step * ind - 2000:start + step * ind + 2000]
        freqs, powers = trim_spectrum(*compute_spectrum(spect_sig, fs=fs),
                                      [1, 50])

        plot_timeseries(next(sig_yielder), ax=axes[0])
        plot_spectra(freqs, powers, log_freqs=True, ax=axes[1])

        animate_plot(fig, save, ind, label=label, sleep=sleep)
Beispiel #15
0
}

###################################################################################################

# Simulate an oscillation over an aperiodic component
signal = sim.sim_combined(n_seconds, fs, components)

###################################################################################################

# Plot the simulated data, in the time domain
plot_time_series(times, signal)

###################################################################################################

# Plot the simulated data, in the frequency domain
freqs, psd = spectral.compute_spectrum(signal, fs)
plot_power_spectra(freqs, psd)

###################################################################################################
#
# We can switch out any components that we want, for example trading the stationary oscillation
# for a bursting oscillation, also with an aperiodic component.
#
# We can also control the relative proportions of each component, by using a parameter called
# `component_variances` that specifies the variance of each component.
#

###################################################################################################

# Define the components of the combined signal to simulate
components = {
Beispiel #16
0
# Plot a segment of the extracted time series data
plot_time_series(times, sig)

###################################################################################################
# Calculate Power Spectra
# -----------------------
#
# Next lets check the data in the frequency domain, calculating a power spectrum
# with the median welch's procedure from NeuroDSP.
#

###################################################################################################

# Calculate the power spectrum, using a median welch & extract a frequency range of interest
freqs, powers = compute_spectrum(sig, fs, method='welch', avg_type='median')
freqs, powers = trim_spectrum(freqs, powers, [3, 30])

###################################################################################################

# Check where the peak power is
peak_cf = freqs[np.argmax(powers)]
print(peak_cf)

###################################################################################################

# Plot the power spectra, and note the peak power
plot_power_spectra(freqs, powers)
plt.plot(freqs[np.argmax(powers)], np.max(powers), '.r', ms=12)

###################################################################################################
Beispiel #17
0
# Set the exponent for brown noise, which is -2
exponent = -2

# Simulate powerlaw activity
br_noise = sim_powerlaw(n_seconds, fs, exponent)

###################################################################################################

# Plot the simulated data, in the time domain
plot_time_series(times, br_noise, title='Brown Noise')

###################################################################################################

# Plot the simulated data, in the frequency domain
freqs, psd = compute_spectrum(br_noise, fs)
plot_power_spectra(freqs, psd)

###################################################################################################
# Simulate Filtered 1/f Activity
# ------------------------------
#
# The power law simulation function is also integrated with a filter. This can be useful
# for filtering out some low frequencies, as is often done with neural signals,
# to remove the very slow drifts that we see in the pure 1/f simulations.
#
# To filter a simulated power law signal, simply pass in a filter range, and the filter will
# be applied to the simulated data before being returned.
#
# Here we will apply a high-pass filter. We can see that the resulting signal has much less
# low-frequency drift than the first one.
Beispiel #18
0
#
# However, in physiological data, we may be interested in visualizing the distribution of
# power values around the mean at each frequency, as estimated in sequential slices of
# short-time Fourier transform (STFT), since it may reveal non-stationarities in the data
# or particular frequencies that are not like the rest. Here, we simply bin the log-power
# values across time, in a histogram, to observe the noise distribution at each frequency.
#

###################################################################################################

# Calculate the spectral histogram
freqs, bins, spect_hist = compute_spectral_hist(sig, fs, nbins=50, f_range=(0, 80),
                                                cut_pct=(0.1, 99.9))

# Calculate a power spectrum, with median Welch
freq_med, psd_med = compute_spectrum(sig, fs, method='welch',
                                     avg_type='median', nperseg=fs*2)

# Plot the spectral histogram
plot_spectral_hist(freqs, bins, spect_hist, freq_med, psd_med)

###################################################################################################
#
# Notice in the plot that not only is theta power higher overall (shifted up),
# it also has lower variance around its mean.
#

###################################################################################################
# Spectral Coefficient of Variation (SCV)
# ---------------------------------------
#
# Next, let's look at computing the spectral coefficient of variation, with
Beispiel #19
0
# Simulate brown noise
n_seconds = 10
fs = 1000
exponent = -2
times = create_times(n_seconds, fs)
br_noise = sim.sim_powerlaw(n_seconds, fs, exponent)

###################################################################################################

# Plot the simulated data, in the time domain
plot_time_series(times, br_noise)

###################################################################################################

# Plot the simulated data, in the frequency domain
freqs, psd = spectral.compute_spectrum(br_noise, fs)
plot_power_spectra(freqs, psd)

###################################################################################################
#
# Simulate filtered brown noise
# -----------------------------
#
# However, brown noise has a lot of power in very slow frequnecies,
# whereas these slow frequencies are often not present or filtered out
# in neural signals. Therefore, we may desire our brown noise to be
# high-pass filtered. See that the resulting signal has much less
# low-frequency drift.
#
# Note this might not be ideal because it creates an "oscillation" at
# the cutoff frequency.
Beispiel #20
0
def main(argv):
    # defining basepaths
    basepath = '/Users/rdgao/Documents/data/CRCNS/fcx1/'
    rec_dirs = [
        f for f in np.sort(os.listdir(basepath)) if os.path.isdir(basepath + f)
    ]
    result_basepath = '/Users/rdgao/Documents/code/research/field-echos/results/fcx1/wakesleep/'

    if 'do_psds' in argv:
        print('Computing PSDs...')

        for cur_rec in range(len(rec_dirs)):
            print(rec_dirs[cur_rec])
            # compute PSDs
            psd_path = result_basepath + rec_dirs[cur_rec] + '/psd_spikes/'

            # load data
            ephys_data = io.loadmat(basepath + rec_dirs[cur_rec] + '/' +
                                    rec_dirs[cur_rec] + '_ephys.mat',
                                    squeeze_me=True)
            behav_data = pd.read_csv(basepath + rec_dirs[cur_rec] + '/' +
                                     rec_dirs[cur_rec] + '_wakesleep.csv',
                                     index_col=0)
            elec_region = np.unique(ephys_data['elec_regions'])[0]
            elec_shank_map = ephys_data['elec_shank_map']

            # some organization of spike meta datafile
            # NOTE that all this had to be done because I was an idiot and
            # organized the spikeinfo table and spikes in some dumb way
            # make spike info into df and access based on cell, and add end time
            df_spkinfo = pd.DataFrame(ephys_data['spike_info'],
                                      columns=ephys_data['spike_info_cols'])
            df_spkinfo.insert(
                len(df_spkinfo.columns) - 1, 'spike_start_ind',
                np.concatenate(
                    ([0], df_spkinfo['num_spikes_cumsum'].iloc[:-1].values)))
            df_spkinfo.rename(columns={'num_spikes_cumsum': 'spike_end_ind'},
                              inplace=True)

            # this is now a list of N arrays, where N is the number of cells
            #    now we can aggregate arbitrarily based on cell index
            spikes_list = utils.spikes_as_list(ephys_data['spiketrain'],
                                               df_spkinfo)

            # pooling across populations from the same shanks
            df_spkinfo_pooled = df_spkinfo.copy()
            for g_i, g in df_spkinfo.groupby(['shank', 'cell_EI_type']):
                # super python magic that collapses all the spikes of the same pop on the same shank into one array
                spikes_list.append(
                    np.sort(
                        np.hstack(
                            [spikes_list[c_i] for c_i, cell in g.iterrows()])))
                # update spike info dataframe
                df_pop = pd.DataFrame({
                    'shank': g['shank'].head(1),
                    'cell_EI_type': g['cell_EI_type'].head(1),
                    'num_spikes': g['num_spikes'].sum(),
                    'cell_id': 0
                })
                df_spkinfo_pooled = df_spkinfo_pooled.append(df_pop,
                                                             ignore_index=True)

            # pooling across entire recording
            for g_i, g in df_spkinfo.groupby(['cell_EI_type']):
                spikes_list.append(
                    np.sort(
                        np.hstack(
                            [spikes_list[c_i] for c_i, cell in g.iterrows()])))
                df_pop = pd.DataFrame({
                    'shank': 0,
                    'cell_id': 0,
                    'cell_EI_type': g['cell_EI_type'].head(1),
                    'num_spikes': g['num_spikes'].sum()
                })
                df_spkinfo_pooled = df_spkinfo_pooled.append(df_pop,
                                                             ignore_index=True)

            # save spikeinfo table to recording folder
            utils.makedir(psd_path, timestamp=False)
            df_spkinfo_pooled.to_csv(psd_path + '/spike_info.csv')

            ##### ------------- #####
            # compute PSDs across conditions and populations
            # individual cells
            dt = 0.005
            fs = 1 / dt

            # name, nperseg, noverlap, f_range, outlier_pct
            p_configs = [['2sec', int(2 * fs),
                          int(2 * fs * 4 / 5)],
                         ['5sec', int(5 * fs),
                          int(5 * fs * 4 / 5)]]

            behav_sub = behav_data[behav_data['Label'].isin(['Wake', 'Sleep'
                                                             ])].reset_index()
            behav_info = np.array(behav_sub)
            num_block, num_cell = len(behav_sub), len(spikes_list)
            for p_cfg in p_configs:
                print(p_cfg)
                saveout_path = psd_path + p_cfg[0]
                nperseg, noverlap = p_cfg[1:]

                psd_mean = np.zeros(
                    (num_block, num_cell, int(p_cfg[1] / 2 + 1)))
                psd_med = np.zeros(
                    (num_block, num_cell, int(p_cfg[1] / 2 + 1)))
                for cell, spikes in enumerate(spikes_list):
                    print(cell, end='|')
                    for block, cur_eps in behav_sub.iterrows():
                        spikes_eps = spikes[np.logical_and(
                            spikes >= cur_eps['Start'],
                            spikes < cur_eps['End'])]
                        t_spk, spikes_binned = utils.bin_spiketrain(
                            spikes_eps, dt, cur_eps[['Start', 'End']])
                        f_axis, psd_mean[block,
                                         cell, :] = spectral.compute_spectrum(
                                             spikes_binned,
                                             fs,
                                             method='welch',
                                             avg_type='mean',
                                             nperseg=nperseg,
                                             noverlap=noverlap)
                        f_axis, psd_med[block,
                                        cell, :] = spectral.compute_spectrum(
                                            spikes_binned,
                                            fs,
                                            method='welch',
                                            avg_type='median',
                                            nperseg=nperseg,
                                            noverlap=noverlap)

                # save PSDs and spike_info dataframe
                save_dict = {}
                for name in [
                        'psd_mean', 'psd_med', 'nperseg', 'noverlap', 'fs',
                        'behav_info', 'elec_region', 'elec_shank_map', 'f_axis'
                ]:
                    save_dict[name] = eval(name)
                utils.makedir(saveout_path, timestamp=False)
                np.savez(file=saveout_path + '/psds.npz', **save_dict)

    if 'do_fooof' in argv:
        fooof_settings = [['fixed', 2, (.5, 80)], ['fixed', 1, (.5, 5)],
                          ['fixed', 1, (10, 20)], ['fixed', 1, (30, 80)]]

        for cur_rec in range(len(rec_dirs)):
            print(rec_dirs[cur_rec])
            psd_path = result_basepath + rec_dirs[cur_rec] + '/psd_spikes/'
            df_spkinfo_pooled = pd.read_csv(psd_path + '/spike_info.csv',
                                            index_col=0)

            # grab only the aggregate cells
            df_pops = df_spkinfo_pooled[df_spkinfo_pooled['cell_id'] == 0]
            df_pops.to_csv(psd_path + '/pop_spike_info.csv')

            for psd_win in ['2sec/', '5sec/']:
                psd_folder = psd_path + psd_win
                psd_data = np.load(psd_folder + 'psds.npz')
                for psd_mode in ['psd_mean']:
                    psd_spikes = psd_data[psd_mode][:, df_pops.index.values, :]
                    if np.any(np.isinf(np.log(psd_spikes[:, :, 0]))):
                        # if any PSDs are 0s, set it to ones
                        print('Null PSDs found.')
                        zero_inds = np.where(
                            np.isinf(np.log(psd_spikes[:, :, 0])))
                        psd_spikes[zero_inds] = 1.

                    fg_all = []
                    for f_s in fooof_settings:
                        fg = FOOOFGroup(aperiodic_mode=f_s[0],
                                        max_n_peaks=f_s[1],
                                        peak_width_limits=(5, 20))
                        fgs = fit_fooof_group_3d(fg,
                                                 psd_data['f_axis'],
                                                 psd_spikes,
                                                 freq_range=f_s[2])
                        fg_all = combine_fooofs(fgs)
                        fooof_savepath = utils.makedir(psd_folder,
                                                       '/fooof/' + psd_mode +
                                                       '/',
                                                       timestamp=False)
                        fg_all.save('fg_%s_%ipks_%i-%iHz' %
                                    (f_s[0], f_s[1], f_s[2][0], f_s[2][1]),
                                    fooof_savepath,
                                    save_results=True,
                                    save_settings=True)

    print('Done.')
Beispiel #21
0
def compute_irasa(sig,
                  fs,
                  f_range=None,
                  hset=None,
                  thresh=None,
                  **spectrum_kwargs):
    """Separate aperiodic and periodic components using IRASA.

    Parameters
    ----------
    sig : 1d array
        Time series.
    fs : float
        The sampling frequency of sig.
    f_range : tuple, optional
        Frequency range to restrict the analysis to.
    hset : 1d array, optional
        Resampling factors used in IRASA calculation.
        If not provided, defaults to values from 1.1 to 1.9 with an increment of 0.05.
    thresh : float, optional
        A relative threshold to apply when separating out periodic components.
        The threshold is defined in terms of standard deviations of the original spectrum.
    spectrum_kwargs : dict
        Optional keywords arguments that are passed to `compute_spectrum`.

    Returns
    -------
    freqs : 1d array
        Frequency vector.
    psd_aperiodic : 1d array
        The aperiodic component of the power spectrum.
    psd_periodic : 1d array
        The periodic component of the power spectrum.

    Notes
    -----
    Irregular-Resampling Auto-Spectral Analysis (IRASA) is an algorithm ([1]_) that aims to
    separate 1/f and periodic components by resampling time series and computing power spectra,
    averaging away any activity that is frequency specific to isolate the aperiodic component.

    References
    ----------
    .. [1] Wen, H., & Liu, Z. (2016). Separating Fractal and Oscillatory Components in
           the Power Spectrum of Neurophysiological Signal. Brain Topography, 29(1), 13–26.
           DOI: https://doi.org/10.1007/s10548-015-0448-0
    """

    # Check & get the resampling factors, with rounding to avoid floating point precision errors
    hset = np.arange(1.1, 1.95, 0.05) if hset is None else hset
    hset = np.round(hset, 4)

    # The `nperseg` input needs to be set to lock in the size of the FFT's
    if 'nperseg' not in spectrum_kwargs:
        spectrum_kwargs['nperseg'] = int(4 * fs)

    # Calculate the original spectrum across the whole signal
    freqs, psd = compute_spectrum(sig, fs, **spectrum_kwargs)

    # Do the IRASA resampling procedure
    psds = np.zeros((len(hset), *psd.shape))
    for ind, h_val in enumerate(hset):

        # Get the up-sampling / down-sampling (h, 1/h) factors as integers
        rat = fractions.Fraction(str(h_val))
        up, dn = rat.numerator, rat.denominator

        # Resample signal
        sig_up = signal.resample_poly(sig, up, dn, axis=-1)
        sig_dn = signal.resample_poly(sig, dn, up, axis=-1)

        # Calculate the power spectrum, using the same params as original
        freqs_up, psd_up = compute_spectrum(sig_up, h_val * fs,
                                            **spectrum_kwargs)
        freqs_dn, psd_dn = compute_spectrum(sig_dn, fs / h_val,
                                            **spectrum_kwargs)

        # Calculate the geometric mean of h and 1/h
        psds[ind, :] = np.sqrt(psd_up * psd_dn)

    # Take the median resampled spectra, as an estimate of the aperiodic component
    psd_aperiodic = np.median(psds, axis=0)

    # Subtract aperiodic from original, to get the periodic component
    psd_periodic = psd - psd_aperiodic

    # Apply a relative threshold for tuning which activity is labeled as periodic
    if thresh is not None:
        sub_thresh = np.where(
            psd_periodic - psd_aperiodic < thresh * np.std(psd))[0]
        psd_periodic[sub_thresh] = 0
        psd_aperiodic[sub_thresh] = psd[sub_thresh]

    # Restrict spectrum to requested range
    if f_range:
        psds = np.array([psd_aperiodic, psd_periodic])
        freqs, (psd_aperiodic,
                psd_periodic) = trim_spectrum(freqs, psds, f_range)

    return freqs, psd_aperiodic, psd_periodic
# Plot the simulated data, in the time domain
plot_time_series(times, [osc_sine, osc_shape], ['rdsym='+str(.5), 'rdsym='+str(.3)])

###################################################################################################
#
# We can also compare these signals in the frequency.
#
# Notice that the asymmetric oscillation has strong harmonics resulting from the
# non-sinusoidal nature of the oscillation.
#

###################################################################################################

# Plot the simulated data, in the frequency domain
freqs_sine, psd_sine = compute_spectrum(osc_sine, fs)
freqs_shape, psd_shape = compute_spectrum(osc_shape, fs)

plot_power_spectra([freqs_sine, freqs_shape], [psd_sine, psd_shape])

###################################################################################################
# Simulate a Bursty Oscillation
# -----------------------------
#
# Sometimes we want to study oscillations that come and go, so it can be useful to simulate
# oscillations with this property.
#
# You can simulate bursty oscillations with :func:`~neurodsp.sim.periodic.sim_bursty_oscillation`.
#
# To control the bursitness of the simulated signal, you can control the probability
# that a burst will start or stop with each new cycle.
Beispiel #23
0
def main(argv):
    # defining basepaths
    basepath = '/Users/rdgao/Documents/data/CRCNS/fcx1/'
    rec_dirs = [f for f in np.sort(os.listdir(basepath)) if os.path.isdir(basepath+f)]
    result_basepath = '/Users/rdgao/Documents/code/research/field-echos/results/fcx1/wakesleep/'

    if 'do_psds' in argv:
        print('Computing PSDs...')

        for cur_rec in range(len(rec_dirs))[21:]:
            print(rec_dirs[cur_rec])
            # compute PSDs
            psd_path = result_basepath + rec_dirs[cur_rec] + '/psd/'

            # load data
            ephys_data = io.loadmat(basepath+rec_dirs[cur_rec]+'/'+rec_dirs[cur_rec]+'_ephys.mat', squeeze_me=True)
            behav_data = pd.read_csv(basepath+rec_dirs[cur_rec]+'/'+rec_dirs[cur_rec]+'_wakesleep.csv', index_col=0)

            # get some params
            nchan,nsamp = ephys_data['lfp'].shape
            fs = ephys_data['fs']
            ephys_data['t_lfp'] = np.arange(0,nsamp)/fs
            elec_region = np.unique(ephys_data['elec_regions'])[0]

            # get subset of behavior that marks wake and sleep
            behav_sub = behav_data[behav_data['Label'].isin(['Wake', 'Sleep'])]

            # name, nperseg, noverlap, f_range, outlier_pct
            p_configs = [['1sec', int(fs), int(fs/2), [0., 200.], 5],
                            ['5sec', int(fs*5), int(fs*4), [0., 200.], 5]]

            for p_cfg in p_configs:
                # parameter def
                print(p_cfg)
                saveout_path = psd_path+ p_cfg[0]
                nperseg, noverlap, f_range, outlier_pct = p_cfg[1:]

                psd_mean, psd_med,  = [], []
                for ind, cur_eps in behav_sub.iterrows():
                    # find indices of LFP that correspond to behavior
                    lfp_inds = np.where(np.logical_and(ephys_data['t_lfp']>=cur_eps['Start'],ephys_data['t_lfp']<cur_eps['End']))[0]

                    # compute mean and median welchPSD
                    p_squished = spectral.compute_spectrum(ephys_data['lfp'][:,lfp_inds], ephys_data['fs'], method='welch',avg_type='mean', nperseg=nperseg, noverlap=noverlap, f_range=f_range, outlier_pct=outlier_pct)
                    f_axis, cur_psd_mean = p_squished[0,:], p_squished[1::2,:] # work-around for ndsp currently squishing together the outputs
                    p_squished = spectral.compute_spectrum(ephys_data['lfp'][:,lfp_inds], ephys_data['fs'], method='welch',avg_type='median', nperseg=nperseg, noverlap=noverlap, f_range=f_range, outlier_pct=outlier_pct)
                    f_axis, cur_psd_med = p_squished[0,:], p_squished[1::2,:]

                    # append to list
                    psd_mean.append(cur_psd_mean)
                    psd_med.append(cur_psd_med)

                # collect, stack, and save out
                psd_mean, psd_med, behav_info = np.array(psd_mean), np.array(psd_med), np.array(behav_sub)
                save_dict = {}
                for name in ['psd_mean', 'psd_med','nperseg','noverlap','fs','outlier_pct', 'behav_info', 'elec_region', 'f_axis']:
                    save_dict[name] = eval(name)
                utils.makedir(saveout_path, timestamp=False)
                np.savez(file=saveout_path+'/psds.npz', **save_dict)

    if 'do_fooof' in argv:
        fooof_settings = [['knee', 4, (0.1,200)],
                            ['fixed', 4, (0.1,200)],
                            ['fixed', 2, (0.1,10)],
                            ['fixed', 2, (30,55)]]
        for cur_rec in range(len(rec_dirs)):
            print(rec_dirs[cur_rec])
            psd_path = result_basepath + rec_dirs[cur_rec] + '/psd/'
            for psd_win in ['1sec/', '5sec/']:
                psd_folder = psd_path+psd_win
                psd_data = np.load(psd_folder+'psds.npz')
                for psd_mode in ['psd_mean', 'psd_med']:
                    for f_s in fooof_settings:
                        fg = FOOOFGroup(aperiodic_mode=f_s[0], max_n_peaks=f_s[1])
                        fgs = fit_fooof_group_3d(fg, psd_data['f_axis'], psd_data[psd_mode], freq_range=f_s[2])
                        fg_all = combine_fooofs(fgs)
                        fooof_savepath = utils.makedir(psd_folder, '/fooof/'+psd_mode+'/', timestamp=False)
                        fg_all.save('fg_%s_%ipks_%i-%iHz'%(f_s[0],f_s[1],f_s[2][0],f_s[2][1]), fooof_savepath, save_results=True, save_settings=True)
Beispiel #24
0
                n_windows = len(window_starts_ep)

                for i in tqdm(range(n_windows)):
                    end_ind = ((window_starts_ep[i] + large_win_samps) if i <
                               (n_windows - 1) else -1)
                    first_elec = nwb.acquisition["ElectricalSeries"].data[
                        window_starts_ep[i]:end_ind, 0]
                    if np.sum(np.isnan(first_elec)) == 0:
                        dat = (nwb.acquisition["ElectricalSeries"].data[
                            window_starts_ep[i]:end_ind, :].T)

                        # Compute power using Welch's method
                        f_welch, spg = compute_spectrum(
                            dat,
                            fs,
                            method="welch",
                            avg_type="median",
                            nperseg=win_n_samps,
                            f_range=freq_range,
                        )

                        # Interpolate power to integer frequencies
                        f = interpolate.interp1d(f_welch, spg)
                        spg_new = f(freqs)

                        # Project power to ROI of interest
                        spg_proj = project_power(spg_new, proj_mats[part_ind],
                                                 good_rois[selected_roi])

                        # Append result to final list
                        if pows_sbj is None:
                            pows_sbj = spg_proj[np.newaxis, :].copy()
Beispiel #25
0
def compute_irasa(sig, fs=None, f_range=(1, 30), hset=None, **spectrum_kwargs):
    """Separate the aperiodic and periodic components using the IRASA method.

    Parameters
    ----------
    sig : 1d array
        Time series.
    fs : float
        The sampling frequency of sig.
    f_range : tuple or None
        Frequency range.
    hset : 1d array
        Resampling factors used in IRASA calculation.
        If not provided, defaults to values from 1.1 to 1.9 with an increment of 0.05.
    spectrum_kwargs : dict
        Optional keywords arguments that are passed to `compute_spectrum`.

    Returns
    -------
    freqs : 1d array
        Frequency vector.
    psd_aperiodic : 1d array
        The aperiodic component of the power spectrum.
    psd_periodic : 1d array
        The periodic component of the power spectrum.

    Notes
    -----
    Irregular-Resampling Auto-Spectral Analysis (IRASA) is described in Wen & Liu (2016).
    Briefly, it aims to separate 1/f and periodic components by resampling time series, and
    computing power spectra, effectively averaging away any activity that is frequency specific.

    References
    ----------
    Wen, H., & Liu, Z. (2016). Separating Fractal and Oscillatory Components in the Power Spectrum
    of Neurophysiological Signal. Brain Topography, 29(1), 13–26. DOI: 10.1007/s10548-015-0448-0
    """

    # Check & get the resampling factors, with rounding to avoid floating point precision errors
    hset = np.arange(1.1, 1.95, 0.05) if not hset else hset
    hset = np.round(hset, 4)

    # The `nperseg` input needs to be set to lock in the size of the FFT's
    if 'nperseg' not in spectrum_kwargs:
        spectrum_kwargs['nperseg'] = int(4 * fs)

    # Calculate the original spectrum across the whole signal
    freqs, psd = compute_spectrum(sig, fs, **spectrum_kwargs)

    # Do the IRASA resampling procedure
    psds = np.zeros((len(hset), *psd.shape))
    for ind, h_val in enumerate(hset):

        # Get the up-sampling / down-sampling (h, 1/h) factors as integers
        rat = fractions.Fraction(str(h_val))
        up, dn = rat.numerator, rat.denominator

        # Resample signal
        sig_up = signal.resample_poly(sig, up, dn, axis=-1)
        sig_dn = signal.resample_poly(sig, dn, up, axis=-1)

        # Calculate the power spectrum using the same params as original
        freqs_up, psd_up = compute_spectrum(sig_up, h_val * fs,
                                            **spectrum_kwargs)
        freqs_dn, psd_dn = compute_spectrum(sig_dn, fs / h_val,
                                            **spectrum_kwargs)

        # Geometric mean of h and 1/h
        psds[ind, :] = np.sqrt(psd_up * psd_dn)

    # Now we take the median resampled spectra, as an estimate of the aperiodic component
    psd_aperiodic = np.median(psds, axis=0)

    # Subtract aperiodic from original, to get the periodic component
    psd_periodic = psd - psd_aperiodic

    # Restrict spectrum to requested range
    if f_range:
        psds = np.array([psd_aperiodic, psd_periodic])
        freqs, (psd_aperiodic,
                psd_periodic) = trim_spectrum(freqs, psds, f_range)

    return freqs, psd_aperiodic, psd_periodic
Beispiel #26
0
# 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'])

###################################################################################################
#
# In the above, we can see that the the bandstop filter removes power in the filtered range,
# leaving a "dip" in the power spectrum. This dip causes issues with subsequent fitting.
#
            fontsize=16.0)
    return ax

gridsize = (3, 2)
fig = plt.figure(figsize=(20, 20))
ax1 = plt.subplot2grid(gridsize, (0, 0), colspan=2, rowspan=1)
ax2 = plt.subplot2grid(gridsize, (1, 0))
ax3 = plt.subplot2grid(gridsize, (1, 1))
ax4 = plt.subplot2grid(gridsize, (2, 0))
ax5 = plt.subplot2grid(gridsize, (2, 1))


qq = 0

######## ax2: PSD Welch
freq_mean, psd_mean = spectral.compute_spectrum(sig, fs, method='welch', avg_type='mean', nperseg=fs*2)
ax2.loglog(freq_mean[:118],psd_mean[:118])
add_titlebox(ax2, 'PSD Welch up to 118 Hz', 1)

######## ax5: stats 

# Initialize FOOOF model
fm = FOOOF(peak_width_limits=bw_lims, background_mode='knee', max_n_peaks=max_n_peaks)

# fit model
fm.fit(freq_mean, psd_mean, freq_range) 

# Central frequency, Amplitude, Bandwidth
peak_params = fm.peak_params_

#offset, knee, slope
Beispiel #28
0
def refit(fm, sig, fs, f_range, imf_kwargs=None, power_thresh=.2, energy_thresh=0., refit_ap=False):
    """Refit a power spectrum using EMD based parameter estimation.

    Parameters
    ----------
    fm : fooof.FOOOF
        FOOOF object containing results from fitting.
    sig : 1d array
        Voltage time series.
    fs : float
        Sampling rate, in Hz.
    f_range : tuple of [float, float]
        Frequency range to restrict power spectrum to.
    imf_kwargs : optional, default: None
        Optional keyword arguments for compute_emd. Includes:

        - max_imfs
        - sift_thresh
        - env_step_size
        - max_iters
        - energy_thresh
        - stop_method
        - sd_thresh
        - rilling_thresh

    power_thresh : float, optional, default: .2
        IMF power threshold as the mean power above the initial aperiodic fit.
    energy_thresh : float, optional, default: 0.
        Normalized HHT energy threshold to define oscillatory frequencies. This aids the removal of
        harmonic peaks if present.
    refit_ap : bool, optional, default: None
        Refits the aperiodic component when True. When False, the aperiodic component is defined
        from the intial specparam fit.

    Returns
    -------
    fm : fooof.FOOOF
        Updated FOOOF fit.
    imf : 2d array
        Intrinsic modes functions.
    pe_mask : 1d array
        Booleans to mark imfs above aperiodic fit.
    """

    fm_refit = fm.copy()

    if imf_kwargs is None:
        imf_kwargs = {'sd_thresh': .1}

    # Compute modes
    imf = compute_emd(sig, **imf_kwargs)

    # Convert spectra of mode timeseries
    _, powers_imf = compute_spectrum(imf, fs, f_range=f_range)

    freqs = fm_refit.freqs
    powers = fm_refit.power_spectrum
    powers_imf = np.log10(powers_imf)

    # Initial aperiodic fit
    powers_ap = fm_refit._ap_fit

    # Select superthreshold modes
    pe_mask = select_modes(powers_imf, powers_ap, power_thresh=power_thresh)

    # Refit periodic
    if not pe_mask.any():
        warnings.warn('No IMFs are above the intial aperiodic fit. '
                      'Returning the inital spectral fit.')
        return fm_refit, imf, pe_mask

    if energy_thresh > 0:

        # Limit frequency ranges to fit using HHT
        freqs_min, freqs_max = limit_freqs_hht(imf[pe_mask], freqs, fs,
                                               energy_thresh=energy_thresh)

        if freqs_min is None and freqs_max is None:
            warnings.warn('No superthreshold energy in HHT. '
                          'Returning the inital spectral fit.')
            return fm_refit, imf, np.zeros(len(pe_mask), dtype=bool)

        limits = (freqs_min, freqs_max)

        gauss_params = fit_gaussians(freqs, powers, powers_imf, powers_ap, pe_mask, limits)

    else:

        gauss_params = fit_gaussians(freqs, powers, powers_imf, powers_ap, pe_mask)

    if gauss_params is not None:

        fm_refit.peak_params_ = fm_refit._create_peak_params(gauss_params)
        fm_refit._peak_fit = gen_periodic(freqs, gauss_params.flatten())

    else:
        fm_refit.peak_params_ = None
        fm_refit._peak_fit = np.zeros_like(freqs)

    # Refit aperiodic
    if refit_ap:
        ap_params, ap_fit = refit_aperiodic(freqs, powers, fm_refit._peak_fit)
        fm_refit._ap_fit = ap_fit
        fm_refit.aperiodic_params_ = ap_params

    # Update attibutes
    fm_refit.gaussian_params_ = gauss_params
    fm_refit.fooofed_spectrum_ = fm_refit._peak_fit + fm_refit._ap_fit

    fm_refit._calc_r_squared()
    fm_refit._calc_error()

    return fm_refit, imf, pe_mask
Beispiel #29
0
# Setting for the simulation
exponent = -2

# Simulate powerlaw activity, specifically brown noise
times = create_times(n_seconds, fs)
br_noise = sim.sim_powerlaw(n_seconds, fs, exponent)

###################################################################################################

# Plot the simulated data, in the time domain
plot_time_series(times, br_noise)

###################################################################################################

# Plot the simulated data, in the frequency domain
freqs, psd = spectral.compute_spectrum(br_noise, fs)
plot_power_spectra(freqs, psd)

###################################################################################################
#
# Simulate Filtered 1/f Activity
# ------------------------------
#
# The powerlaw simulation function is also integrated with a filter. This can be useful
# if one wants to filter out the slow frequencies, as is often done with neural signals,
# to remove the very slow drifts that we see in the pure 1/f simulations.
#
# To filter a simulated powerlaw signal, simply pass in a filter range, and the filter will
# be applied to the simulated data before being returned. Here we will apply a high-pass filter.
#
# We can see that the resulting signal has much less low-frequency drift than the first one.
Beispiel #30
0
    },
    'sim_powerlaw': {
        'exponent': exp
    }
}

# Define the frequency range of interest for the analysis
f_range = (1, 40)

# Create the simulate time series
sig = sim_combined(n_seconds, fs, components)

###################################################################################################

# Compute the power spectrum of the simulated signal
freqs, psd = compute_spectrum(sig, fs, nperseg=4 * fs)

# Trim the power spectrum to the frequency range of interest
freqs, psd = trim_spectrum(freqs, psd, f_range)

# Plot the computed power spectrum
plot_power_spectra(freqs, psd, title="Original Spectrum")

###################################################################################################
#
# In the above spectrum, we can see a pattern of power across all frequencies, which reflects
# the 1/f activity, as well as a peak at 10 Hz, which represents the simulated oscillation.
#

###################################################################################################
# IRASA