Complexity Measures

This notebooks runs complexity metrics on time series signals.

This notebook includes:

  • Hjorth parameters

  • Lempel-Ziv complexity

# Setup notebook state
from nbutils import setup_notebook; setup_notebook()
import numpy as np
from neurodsp.sim import sim_powerlaw, sim_synaptic_current, sim_combined
from neurodsp.utils import set_random_seed

from antropy import lziv_complexity
# Import custom project code
from apm.io import APMDB
from apm.run import run_sims_load
from apm.run import run_sims_parallel as run_sims
from apm.methods import hjorth_activity, hjorth_mobility, hjorth_complexity, lempelziv
from apm.methods.settings import HJA_PARAMS, HJM_PARAMS, HJC_PARAMS, LZ_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
# Settings for saving figures
SAVE_FIG = True
FIGPATH = APMDB().figs_path / '24_complexity'

# 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()

Hjorth Parameters

Hjorth parameters are statistical properties that can be used to characterize EEG data.

The Hjorth measures are:

  • activity: represents the signal power, computed as the variance of the time series

  • mobility: represents the mean frequency of the power spectrum

  • complexity: represents the change in frequency

# Check Hjorth measure parameters
print(HJM_PARAMS)
print(HJC_PARAMS)
{}
{}

Compute Hjorth Parameters on Examples Signals

Note that the simulated signals are normalized to a variance of one, which is why the Hjorth activity is the same for all.

Powerlaw Signal

# Check the calculated Hjorth activity values across some example signals
check_examples(examples, hjorth_activity, HJA_PARAMS, 'Hjorth Activity')
Computed Hjorth Activity:
  powerlaw       : 	 1.0000
  synaptic       : 	 1.0000
  knee           : 	 1.0000
  oscillation    : 	 1.0000
  burst          : 	 0.4933
  combined       : 	 1.0000
  comb_burst     : 	 1.0000
  comb_peak      : 	 1.0000
# Check the calculated Hjorth mobility values across some example signals
check_examples(examples, hjorth_mobility, HJM_PARAMS, 'Hjorth Mobility')
Computed Hjorth Mobility:
  powerlaw       : 	 0.7830
  synaptic       : 	 0.8149
  knee           : 	 0.5909
  oscillation    : 	 0.2507
  burst          : 	 0.2507
  combined       : 	 0.6530
  comb_burst     : 	 0.7008
  comb_peak      : 	 0.6630
# Check the calculated Hjorth mobility values across some example signals
check_examples(examples, hjorth_complexity, HJC_PARAMS, 'Hjorth Complexity')
Computed Hjorth Complexity:
  powerlaw       : 	 2.0246
  synaptic       : 	 1.8788
  knee           : 	 2.3371
  oscillation    : 	 1.0003
  burst          : 	 1.2211
  combined       : 	 2.3870
  comb_burst     : 	 2.2392
  comb_peak      : 	 2.3492

Hjorth Parameters: Aperiodic Variations

