def test_compute_features():
    """Test cycle-by-cycle feature computation"""

    # Load signal
    signal = np.load(data_path + 'sim_stationary.npy')
    Fs = 1000  # Sampling rate
    f_range = (6, 14)  # Frequency range

    # Compute cycle features
    df = features.compute_features(signal, Fs, f_range)

    # Check inverted signal gives appropriately opposite data
    df_opp = features.compute_features(-signal,
                                       Fs,
                                       f_range,
                                       center_extrema='T')

    np.testing.assert_allclose(df['sample_peak'], df_opp['sample_trough'])
    np.testing.assert_allclose(df['sample_last_trough'],
                               df_opp['sample_last_peak'])
    np.testing.assert_allclose(df['time_peak'], df_opp['time_trough'])
    np.testing.assert_allclose(df['time_rise'], df_opp['time_decay'])
    np.testing.assert_allclose(df['volt_rise'], df_opp['volt_decay'])
    np.testing.assert_allclose(df['volt_amp'], df_opp['volt_amp'])
    np.testing.assert_allclose(df['period'], df_opp['period'])
    np.testing.assert_allclose(df['time_rdsym'], 1 - df_opp['time_rdsym'])
    np.testing.assert_allclose(df['time_ptsym'], 1 - df_opp['time_ptsym'])
def test_compute_features():
    """Test cycle-by-cycle feature computation."""

    # Load signal
    sig = np.load(DATA_PATH + 'sim_stationary.npy')

    fs = 1000
    f_range = (6, 14)

    # Compute cycle features
    df = compute_features(sig, fs, f_range)

    # Check inverted signal gives appropriately opposite data
    df_opp = compute_features(-sig, fs, f_range, center_extrema='trough')

    np.testing.assert_allclose(df['sample_peak'], df_opp['sample_trough'])
    np.testing.assert_allclose(df['sample_last_trough'],
                               df_opp['sample_last_peak'])
    np.testing.assert_allclose(df['time_peak'], df_opp['time_trough'])
    np.testing.assert_allclose(df['time_rise'], df_opp['time_decay'])
    np.testing.assert_allclose(df['volt_rise'], df_opp['volt_decay'])
    np.testing.assert_allclose(df['volt_amp'], df_opp['volt_amp'])
    np.testing.assert_allclose(df['period'], df_opp['period'])
    np.testing.assert_allclose(df['time_rdsym'], 1 - df_opp['time_rdsym'])
    np.testing.assert_allclose(df['time_ptsym'], 1 - df_opp['time_ptsym'])
Exemple #3
0
    def fit(self, sig, fs, f_range):
        """Run the bycycle algorithm on a signal.

        Parameters
        ----------
        sig : 1d array
            Time series.
        fs : float
            Sampling rate, in Hz.
        f_range : tuple of (float, float)
            Frequency range for narrowband signal of interest (Hz).
        """

        if sig.ndim != 1:
            raise ValueError('Signal must be 1-dimensional.')

        # Add settings as attributes
        self.sig = sig
        self.fs = fs
        self.f_range = f_range

        self.df_features = compute_features(self.sig, self.fs, self.f_range,
                                            self.center_extrema,
                                            self.burst_method,
                                            self.burst_kwargs, self.thresholds,
                                            self.find_extrema_kwargs,
                                            self.return_samples)
Exemple #4
0
def test_plot_burst_detect_params(only_result):
    """Test plotting burst detection."""

    # Simulate oscillating time series
    n_seconds = 25
    fs = 1000
    freq = 10
    f_range = (6, 14)

    osc_kwargs = {
        'amplitude_fraction_threshold': 0,
        'amplitude_consistency_threshold': .5,
        'period_consistency_threshold': .5,
        'monotonicity_threshold': .8,
        'n_cycles_min': 3
    }

    sig = sim_oscillation(n_seconds, fs, freq)

    df = compute_features(sig, fs, f_range)

    fig = plot_burst_detect_params(sig,
                                   fs,
                                   df,
                                   osc_kwargs,
                                   plot_only_result=only_result)

    if not only_result:
        for param in fig:
            assert param is not None
    else:
        assert fig is not None
Exemple #5
0
def sim_args_comb():

    components = {
        'sim_bursty_oscillation': {
            'freq': FREQ
        },
        'sim_powerlaw': {
            'exp': 2
        }
    }

    sig = sim_combined(N_SECONDS, FS, components=components)

    df_shapes = compute_shape_features(sig, FS, F_RANGE)
    df_burst = compute_burst_features(df_shapes, sig)
    df_features = compute_features(sig, FS, F_RANGE)

    threshold_kwargs = {
        'amp_fraction_threshold': 0.,
        'amp_consistency_threshold': .5,
        'period_consistency_threshold': .5,
        'monotonicity_threshold': .5,
        'min_n_cycles': 3
    }

    yield {
        'sig': sig,
        'fs': FS,
        'f_range': F_RANGE,
        'df_features': df_features,
        'df_shapes': df_shapes,
        'df_burst': df_burst,
        'threshold_kwargs': threshold_kwargs
    }
