Fluctuation Analyses

This notebook covers ‘fluctuation’ analyses, which examine patterns of fluctuations in time series.

This notebook covers:

  • The Hurst Exponent

  • Detrended Fluctuation Analysis

# Setup notebook state
from nbutils import setup_notebook; setup_notebook()
from neurodsp.sim import sim_powerlaw, sim_synaptic_current, sim_combined
from neurodsp.aperiodic.conversions import convert_exponent_alpha
from neurodsp.utils import set_random_seed
# Import custom project code
from apm.io import APMDB
from apm.run import run_sims, run_sims_load
from apm.methods import hurst, dfa
from apm.methods.settings import HURST_PARAMS, DFA_PARAMS
from apm.plts.sims import plot_ap_sims, plot_pe_sims, plot_sims_two
from apm.plts.settings import COLORS
from apm.plts.utils import figsaver
from apm.sim.examples import get_examples, check_examples
from apm.sim.settings import N_SIMS
from apm.sim.defs import SIM_ITERS

Settings

# Get current sampling rate
fs = SIM_ITERS.fs
# Add data specific settings to method settings
HURST_PARAMS['fs'] = fs
DFA_PARAMS['fs'] = fs
# Settings for saving figures
SAVE_FIG = True
FIGPATH = APMDB().figs_path / '22_fluctuations'

# Create helper function to manage figsaver settings
fsaver = figsaver(SAVE_FIG, FIGPATH)
# Set the random seed
set_random_seed(111)
# Define collection of example signals
examples = get_examples()

Hurst Exponent

The Hurst exponent is a method for measuring the self-similarity of a signal.

# Check Hurst exponent settings
HURST_PARAMS
{'fs': 250, 'n_scales': 10, 'min_scale': 0.1, 'max_scale': 2.0}

Hurst Exponent on Example Signals

Compute the Hurst exponent on some example signals.

# Define expected values for Hurst estimation
hurst_expected = {}
hurst_expected['powerlaw'] = 0.5
# Check the calculated hurst exponent, and the expected value
check_examples(examples, hurst, HURST_PARAMS, 'Hurst exponent', hurst_expected)
Computed Hurst exponent:
  powerlaw       : 	 0.5378 	0.50
  synaptic       : 	 0.5766
  knee           : 	 0.5475
  oscillation    : 	 -0.0033
  burst          : 	 nan
  combined       : 	 0.5027
  comb_burst     : 	 0.5049
  comb_peak      : 	 0.4780
/Users/tom/opt/anaconda3/envs/apm/lib/python3.9/site-packages/neurodsp/aperiodic/dfa.py:138: RuntimeWarning: divide by zero encountered in divide
  rs_win = np.ptp(segments, axis=0) / np.std(split_signal(sig, win_len).T, axis=0)

Hurst Exponent: Aperiodic Variations

# Run simulations calculating Hurst across exponents
hurst_sims_exp = run_sims(sim_powerlaw, SIM_ITERS['ap_exp'], hurst, HURST_PARAMS, N_SIMS)
# Run simulations calculating Hurst across exponents, with an oscillation
hurst_sims_comb = run_sims(sim_combined, SIM_ITERS['comb_exp'], hurst, HURST_PARAMS, N_SIMS)
# Plot Hurst exponent estimates across aperiodic variations
plot_ap_sims(hurst_sims_exp, hurst_sims_comb, 'Hurst Exponent', **fsaver('hurst_ap'))
../_images/22-Fluctuations_18_0.png

Hurst Exponent: Periodic Variations

# Run simulations calculating Hurst Exponent across oscillation frequencies
hurst_sims_freq = run_sims(sim_combined, SIM_ITERS['osc_freq'], hurst, HURST_PARAMS, N_SIMS)
# Run simulations calculating Hurst Exponent across oscillation power
hurst_sims_pow = run_sims(sim_combined, SIM_ITERS['osc_pow'], hurst, HURST_PARAMS, N_SIMS)
# Plot Hurst exponent estimates across periodic variations
plot_pe_sims(hurst_sims_freq, hurst_sims_pow, 'Hurst Exponent', **fsaver('hurst_pe'))
../_images/22-Fluctuations_22_0.png

