Network Response#

In this tutorial we will cover how to do statistical significance testing for a ‘network response’. This is the average state/mode time course epoched around events of interest. We test is the response is significantly different from zero.

Get network response#

First we need to get the network response for each subject. This is the epoched state/mode time course averaged over trials, which would be a (subjects, states/modes, time) array. In this tutorial, we will simulate this.

import numpy as np

n_subjects = 20
n_time = 250
n_networks = 6

network_response = np.random.normal(size=(n_subjects, n_time, n_networks))

# Add non-zero network response for the first network for time points 30-80
network_response[:, 30:80, 0] += 1.5

# Add non-zero network response for the second network for time points 80-100
network_response[:, 80:100, 1] += 2

Statistical significance testing#

osl-dynamics has the analysis.statistics.evoked_response_max_stat_perm function for doing GLM permutation stats testing see if a value is significantly different from zero. This function uses the maximum test statistic to control for multiple comparisons across the time points and networks.

from osl_dynamics.analysis import statistics

pvalues = statistics.evoked_response_max_stat_perm(
    network_response,
    n_perm=1000,
    n_jobs=4,
)
print(pvalues.shape)
QUEUEING TASKS | Running permutations:   0%|          | 0/1000 [00:00<?, ?it/s]
QUEUEING TASKS | Running permutations:   8%|▊         | 82/1000 [00:00<00:01, 819.77it/s]
QUEUEING TASKS | Running permutations: 100%|██████████| 1000/1000 [00:00<00:00, 7774.69it/s]

PROCESSING TASKS | Running permutations:   0%|          | 0/1000 [00:00<?, ?it/s]
PROCESSING TASKS | Running permutations:   2%|▏         | 19/1000 [00:00<00:05, 184.63it/s]
PROCESSING TASKS | Running permutations:   5%|▌         | 53/1000 [00:00<00:03, 271.43it/s]
PROCESSING TASKS | Running permutations:   8%|▊         | 81/1000 [00:00<00:03, 268.06it/s]
PROCESSING TASKS | Running permutations:  11%|█▏        | 114/1000 [00:00<00:03, 289.71it/s]
PROCESSING TASKS | Running permutations:  15%|█▍        | 146/1000 [00:00<00:02, 298.29it/s]
PROCESSING TASKS | Running permutations:  18%|█▊        | 181/1000 [00:00<00:02, 314.50it/s]
PROCESSING TASKS | Running permutations:  21%|██▏       | 214/1000 [00:00<00:02, 316.42it/s]
PROCESSING TASKS | Running permutations:  25%|██▍       | 247/1000 [00:00<00:02, 317.74it/s]
PROCESSING TASKS | Running permutations:  28%|██▊       | 282/1000 [00:00<00:02, 324.93it/s]
PROCESSING TASKS | Running permutations:  32%|███▏      | 315/1000 [00:01<00:02, 320.33it/s]
PROCESSING TASKS | Running permutations:  35%|███▌      | 353/1000 [00:01<00:01, 337.28it/s]
PROCESSING TASKS | Running permutations:  39%|███▉      | 389/1000 [00:01<00:01, 342.70it/s]
PROCESSING TASKS | Running permutations:  43%|████▎     | 426/1000 [00:01<00:01, 346.90it/s]
PROCESSING TASKS | Running permutations:  46%|████▌     | 461/1000 [00:01<00:01, 344.09it/s]
PROCESSING TASKS | Running permutations:  50%|████▉     | 498/1000 [00:01<00:01, 351.30it/s]
PROCESSING TASKS | Running permutations:  53%|█████▎    | 534/1000 [00:01<00:01, 352.77it/s]
PROCESSING TASKS | Running permutations:  57%|█████▋    | 570/1000 [00:01<00:01, 349.30it/s]
PROCESSING TASKS | Running permutations:  60%|██████    | 605/1000 [00:01<00:01, 340.25it/s]
PROCESSING TASKS | Running permutations:  64%|██████▍   | 640/1000 [00:01<00:01, 317.28it/s]
PROCESSING TASKS | Running permutations:  67%|██████▋   | 673/1000 [00:02<00:01, 316.42it/s]
PROCESSING TASKS | Running permutations:  70%|███████   | 705/1000 [00:02<00:00, 311.31it/s]
PROCESSING TASKS | Running permutations:  74%|███████▎  | 737/1000 [00:02<00:00, 300.88it/s]
PROCESSING TASKS | Running permutations:  77%|███████▋  | 770/1000 [00:02<00:00, 308.28it/s]
PROCESSING TASKS | Running permutations:  80%|████████  | 801/1000 [00:02<00:00, 298.47it/s]
PROCESSING TASKS | Running permutations:  83%|████████▎ | 831/1000 [00:02<00:00, 298.16it/s]
PROCESSING TASKS | Running permutations:  86%|████████▌ | 861/1000 [00:02<00:00, 294.28it/s]
PROCESSING TASKS | Running permutations:  89%|████████▉ | 891/1000 [00:02<00:00, 295.06it/s]
PROCESSING TASKS | Running permutations:  92%|█████████▏| 921/1000 [00:02<00:00, 291.06it/s]
PROCESSING TASKS | Running permutations:  95%|█████████▌| 951/1000 [00:03<00:00, 285.37it/s]
PROCESSING TASKS | Running permutations:  98%|█████████▊| 980/1000 [00:03<00:00, 282.41it/s]
PROCESSING TASKS | Running permutations: 100%|██████████| 1000/1000 [00:03<00:00, 310.44it/s]

COLLECTING RESULTS | Running permutations:   0%|          | 0/1000 [00:00<?, ?it/s]
COLLECTING RESULTS | Running permutations: 100%|██████████| 1000/1000 [00:00<00:00, 461622.72it/s]
(250, 6)

Let’s plot the group average network response with the significant time points highlighted.

from osl_dynamics.utils import plotting

t = np.arange(n_time)
group_network_response = np.mean(network_response, axis=0)
fig, ax = plotting.plot_evoked_response(
    t,
    group_network_response,
    pvalues,
    x_label="Sample",
    y_label="Network Activation",
)
7 2 group network response

Note, this function also has a covariates argument that can be used to account for confounds.

Total running time of the script: (0 minutes 3.597 seconds)

Gallery generated by Sphinx-Gallery