Exemple #6
0
def sim_args():

    sig = sim_oscillation(N_SECONDS, FS, FREQ)

    df_shapes = compute_shape_features(sig, FS, F_RANGE)
    df_burst = compute_burst_features(df_shapes, sig)
    df_features = compute_features(sig, FS, F_RANGE)

    threshold_kwargs = {
        'amp_fraction_threshold': 0.,
        'amp_consistency_threshold': .5,
        'period_consistency_threshold': .5,
        'monotonicity_threshold': .5,
        'min_n_cycles': 3
    }

    yield {
        'sig': sig,
        'fs': FS,
        'f_range': F_RANGE,
        'df_features': df_features,
        'df_shapes': df_shapes,
        'df_burst': df_burst,
        'threshold_kwargs': threshold_kwargs
    }
Exemple #7
0
def test_detect_bursts_df_amp():
    """Test amplitude-threshold burst detection."""

    # Load signal
    signal = np.load(DATA_PATH + 'sim_bursting.npy')
    Fs = 1000
    f_range = (6, 14)
    signal = filt.lowpass_filter(signal, Fs, 30, N_seconds=.3,
                                 remove_edge_artifacts=False)

    # Compute cycle-by-cycle df without burst detection column
    df = features.compute_features(signal, 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 = burst.detect_bursts_df_amp(df, signal, 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
Exemple #8
0
def _proxy_2d(args, fs=None, f_range=None, return_samples=None):
    """Proxy function to map kwargs and 2d sigs together."""

    sig, kwargs = args[0], args[1:]

    return compute_features(sig,
                            fs=fs,
                            f_range=f_range,
                            return_samples=return_samples,
                            **kwargs[0])
def spindle_detect_bycycle():
    import sys
    import numpy as np
    import scipy as sp
    from scipy import io
    import matplotlib.pyplot as plt
    from bycycle.filt import lowpass_filter
    from bycycle.features import compute_features
    import pandas
    import hdf5storage

    signal_all = hdf5storage.loadmat(str(sys.argv[1]))
    signal_all = signal_all["lfp"]["trial"][0][0][0]
    refch_all = range(0, len(signal_all))

    Fs = 1000
    f_lowpass = 40
    N_seconds = .25

    for refch in refch_all:
        signal = signal_all[refch, :]
        signal_low = lowpass_filter(signal,
                                    Fs,
                                    f_lowpass,
                                    N_seconds=N_seconds,
                                    remove_edge_artifacts=False)

        from bycycle.burst import plot_burst_detect_params

        burst_kwargs = {
            'amplitude_fraction_threshold': float(sys.argv[2]),
            'amplitude_consistency_threshold': float(sys.argv[3]),
            'period_consistency_threshold': float(sys.argv[4]),
            'monotonicity_threshold': float(sys.argv[5]),
            'N_cycles_min': 4
        }

        #burst_kwargs = {'amplitude_fraction_threshold': .5,
        #            'amplitude_consistency_threshold': .3,
        #            'period_consistency_threshold': .5,
        #            'monotonicity_threshold': .7,
        #            'N_cycles_min': 4}

        df = compute_features(signal_low,
                              Fs, (8.5, 19),
                              burst_detection_kwargs=burst_kwargs,
                              hilbert_increase_N=True)

        df_csv = pandas.DataFrame.to_csv(df)

        df_dir = "spindle_detect\spindle_peaks" + str(refch) + ".csv"
        csv = open(df_dir, "w")
        csv.write(df_csv)
    return
Exemple #10
0
def sim_args():

    # Simulate oscillating time series
    n_seconds = 10
    fs = 500
    freq = 10
    f_range = (6, 14)

    sig = sim_oscillation(n_seconds, fs, freq)

    df = compute_features(sig, fs, f_range)

    yield {'df': df, 'sig': sig, 'fs': fs}
Exemple #11
0
def test_recompute_edges(sim_args_comb):

    # Grab sim arguments from fixture
    sig = sim_args_comb['sig']
    threshold_kwargs = sim_args_comb['threshold_kwargs']
    fs = sim_args_comb['fs']
    n_seconds = len(sig) / fs
    f_range = sim_args_comb['f_range']
    df_features = sim_args_comb['df_features']

    # Case 1: use the same thresholds should result in the same dataframe
    df_features_edges = recompute_edges(df_features, threshold_kwargs)
    assert (df_features['is_burst'].values == df_features['is_burst'].values
            ).all()

    # Case 2: ensure the number of burst increases when edge thresholds are lowered

    # This guarantees at least one non-burst cycle on the edge will be recomputed
    #   as a burst by duplicating the signal and adding zero-padding between
    sig_force_recomp = np.zeros(2 * len(sig) + fs)
    sig_force_recomp[:int(fs * n_seconds)] = sig
    sig_force_recomp[int(fs * n_seconds) + fs:] = sig

    df_features = compute_features(sig,
                                   fs,
                                   f_range,
                                   threshold_kwargs=threshold_kwargs)

    # Update thresholds to give all edge cycles is_burst = True, except for the first and
    #   last cycles, since these cycles contain np.nan values for consistency measures
    threshold_kwargs['amp_fraction_threshold'] = 0
    threshold_kwargs['amp_consistency_threshold'] = 0
    threshold_kwargs['period_consistency_threshold'] = 0
    threshold_kwargs['monotonicity_threshold'] = 0

    df_features_edges = recompute_edges(df_features, threshold_kwargs)

    # Ensure that at least one cycle was added to is_burst after recomputed
    is_burst_orig = len(np.where(df_features['is_burst'] == True)[0])
    is_burst_recompute = len(
        np.where(df_features_edges['is_burst'] == True)[0])

    assert is_burst_recompute > is_burst_orig
Exemple #12
0
def test_detect_bursts_cycles():
    """Test amplitude and period consistency burst detection"""

    # Load signal
    signal = np.load(data_path + 'sim_bursting.npy')
    Fs = 1000  # Sampling rate
    f_range = (6, 14)  # Frequency range

    signal = filt.lowpass_filter(signal,
                                 Fs,
                                 30,
                                 N_seconds=.3,
                                 remove_edge_artifacts=False)

    # Compute cycle-by-cycle df without burst detection column
    df = features.compute_features(signal,
                                   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_cycles = burst.detect_bursts_cycles(df, signal)

    # Make sure that burst detection is only boolean
    assert df_burst_cycles.dtypes['is_burst'] == 'bool'
    assert df_burst_cycles['is_burst'].mean() > 0
    assert df_burst_cycles['is_burst'].mean() < 1
    assert np.min([
        sum(1 for _ in group)
        for key, group in itertools.groupby(df_burst_cycles['is_burst']) if key
    ]) >= 3
Exemple #13
0
def test_detect_bursts_cycles():
    """Test amplitude and period consistency 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_cycles = detect_bursts_cycles(df, sig_filt)

    # Make sure that burst detection is only boolean
    assert df_burst_cycles.dtypes['is_burst'] == 'bool'
    assert df_burst_cycles['is_burst'].mean() > 0
    assert df_burst_cycles['is_burst'].mean() < 1
    assert np.min([sum(1 for _ in group) for key, group in \
        itertools.groupby(df_burst_cycles['is_burst']) if key]) >= 3
Exemple #14
0
    "monotonicity_threshold": 0.5,
    "N_cycles_min": 3,
}

bins = np.linspace(0, 1, 21)
bandwidth = (peak - 2, peak + 2)
Fs = int(raw.info["sfreq"])

raw_car.filter(3, None)
raw_ssd.filter(3, None)

i_comp = 0
df_ssd = compute_features(
    raw_ssd._data[i_comp],
    Fs,
    f_range=bandwidth,
    burst_detection_kwargs=osc_param,
    center_extrema="P",
)
df_ssd = df_ssd[df_ssd.is_burst]

i = 0
df = compute_features(
    raw_car._data[i],
    Fs,
    f_range=bandwidth,
    burst_detection_kwargs=osc_param,
    center_extrema="P",
)
df = df[df.is_burst]
# cycle. The main cycle-by-cycle function, :func:`~.compute_features`, returns a dataframe
# containing cycle features and sample locations of cyclepoints in the signal. Each entry or row
# in either dataframe is a cycle and each column is a property of that cycle (see table below). The
# four main features are:
#
# - amplitude (volt_amp) - average voltage change of the rise and decay
# - period (period) - time between consecutive troughs (or peaks, if default is changed)
# - rise-decay symmetry (time_rdsym) - fraction of the period in the rise period
# - peak-trough symmetry (time_ptsym) - fraction of the period in the peak period
#
# Note that a warning appears here because no burst detection parameters are provided. This is
# addressed in `section #4 <https://bycycle-tools.github.io/bycycle/auto_tutorials/plot_2_bycycle_algorithm.html#determine-parts-of-signal-in-oscillatory-burst>`_.

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

df_features = compute_features(sig, fs, f_theta)

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

df_features

####################################################################################################
#
# 4. Determine parts of signal in oscillatory burst
# -------------------------------------------------
# Note above that the signal is segmented into cycles and the dataframe provides properties for each
# segment of the signal. However, if no oscillation is apparent in the signal at a given time, the
# properties for these "cycles" are meaningless. Therefore, it is useful to have a binary indicator
# for each cycle that indicates whether the cycle being analyzed is truly part of an oscillatory
# burst or not. Recently, significant interest has emerged in detecting bursts in signals and
# analyzing their properties (see e.g. Feingold et al., PNAS, 2015). Nearly all efforts toward burst
Exemple #16
0
###############################################################################

f_alpha = (7, 13)  # Frequency band of interest
burst_kwargs = {
    'amplitude_fraction_threshold': .2,
    'amplitude_consistency_threshold': .5,
    'period_consistency_threshold': .5,
    'monotonicity_threshold': .8,
    'N_cycles_min': 3
}  # Tuned burst detection parameters

# Compute features for each signal and concatenate into single dataframe
dfs = []
for i in range(N):
    df = compute_features(signals[i],
                          Fs,
                          f_alpha,
                          burst_detection_kwargs=burst_kwargs)
    if i >= int(N / 2):
        df['group'] = 'patient'
    else:
        df['group'] = 'control'
    df['subject_id'] = i
    dfs.append(df)
df_cycles = pd.concat(dfs)

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

print(df_cycles.head())

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

# Set parameters for defining oscillatory bursts
osc_kwargs = {
    'amp_fraction_threshold': 0,
    'amp_consistency_threshold': .6,
    'period_consistency_threshold': .75,
    'monotonicity_threshold': .8,
    'n_cycles_min': 3
}

# Cycle-by-cycle analysis
df_ca1 = compute_features(ca1,
                          fs,
                          f_theta,
                          center_extrema='trough',
                          burst_detection_kwargs=osc_kwargs)

df_ec3 = compute_features(ec3,
                          fs,
                          f_theta,
                          center_extrema='trough',
                          burst_detection_kwargs=osc_kwargs)

# Limit analysis only to oscillatory bursts
df_ca1_cycles = df_ca1[df_ca1['is_burst']]
df_ec3_cycles = df_ec3[df_ec3['is_burst']]

####################################################################################################
#
Exemple #18
0
    subj = subj_idx[ii]
    ch = ch_idx[ii]
    ep = ep_idx[ii]
      
    # get phase providing band
    lower_phase = psd_peaks[subj][ch][ep][0] - (psd_peaks[subj][ch][ep][2] / 2)
    upper_phase = psd_peaks[subj][ch][ep][0] + (psd_peaks[subj][ch][ep][2] / 2)
    
    fs = 1000
    f_range = [lower_phase, upper_phase]
    f_lowpass = 55
    N_seconds = len(datastruct[subj][ch]) / fs / num_epochs - 2
    
    signal = lowpass_filter(datastruct[subj][ch][(ep*fs*epoch_len):((ep*fs*epoch_len)+fs*epoch_len)], fs, f_lowpass, N_seconds=N_seconds, remove_edge_artifacts=False)
    
    df = compute_features(signal, fs, f_range,  burst_detection_kwargs=burst_kwargs)
    
    is_burst = df['is_burst'].tolist()
    time_rdsym = df['time_rdsym'].to_numpy()
    time_ptsym = df['time_ptsym'].to_numpy()
    period_ch = df['period'].to_numpy()
    volt_amp_ch = df['volt_amp'].to_numpy()
    
    bursts.append(is_burst)
    rdsym.append(time_rdsym)
    ptsym.append(time_ptsym)
    period.append(period_ch)
    volt_amp.append(volt_amp_ch)

                
#%% 
    # get individual peaks
    peaks = helper.get_participant_peaks(participant, exp)

    for peak1 in peaks:

        patterns, filters = helper.load_ssd(participant_id, peak1)
        raw_ssd = ssd.apply_filters(raw, filters[:, :2])
        bandwidth = (float(peak1) - freq_width, float(peak1) + freq_width)
        i_comp = 0

        # compute features for SSD component
        df = compute_features(
            raw_ssd._data[i_comp],
            raw.info["sfreq"],
            f_range=bandwidth,
            burst_detection_kwargs=osc_param,
            center_extrema="T",
        )

        # save mean burst features
        df = df[df.is_burst]
        nr_bursts = len(df)
        df1 = df.mean()
        df1 = df1[features]
        df1["comp"] = i_comp
        df1["nr_bursts"] = nr_bursts

        df_file_name = "%s/bursts_%s_peak_%s.csv" % (
            results_folder,
            participant_id,
f_alpha = (7, 13)  # Frequency band of interest
burst_kwargs = {
    'amp_fraction_threshold': .2,
    'amp_consistency_threshold': .5,
    'period_consistency_threshold': .5,
    'monotonicity_threshold': .8,
    'n_cycles_min': 3
}  # Tuned burst detection parameters

# Compute features for each signal and concatenate into single dataframe
dfs = []
for idx in range(n_signals):

    df = compute_features(sigs[idx],
                          fs,
                          f_alpha,
                          burst_detection_kwargs=burst_kwargs)

    if idx >= int(n_signals / 2):
        df['group'] = 'patient'
    else:
        df['group'] = 'control'

    df['subject_id'] = idx
    dfs.append(df)

df_cycles = pd.concat(dfs)

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

df_cycles.head()
# 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)

fm.report()

#%% Run these data cycle by cycle
from bycycle.filt import lowpass_filter
from bycycle.features import compute_features

fs = 1000
f_range = [4, 8]
f_lowpass = 55
N_seconds = len(sig) / fs - 2

signal = lowpass_filter(sig,
                        fs,
                        f_lowpass,
                        N_seconds=N_seconds,
                        remove_edge_artifacts=False)

plt.plot(sig[0:1500])
plt.show()
df = compute_features(signal, fs, f_range)

plt.scatter(df.time_ptsym, df.time_rdsym)
Exemple #22
0
# in which each entry is a cycle and each column is a property of that cycle (see table below).
# There are columns to indicate where in the signal the cycle is located, but the four main features
# are:
#
# - amplitude (volt_amp) - average voltage change of the rise and decay
# - period (period) - time between consecutive troughs (or peaks, if default is changed)
# - rise-decay symmetry (time_rdsym) - fraction of the period in the rise period
# - peak-trough symmetry (time_ptsym) - fraction of the period in the peak period
#
# Note that a warning appears here because no burst detection parameters are provided. This is
# addressed in section #4

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

from bycycle.features import compute_features
df = compute_features(signal, Fs, f_theta)
print(df.head())

####################################################################################################
#
# 4. Determine parts of signal in oscillatory burst
# -------------------------------------------------
# Note above that the signal is segmented into cycles and the dataframe provides properties for each
# segment of the signal. However, if no oscillation is apparent in the signal at a given time, the
# properties for these "cycles" are meaningless. Therefore, it is useful to have a binary indicator
# for each cycle that indicates whether the cycle being analyzed is truly part of an oscillatory
# burst or not. Recently, significant interest has emerged in detecting bursts in signals and
# analyzing their properties (see e.g. Feingold et al., PNAS, 2015). Nearly all efforts toward burst
# detection relies on amplitude thresholds, but this can be disadvantageous because these algorithms
# will behave very differently on signals where oscillations are common versus rare.
#
Exemple #23
0
                   min_peak_amplitude=min_peak_amplitude)

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

        # run ByCycle
        # low pass filter
        lp_signal = lowpass_filter(signal,
                                   fs,
                                   f_lowpass,
                                   N_seconds=n_seconds_kernal,
                                   remove_edge_artifacts=False)

        # bycycle dataframe
        bycycle_df = compute_features(lp_signal,
                                      fs,
                                      [osc_freq_rand - 2, osc_freq_rand + 2],
                                      burst_detection_kwargs=burst_kwargs)

        # calculate PAC
        #calculating phase of theta
        phase_data = butter_bandpass_filter(signal, osc_freq_rand - 1,
                                            osc_freq_rand + 1, fs)
        phase_data_hilbert = hilbert(phase_data)
        phase_data_angle = np.angle(phase_data_hilbert)

        #calculating amplitude envelope of high gamma
        amp_data = butter_bandpass_filter(signal, 80, 250, fs)
        amp_data_hilbert = hilbert(amp_data)
        amp_data_abs = abs(amp_data_hilbert)

        PAC_values = circle_corr(phase_data_angle, amp_data_abs)
Exemple #24
0
def fit_bycycle(sig,
                fs,
                f_range,
                center_extrema='peak',
                burst_method='cycles',
                threshold_kwargs=None,
                find_extrema_kwargs=None,
                axis=0,
                verbose=False,
                n_jobs=-1):
    """A generalized Bycycle compute_features function to handle 1d, 2d, or 3d arrays.

    Parameters
    ----------
    sig : 1d array
        Time series.
    fs : float
        Sampling rate, in Hz.
    f_range : tuple of (float, float)
        Frequency range for narrowband signal of interest (Hz).
    center_extrema : {'peak', 'trough'}
        The center extrema in the cycle.
    burst_method : string, optional, default: 'cycles'
        Method for detecting bursts.
    threshold_kwargs : dict, optional, default: None
        Feature thresholds for cycles to be considered bursts.
    find_extrema_kwargs : dict, optional, default: None
        Keyword arguments for function to find peaks an troughs (``find_extrema``)
        to change filter Parameters or boundary. By default, it sets the filter length to three
        cycles of the low cutoff frequency (``f_range[0]``).
    n_jobs : int, optional, default: -1
        The number of jobs, one per cpu, to compute features in parallel.
    verbose : bool, optional, default: False
        Suppress warnings when False.

    Returns
    -------
    df_features : pandas.DataFrame
        A dataframe containing cycle features.

    Notes
    -----
    See bycycle documentation for more details.
    """

    if not verbose:
        warnings.simplefilter("ignore")

    threshold_kwargs = {} if not threshold_kwargs else threshold_kwargs
    find_extrema_kwargs = {} if not find_extrema_kwargs else find_extrema_kwargs

    compute_kwargs = dict(center_extrema=center_extrema,
                          burst_method=burst_method,
                          threshold_kwargs=threshold_kwargs,
                          find_extrema_kwargs=find_extrema_kwargs)

    if sig.ndim == 1:

        df_features = compute_features(sig, fs, f_range, **compute_kwargs)

    elif sig.ndim == 2:

        df_features = compute_features_2d(
            sig,
            fs,
            f_range,
            compute_features_kwargs=compute_kwargs,
            axis=axis,
            n_jobs=n_jobs)

    elif sig.ndim == 3:

        df_features = compute_features_3d(
            sig,
            fs,
            f_range,
            compute_features_kwargs=compute_kwargs,
            axis=axis,
            n_jobs=n_jobs)

    else:
        raise ValueError(
            'The sig argument must specify a 1d, 2d, or 3d array.')

    warnings.simplefilter("always")

    return df_features
Exemple #25
0
# Here we use the bycycle compute_features function to compute the cycle-by-
# cycle features of the three signals.
#

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

# Set parameters for defining oscillatory bursts
threshold_kwargs = {'amp_fraction_threshold': 0.3,
                    'amp_consistency_threshold': 0.4,
                    'period_consistency_threshold': 0.5,
                    'monotonicity_threshold': 0.8,
                    'min_n_cycles': 3}

# Cycle-by-cycle analysis
dfs = dict()
dfs['pac'] = compute_features(sig_pac, fs, f_beta, center_extrema='trough',
                              threshold_kwargs=threshold_kwargs, return_samples=False)

dfs['spurious'] = compute_features(sig_spurious_pac, fs, f_beta, center_extrema='trough',
                                   threshold_kwargs=threshold_kwargs, return_samples=False)

dfs['no_pac'] = compute_features(sig_no_pac, fs, f_beta, center_extrema='trough',
                                 threshold_kwargs=threshold_kwargs, return_samples=False)


####################################################################################################
#
# Plot feature distributions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# As shown in the feature distributions, the pac signal displays some peak-
# tough asymmetry as does the spurious pac signal.
        BW = features_df['BW'][ii]
        
        # only for now, delete with new FOOOF
        if BW < 5:
            phase_providing_band= [(CF - (BW/2))-1,  (CF + (BW/2))+1]
        else: 
            phase_providing_band= [(CF - (BW/2)),  (CF + (BW/2))]
               
        subj = features_df['subj'][ii]
        ch = features_df['ch'][ii]
        ep = features_df['ep'][ii]
        data = datastruct[subj][ch][ep]
        
        signal = lowpass_filter(data, fs, f_lowpass, N_seconds=N_seconds, remove_edge_artifacts=False)
        
        bycycle_df = compute_features(signal, fs, phase_providing_band, burst_detection_kwargs=burst_kwargs)
#
#        plot_burst_detect_params(signal, fs, bycycle_df,
#                         burst_kwargs, tlims=(0, 5), figsize=(16, 3), plot_only_result=True)
#
#        plot_burst_detect_params(signal, fs, bycycle_df,
#                         burst_kwargs, tlims=(0, 5), figsize=(16, 3))
        
        # find biggest length with no violations   

        # We have to ensure that the index is sorted
        bycycle_df.sort_index(inplace=True)
        # Resetting the index to create a column
        bycycle_df.reset_index(inplace=True)
        
        # create dataframe that only accumulates the true bursts
def identify_bursts(monkey, days, f_beta, f_lowpass, Fs):
    '''
    :param monkey: Satan or Risette
    :param days: range of days, starts at 1
    :param f_beta: frequency range of interest, e.g. (4,40)
    :param f_lowpass: integer for lowpass filter, e.g. 40
    :param Fs: samplting frequency
    :param n_bins: number of bins for time analysis, e.g. 8
    :return:
    '''
    # ------------------------------------------------------------------------
    # 1. import packages
    # ------------------------------------------------------------------------
    import numpy as np
    from bycycle.filt import lowpass_filter
    import pandas as pd
    pd.options.display.max_columns = 50
    from bycycle.filt import bandpass_filter
    from bycycle.cyclepoints import _fzerorise, _fzerofall, find_extrema
    from bycycle.features import compute_features
    # ------------------------------------------------------------------------
    #  Define parameters
    # ------------------------------------------------------------------------
    length_trial = 1564
    # ------------------------------------------------------------------------
    # 2. load and select data
    # ------------------------------------------------------------------------
    df_all = []
    for d in days:
        print('Identifying bursts for day', d)
        data = get_data(monkey, d)
        n_trials = data.shape[1] // length_trial
        n_el = data.shape[0]
        # ------------------------------------------------------------------------
        # 3. Prepare data
        # ------------------------------------------------------------------------
        for el in range(n_el):
            # initialize condition array per electrode
            signals_raw = []  # will contain all trials as array
            # separate into trials
            for trial in range(n_trials):
                signals_raw.append(data[el,
                                        trial * length_trial:((trial + 1) *
                                                              length_trial)])
            # lowpass filter
            # aim: remove slow transients or high frequency activity that may interfer with peak and trough identification
            signals_filtered = []
            for signals_raw_trial in signals_raw:
                signals_filtered.append(
                    lowpass_filter(signals_raw_trial,
                                   Fs,
                                   f_lowpass,
                                   N_seconds=.2,
                                   remove_edge_artifacts=False))
        # ------------------------------------------------------------------------
        # 4. cycle feature computation
        # ------------------------------------------------------------------------
            burst_kwargs = {
                'amplitude_fraction_threshold': .3,
                'amplitude_consistency_threshold': .4,
                'period_consistency_threshold': .5,
                'monotonicity_threshold': .8,
                'N_cycles_min': 3
            }
            dfs = []
            for trial, signal_filtered_narrow_trial in enumerate(
                    signals_filtered):
                df = compute_features(signal_filtered_narrow_trial,
                                      Fs,
                                      f_beta,
                                      burst_detection_kwargs=burst_kwargs)
                # only take bursts that are within 1000 ms delay
                df = df.loc[(df['sample_last_trough'] >= 392)
                            & (df['sample_next_trough'] <= 1172)]
                df = df.reset_index()
                df['trial'] = trial + 1  # Search
                df['electrode'] = el
                df['day'] = d
                dfs.append(df)
            df_cycles = pd.concat(dfs)  # make panda df
            df_all.append(df_cycles)
    df_all = pd.concat(df_all)
    df_all.to_pickle(monkey + '_dfBurstRaw_' + str(f_beta))
    return df_all
Exemple #28
0
def compute_features_2d(sigs,
                        fs,
                        f_range,
                        compute_features_kwargs=None,
                        axis=0,
                        return_samples=True,
                        n_jobs=-1,
                        progress=None):
    """Compute shape and burst features for a 2 dimensional array of signals.

    Parameters
    ----------
    sigs : 2d array
        Voltage time series, i.e. (n_channels, n_samples) or (n_epochs, n_samples).
    fs : float
        Sampling rate, in Hz.
    f_range : tuple of (float, float)
        Frequency range for narrowband signal of interest, in Hz.
    compute_features_kwargs : dict or list of dict
        Keyword arguments used in :func:`~.compute_features`.
    axis : {0, None}
        Which axes to calculate features across:

        - ``axis=0`` : Iterates over each row/signal in an array independently (i.e. for each
          channel in (n_channels, n_timepoints)).
        - ``axis=None`` : Flattens rows/signals prior to computing features (i.e. across flatten
          epochs in (n_epochs, n_timepoints)).

    return_samples : bool, optional, default: True
        Whether to return a dataframe of cyclepoint sample indices.
    n_jobs : int, optional, default: -1
        The number of jobs to compute features in parallel.
    progress : {None, 'tqdm', 'tqdm.notebook'}
        Specify whether to display a progress bar. Uses 'tqdm', if installed.

    Returns
    -------
    dfs_features : list of pandas.DataFrame
        Dataframes containing shape and burst features for each cycle.
        Each dataframe is computed using the :func:`~.compute_features` function.

    Notes
    -----

    - The order of ``dfs_features`` corresponds to the order of ``sigs``. This list of dataframes
      may be reorganized into a single dataframe using :func:`~.flatten_dfs`.
    - When ``axis=None`` parallel computation may not be performed due to the requirement of
      flattening the array into one dimension.
    - If ``compute_features_kwargs`` is a dictionary, the same kwargs are applied applied across
      the first axis of ``sigs``. Otherwise, a list of dictionaries equal in length to the
      first axis of ``sigs`` is required to apply unique kwargs to each signal.
    - ``return_samples`` is controlled from the kwargs passed in this function. If
      ``return_samples`` is a key in ``compute_features_kwargs``, it's value will be ignored.

    Examples
    --------
    Compute the features of a 2d array (n_epochs=10, n_samples=5000) containing epoched data:

    >>> import numpy as np
    >>> from neurodsp.sim import sim_bursty_oscillation
    >>> fs = 500
    >>> sigs = np.array([sim_bursty_oscillation(10, fs, 10) for i in range(10)])
    >>> compute_kwargs = {'burst_method': 'amp', 'threshold_kwargs':{'burst_fraction_threshold': 1}}
    >>> dfs_features = compute_features_2d(sigs, fs, f_range=(8, 12), axis=None,
    ...                                   compute_features_kwargs=compute_kwargs)

    Compute the features of a 2d array in parallel using the same compute_features kwargs. Note each
    signal features are computed separately in this case, recommended for (n_channels, n_samples):

    >>> compute_kwargs = {'burst_method': 'amp', 'threshold_kwargs':{'burst_fraction_threshold': 1}}
    >>> dfs_features = compute_features_2d(sigs, fs, f_range=(8, 12), n_jobs=2, axis=0,
    ...                                   compute_features_kwargs=compute_kwargs)

    Compute the features of a 2d array in parallel using using individualized settings per signal to
    examine the effect of various amplitude consistency thresholds:

    >>> sigs =  np.array([sim_bursty_oscillation(10, fs, freq=10)] * 10)
    >>> compute_kwargs = [{'threshold_kwargs': {'amp_consistency_threshold': thresh*.1}}
    ...                   for thresh in range(1, 11)]
    >>> dfs_features = compute_features_2d(sigs, fs, f_range=(8, 12), return_samples=False,
    ...                                   n_jobs=2, compute_features_kwargs=compute_kwargs, axis=0)
    """

    # Check compute_features_kwargs
    kwargs = deepcopy(compute_features_kwargs)
    kwargs = np.array(kwargs) if isinstance(kwargs, list) else kwargs

    check_kwargs_shape(sigs, kwargs, axis)

    kwargs = {} if kwargs is None else kwargs
    kwargs = [kwargs] if isinstance(kwargs, dict) else list(kwargs)

    # Drop return_samples argument, as it is set directly in the function call
    for kwarg in kwargs:
        kwarg.pop('return_samples', None)

    n_jobs = cpu_count() if n_jobs == -1 else n_jobs

    if axis == 0:
        # Compute each signal independently and in paralllel
        with Pool(processes=n_jobs) as pool:

            if len(kwargs) > 1:
                # Map iterable sigs and kwargs together
                mapping = pool.imap(
                    partial(_proxy_2d,
                            fs=fs,
                            f_range=f_range,
                            return_samples=return_samples), zip(sigs, kwargs))

            else:
                # Only map sigs, kwargs are the same for each mapping
                mapping = pool.imap(
                    partial(compute_features,
                            fs=fs,
                            f_range=f_range,
                            return_samples=return_samples,
                            **kwargs[0]), sigs)

            dfs_features = list(progress_bar(mapping, progress, len(sigs)))

    elif axis is None:
        # Compute features after flattening the 2d array (i.e. calculated across a 1d signal)
        sig_flat = sigs.flatten()

        center_extrema = kwargs[0].pop('center_extrema', 'peak')

        df_flat = compute_features(sig_flat,
                                   fs=fs,
                                   f_range=f_range,
                                   return_samples=True,
                                   center_extrema=center_extrema,
                                   **kwargs[0])

        dfs_features = epoch_df(df_flat, len(sig_flat), len(sigs[0]))

        # Apply different thresholds if specified
        if len(kwargs) > 0:

            for idx, compute_kwargs in enumerate(kwargs):

                burst_method = compute_kwargs.pop('burst_method', 'cycles')
                thresholds = compute_kwargs.pop('threshold_kwargs', {})
                center_extrema_next = compute_kwargs.pop(
                    'center_extrema', None)

                if idx > 0 and center_extrema_next is not None \
                    and center_extrema_next != center_extrema:

                    warnings.warn('''
                        The same center extrema must be used when axis is None and
                        compute_features_kwargs is a list. Proceeding using the first
                        center_extrema: {extrema}.'''.format(
                        extrema=center_extrema))

                if burst_method == 'cycles':
                    dfs_features[idx] = detect_bursts_cycles(
                        dfs_features[idx], **thresholds)

                elif burst_method == 'amp':
                    dfs_features[idx] = detect_bursts_amp(
                        dfs_features[idx], **thresholds)

    else:
        raise ValueError("The axis kwarg must be either 0 or None.")

    return dfs_features
    # make Laplacian derivation
    raw = helper.create_laplacian(laplacians, raw)

    # apply broadband filter
    raw.filter(fmin, fmax)
    is_good = helper.return_good_segments(subject, raw)

    for j, channel in enumerate(raw.ch_names):
        print(subject, channel)
        df_file_name = '%s/bursts_%s.csv' % (results_dir, channel)
        data = raw.get_data()[j]

        Fs = raw.info['sfreq']
        df = compute_features(data,
                              Fs,
                              f_range=bandwidth,
                              burst_detection_kwargs=osc_param)

        # check if sample_peak in artefactual segment and exclude
        for i_burst in range(len(df)):
            if df.iloc[i_burst].is_burst:
                sample = df.iloc[i_burst].sample_peak
                if not (is_good[sample]):
                    df.at[i_burst, 'is_burst'] = False

        # transform period in burst frequency
        df = df.rename(columns={'period': 'frequency'})
        df['frequency'] = df.frequency.apply(lambda x: fs / x)

        # save dataframe to file
        df.to_csv(df_file_name, index=False)
# plt.xlim(tlim)
# plt.title('bandpass - rise/decay midpoints - trial 1')
# plt.show()

plt.figure(figsize=(8, 6))
plt.plot(t[tidx], signal_low[tidx], 'k')
plt.plot(t[tidxPs], signal_low[tidxPs], 'b.', ms=10)
plt.plot(t[tidxTs], signal_low[tidxTs], 'r.', ms=10)
plt.plot(t[tidxDs], signal_low[tidxDs], 'm.', ms=10)
plt.plot(t[tidxRs], signal_low[tidxRs], 'g.', ms=10)
plt.xlim(tlim)
plt.title('lowpass - peak/trough and rise/decay midpoints - subject 2')
plt.show()

from bycycle.features import compute_features
df = compute_features(signal_low, Fs, f_theta)
features_mat = df.head()  # matrix with all relevant values
print(features_mat)
#np.save('lp_feature_matrix_trial2',features_mat)
print(features_mat['sample_last_trough'])
print(features_mat['sample_next_trough'])
print(features_mat['period'])

#visualizing burst detection settings
from bycycle.burst import plot_burst_detect_params
burst_kwargs = {
    'amplitude_fraction_threshold': 0,
    'amplitude_consistency_threshold': .2,
    'period_consistency_threshold': .45,
    'monotonicity_threshold': .7,
    'N_cycles_min': 3