Hurst Exponent: Knee Variations

# Run simulations calculating Hurst Exponent across different timescales
hurst_sims_tscales = run_sims(sim_synaptic_current, SIM_ITERS['syn_tscales'],
                              hurst, HURST_PARAMS, N_SIMS)
# Run simulations calculating Hurst Exponent across different knee parameters (sims from file)
hurst_sims_knee = run_sims_load('ap-knee-' + str(fs), hurst, HURST_PARAMS)
# Plot the estimated hurst exponent across different timescales & knees
plot_sims_two(SIM_ITERS['syn_tscales'].values, hurst_sims_tscales, 'Timescale',
              SIM_ITERS['kn_knee'].values, hurst_sims_knee, 'Knee Parameter',
              ylabel='Hurst Exponent', color=COLORS['KN'], **fsaver('hurst_kn_vars'))
../_images/22-Fluctuations_26_0.png

Hurst Exponent: Periodic Variations

Variations: oscillations bandwidth & burst probability.

# Run simulations calculating Hurst Exponent across peak bandwidth (sims from file)
hurst_sims_bw = run_sims_load('comb-bw-' + str(fs), hurst, HURST_PARAMS)
# Run simulations calculating Hurst Exponent across burst probabilities
hurst_sims_burst = run_sims(sim_combined, SIM_ITERS['comb_burst'],
                            hurst, HURST_PARAMS, N_SIMS)
# Plot the estimated hurst exponent across periodic variations
plot_sims_two(SIM_ITERS['peak_bw'].values, hurst_sims_bw, 'Bandwidth',
              SIM_ITERS['comb_burst'].values, hurst_sims_burst, 'Burst Probability',
              ylabel='Hurst Exponent', color=COLORS['BW'], **fsaver('hurst_pe_vars'))
../_images/22-Fluctuations_30_0.png

Detrended Fluctuation Analysis

Detrended Fluctuation Analysis is a method for measuring the self-similarity of a signal, and can be applied to measure properties of 1/f-distributed signals.

DFA measures an output value, \(\alpha\) which can be interpreted as:

  • \(\alpha\) < 0.5 anti-correlated signal

  • \(\alpha\) ~= 0.5: uncorrelated / white noise

  • \(\alpha\) > 0.5: correlated

    • For example: pink noise (\(1/f^1\)) ~= 1 & brown noise (\(1/f^2\)) ~= 1.5

The DFA measure \(\alpha\), should relate to the power spectrum exponent \(f^\chi\) as:

  • \(\chi\) = 2\(\alpha\) - 1 or

  • \(\alpha\) = (\(\chi\) + 1) / 2

Settings

# Check DFA settings
DFA_PARAMS
{'fs': 250, 'n_scales': 10, 'min_scale': 0.1, 'max_scale': 2.0, 'deg': 1}

DFA on Example Signals

# Define expected values for DFA exponent estimation
dfa_expected = {}
dfa_expected['powerlaw'] = convert_exponent_alpha(SIM_ITERS.params['ap']['exponent'])
# Check the computed DFA exponents on some example signals
check_examples(examples, dfa, DFA_PARAMS, 'DFA exponent', dfa_expected)
Computed DFA exponent:
  powerlaw       : 	 0.8164 	1.00
  synaptic       : 	 0.6384
  knee           : 	 0.6589
  oscillation    : 	 0.0078
  burst          : 	 0.0953
  combined       : 	 0.6173
  comb_burst     : 	 0.7456
  comb_peak      : 	 0.6221

DFA Simulations: Aperiodic Variations