# Run simulations calculating Hjorth measures across exponents
hjm_sims_exp = run_sims(sim_powerlaw, SIM_ITERS['ap_exp'], hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_exp = run_sims(sim_powerlaw, SIM_ITERS['ap_exp'], hjorth_complexity, HJC_PARAMS, N_SIMS)
# Run simulations calculating Hjorth measures across exponents with an oscillation
hjm_sims_comb = run_sims(sim_combined, SIM_ITERS['comb_exp'], hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_comb = run_sims(sim_combined, SIM_ITERS['comb_exp'], hjorth_complexity, HJC_PARAMS, N_SIMS)
# Plot Hjorth mobility estimates across aperiodic variations
plot_ap_sims(hjm_sims_exp, hjm_sims_comb, 'Hjorth Mobility', **fsaver('hjm_ap'))
../_images/24-Complexity_20_0.png
# Plot Hjorth complexity estimates across aperiodic variations
plot_ap_sims(hjc_sims_exp, hjc_sims_comb, 'Hjorth Complexity', **fsaver('hjc_ap'))
../_images/24-Complexity_21_0.png

Hjorth Parameters: Periodic Variations

# Run simulations calculating Hjorth estimates across oscillation frequency
hjm_sims_freq = run_sims(\
    sim_combined, SIM_ITERS['osc_freq'], hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_freq = run_sims(\
    sim_combined, SIM_ITERS['osc_freq'], hjorth_complexity, HJC_PARAMS, N_SIMS)
# Run simulations calculating Hjorth estimates across oscillation power
hjm_sims_pow = run_sims(\
    sim_combined, SIM_ITERS['osc_pow'], hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_pow = run_sims(\
    sim_combined, SIM_ITERS['osc_pow'], hjorth_complexity, HJC_PARAMS, N_SIMS)
# Plot Hjorth mobility estimates across periodic variations
plot_pe_sims(hjm_sims_freq, hjm_sims_pow, 'Hjorth Mobility', **fsaver('hjm_pe'))
../_images/24-Complexity_25_0.png
# Plot Hjorth complexity estimates across periodic variations
plot_pe_sims(hjc_sims_freq, hjc_sims_pow, 'Hjorth Complexity',  **fsaver('hjc_pe'))
../_images/24-Complexity_26_0.png

Hjorth Parameters: Knee Variations

# Run simulations calculating Hjorth measures across different timescales
hjm_sims_tscales = run_sims(sim_synaptic_current, SIM_ITERS['syn_tscales'],
                            hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_tscales = run_sims(sim_synaptic_current, SIM_ITERS['syn_tscales'],
                            hjorth_complexity, HJC_PARAMS, N_SIMS)
# Run simulations calculating Hjorth measures across different knee parameters (sims from file)
hjm_sims_knee = run_sims_load('ap-knee-' + str(fs), hjorth_mobility, HJM_PARAMS)
hjc_sims_knee = run_sims_load('ap-knee-' + str(fs), hjorth_complexity, HJC_PARAMS)
# Plot the estimated Hjorth mobility across different timescales & knees
plot_sims_two(SIM_ITERS['syn_tscales'].values, hjm_sims_tscales, 'Timescale',
              SIM_ITERS['kn_knee'].values, hjm_sims_knee, 'Knee Parameter',
              ylabel='Hjorth Mobility', color=COLORS['KN'], **fsaver('hjm_kn_vars'))
../_images/24-Complexity_30_0.png
# Plot the estimated Hjorth complexity across different timescales & knees
plot_sims_two(SIM_ITERS['syn_tscales'].values, hjc_sims_tscales, 'Timescale',
              SIM_ITERS['kn_knee'].values, hjc_sims_knee, 'Knee Parameter',
              ylabel='Hjorth Complexity', color=COLORS['KN'], **fsaver('hjc_kn_vars'))
../_images/24-Complexity_31_0.png

Hjorth Parameters: Peak Variations

# Run simulations calculating Hjorth measures across peak bandwidth (sims from file)
hjm_sims_bw = run_sims_load('comb-bw-' + str(fs), hjorth_mobility, HJM_PARAMS)
hjc_sims_bw = run_sims_load('comb-bw-' + str(fs), hjorth_complexity, HJC_PARAMS)
# Run simulations calculating Hjorth measures across burst probabilities
hjm_sims_burst = run_sims(sim_combined, SIM_ITERS['comb_burst'],
                          hjorth_mobility, HJM_PARAMS, N_SIMS)
hjc_sims_burst = run_sims(sim_combined, SIM_ITERS['comb_burst'],
                          hjorth_complexity, HJC_PARAMS, N_SIMS)
# Plot the estimated Hjoth mobility across periodic variations
plot_sims_two(SIM_ITERS['peak_bw'].values, hjm_sims_bw, 'Bandwidth',
              SIM_ITERS['comb_burst'].values, hjm_sims_burst, 'Burst Probability',
              ylabel='Hjorth Mobility', color=COLORS['BW'], **fsaver('hjm_pe_vars'))
../_images/24-Complexity_35_0.png
# Plot the estimated Hjoth complexity across periodic variations
plot_sims_two(SIM_ITERS['peak_bw'].values, hjc_sims_bw, 'Bandwidth',
              SIM_ITERS['comb_burst'].values, hjc_sims_burst, 'Burst Probability',
              ylabel='Hjorth Complexity', color=COLORS['BW'], **fsaver('hjc_pe_vars'))
../_images/24-Complexity_36_0.png

Lempel-Ziv Complexity

Lempel-Ziv (LZ) complexity is a measure of the complexity of a time series that is sometimes applied to EEG data.

Note that the LZ algorithm is defined for binary sequences.

To apply this measure to neural time series, we binarize the signal by calculating whether each point is above or below the median of the time series.

# Check Lempel-Ziv complexity parameters
LZ_PARAMS
{'normalize': False}
# Example of applying LZ complexity to a binarized signal
bin_sig = np.array(examples['powerlaw'] > np.median(examples['powerlaw'])).astype(int)
lziv_complexity(bin_sig)
473

Calculating Lempel-Ziv Complexity on Example Signals

# Check Lempel-Ziv complexity measures on example signals
check_examples(examples, lempelziv, LZ_PARAMS, 'Lempel Ziv Complexity')
Computed Lempel Ziv Complexity:
  powerlaw       : 	 473.0000
  synaptic       : 	 489.0000
  knee           : 	 415.0000
  oscillation    : 	 5.0000
  burst          : 	 33.0000
  combined       : 	 417.0000
  comb_burst     : 	 443.0000
  comb_peak      : 	 422.0000

Aperiodic Variations

# Run simulations calculating LZ complexity across exponents
lz_sims_exp = run_sims(sim_powerlaw, SIM_ITERS['ap_exp'], lempelziv, LZ_PARAMS, N_SIMS)
# Run simulations calculating LZ complexity across exponents with an oscillation
lz_sims_comb = run_sims(sim_combined, SIM_ITERS['comb_exp'], lempelziv, LZ_PARAMS, N_SIMS)
# Plot Lempel-Ziv complexity estimates across aperiodic variations
plot_ap_sims(lz_sims_exp, lz_sims_comb, 'Lempel-Ziv Complexity', **fsaver('lz_ap'))
../_images/24-Complexity_45_0.png

Periodic Variations

# Run simulations calculating LZ complexity across oscillation frequency
lz_sims_freq = run_sims(sim_combined, SIM_ITERS['osc_freq'], lempelziv, LZ_PARAMS, N_SIMS)
# Run simulations calculating LZ complexity dimension across oscillation power
lz_sims_pow = run_sims(sim_combined, SIM_ITERS['osc_pow'], lempelziv, LZ_PARAMS, N_SIMS)
# Plot Lempel-Ziv complexity estimates across periodic variations
plot_pe_sims(lz_sims_freq, lz_sims_pow, 'Lempel-Ziv Complexity', **fsaver('lz_pe'))
../_images/24-Complexity_49_0.png

Knee Variations

# Run simulations calculating LZ Complexity across different timescales
lz_sims_tscales = run_sims(sim_synaptic_current, SIM_ITERS['syn_tscales'],
                           lempelziv, LZ_PARAMS, N_SIMS)
# Run simulations calculating LZ Complexity across different knee parameters (sims from file)
lz_sims_knee = run_sims_load('ap-knee-' + str(fs), lempelziv, LZ_PARAMS)
# Plot the estimated DFA Exponent across different timescales & knees
plot_sims_two(SIM_ITERS['syn_tscales'].values, lz_sims_tscales, 'Timescale',
              SIM_ITERS['kn_knee'].values, lz_sims_knee, 'Knee Parameter',
              ylabel='Lempel-Ziv Complexity', color=COLORS['KN'], **fsaver('lz_kn_vars'))
../_images/24-Complexity_53_0.png

Bandwidth Variations

# Run simulations calculating LZ Complexity across peak bandwidth (sims from file)
lz_sims_bw = run_sims_load('comb-bw-' + str(fs), lempelziv, LZ_PARAMS)
# Run simulations calculating LZ Complexity across burst probabilities
lz_sims_burst = run_sims(sim_combined, SIM_ITERS['comb_burst'], lempelziv, LZ_PARAMS, N_SIMS)
# Plot the estimated DFA Exponent across periodic variations
plot_sims_two(SIM_ITERS['peak_bw'].values, lz_sims_bw, 'Bandwidth',
              SIM_ITERS['comb_burst'].values, lz_sims_burst, 'Burst Probability',
              ylabel='Lempel-Ziv Complexity', color=COLORS['BW'], **fsaver('lz_pe_vars'))
../_images/24-Complexity_57_0.png

Conclusions

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

Hjorth Parameters:

  • mobility increases with decreasing aperiodic exponent (non-linearly) & increases with oscillation frequency & power

  • complexity decreases with decreasing aperiodic exponent (non-linearly) & decreases with oscillation frequency & power

Lempel-Ziv Complexity:

  • increases with decreasing aperiodic exponent, non-linearly

  • increases with increasing oscillation frequency, approximately linearly

  • decreases with increasing oscillation power, approximately linearly