# Calculate the expected DFA exponent value for each powerlaw exponent
expected_dfa = [convert_exponent_alpha(exp) for exp in SIM_ITERS._iters['ap_exp']['values']]
# Run simulations calculating DFA across exponents
dfa_sims_exp = run_sims(sim_powerlaw, SIM_ITERS['ap_exp'], dfa, DFA_PARAMS, N_SIMS)
# Run simulations calculating DFA across exponents, with an oscillation
dfa_sims_comb = run_sims(sim_combined, SIM_ITERS['comb_exp'], dfa, DFA_PARAMS, N_SIMS)

Plot Aperiodic & Combined Together

# Plot DFA measures across exponents
plot_ap_sims(dfa_sims_exp, dfa_sims_comb, 'DFA Exponent',
             expected=expected_dfa, **fsaver('dfa_ap'))
../_images/22-Fluctuations_42_0.png

DFA Simulations: Periodic Variations

# Run simulations calculating DFA across oscillation frequencies
dfa_sims_freq = run_sims(sim_combined, SIM_ITERS['osc_freq'], dfa, DFA_PARAMS, N_SIMS)
# Run simulations calculating DFA across oscillation power
dfa_sims_pow = run_sims(sim_combined, SIM_ITERS['osc_pow'], dfa, DFA_PARAMS, N_SIMS)
# Plot effect of oscillation variation on DFA exponent
plot_pe_sims(dfa_sims_freq, dfa_sims_pow, 'DFA Exponent', **fsaver('dfa_pe'))
../_images/22-Fluctuations_46_0.png

DFA Simulations: Knee Variations

# Run simulations calculating DFA Exponent across different timescales
dfa_sims_tscales = run_sims(sim_synaptic_current, SIM_ITERS['syn_tscales'], dfa, DFA_PARAMS, N_SIMS)
# Run simulations calculating DFA Exponent across different knee parameters (sims from file)
dfa_sims_knee = run_sims_load('ap-knee-' + str(fs), dfa, DFA_PARAMS)
# Plot the estimated DFA Exponent across different timescales & knees
plot_sims_two(SIM_ITERS['syn_tscales'].values, dfa_sims_tscales, 'Timescale',
              SIM_ITERS['kn_knee'].values, dfa_sims_knee, 'Knee Parameter',
              ylabel='DFA Exponent', color=COLORS['KN'], **fsaver('dfa_kn_vars'))
../_images/22-Fluctuations_50_0.png

DFA Simulations: Periodic Variations

# Run simulations calculating DFA Exponent across peak bandwidth (sims from file)
dfa_sims_bw = run_sims_load('comb-bw-' + str(fs), dfa, DFA_PARAMS)
# Run simulations calculating DFA Exponent across burst probabilities
dfa_sims_burst = run_sims(sim_combined, SIM_ITERS['comb_burst'], dfa, DFA_PARAMS, N_SIMS)
# Plot the estimated DFA Exponent across periodic variations
plot_sims_two(SIM_ITERS['peak_bw'].values, dfa_sims_bw, 'Bandwidth',
              SIM_ITERS['comb_burst'].values, dfa_sims_burst, 'Burst Probability',
              ylabel='DFA Exponent', color=COLORS['BW'], **fsaver('dfa_pe_vars'))
../_images/22-Fluctuations_54_0.png

Conclusions

Overall, we can see the following patterns in these simulations:

Hurst Exponent:

  • has a parabolic relationship with aperiodic exponent, maximal at an exponent of -1

  • is less affected by oscillation, increasing slightly with increasing frequency or power

Detrended Fluctuation Analysis:

  • the estimated DFA increases with increasing aperiodic exponent

    • the expected DFA results is well estimated across a range \(\chi\) from ~ 0 to 2

    • the empirical DFA becomes less close the expected for increasing exponent values

  • when an oscillation is present, the expected DFA value (for the 1/f) is decreases (is under-estimated)

  • there is a complex relationship between estimated DFA and oscillation frequency

  • estimated DFA decreases with increasing oscillation power