Note
Go to the end to download the full example code.
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: 55%|█████▍ | 546/1000 [00:00<00:00, 5452.36it/s]
QUEUEING TASKS | Running permutations: 100%|██████████| 1000/1000 [00:00<00:00, 8817.31it/s]
PROCESSING TASKS | Running permutations: 0%| | 0/1000 [00:00<?, ?it/s]
PROCESSING TASKS | Running permutations: 2%|▏ | 19/1000 [00:00<00:05, 185.98it/s]
PROCESSING TASKS | Running permutations: 5%|▌ | 52/1000 [00:00<00:03, 252.86it/s]
PROCESSING TASKS | Running permutations: 8%|▊ | 82/1000 [00:00<00:03, 265.40it/s]
PROCESSING TASKS | Running permutations: 11%|█ | 112/1000 [00:00<00:03, 274.03it/s]
PROCESSING TASKS | Running permutations: 14%|█▍ | 140/1000 [00:00<00:03, 274.20it/s]
PROCESSING TASKS | Running permutations: 17%|█▋ | 168/1000 [00:00<00:03, 269.91it/s]
PROCESSING TASKS | Running permutations: 20%|█▉ | 196/1000 [00:00<00:02, 271.60it/s]
PROCESSING TASKS | Running permutations: 23%|██▎ | 226/1000 [00:00<00:02, 275.27it/s]
PROCESSING TASKS | Running permutations: 26%|██▌ | 255/1000 [00:00<00:02, 277.08it/s]
PROCESSING TASKS | Running permutations: 29%|██▊ | 286/1000 [00:01<00:02, 286.94it/s]
PROCESSING TASKS | Running permutations: 32%|███▏ | 315/1000 [00:01<00:02, 279.49it/s]
PROCESSING TASKS | Running permutations: 34%|███▍ | 345/1000 [00:01<00:02, 282.33it/s]
PROCESSING TASKS | Running permutations: 38%|███▊ | 375/1000 [00:01<00:02, 284.18it/s]
PROCESSING TASKS | Running permutations: 41%|████ | 409/1000 [00:01<00:01, 299.65it/s]
PROCESSING TASKS | Running permutations: 44%|████▍ | 440/1000 [00:01<00:01, 301.81it/s]
PROCESSING TASKS | Running permutations: 47%|████▋ | 471/1000 [00:01<00:01, 299.79it/s]
PROCESSING TASKS | Running permutations: 50%|█████ | 504/1000 [00:01<00:01, 306.87it/s]
PROCESSING TASKS | Running permutations: 54%|█████▎ | 535/1000 [00:01<00:01, 300.71it/s]
PROCESSING TASKS | Running permutations: 57%|█████▋ | 566/1000 [00:01<00:01, 301.66it/s]
PROCESSING TASKS | Running permutations: 60%|█████▉ | 597/1000 [00:02<00:01, 285.59it/s]
PROCESSING TASKS | Running permutations: 63%|██████▎ | 628/1000 [00:02<00:01, 291.78it/s]
PROCESSING TASKS | Running permutations: 66%|██████▌ | 658/1000 [00:02<00:01, 289.17it/s]
PROCESSING TASKS | Running permutations: 69%|██████▉ | 688/1000 [00:02<00:01, 287.32it/s]
PROCESSING TASKS | Running permutations: 72%|███████▏ | 719/1000 [00:02<00:00, 287.24it/s]
PROCESSING TASKS | Running permutations: 75%|███████▌ | 750/1000 [00:02<00:00, 292.23it/s]
PROCESSING TASKS | Running permutations: 78%|███████▊ | 783/1000 [00:02<00:00, 300.39it/s]
PROCESSING TASKS | Running permutations: 81%|████████▏ | 814/1000 [00:02<00:00, 301.27it/s]
PROCESSING TASKS | Running permutations: 85%|████████▍ | 847/1000 [00:02<00:00, 302.57it/s]
PROCESSING TASKS | Running permutations: 88%|████████▊ | 878/1000 [00:03<00:00, 293.65it/s]
PROCESSING TASKS | Running permutations: 91%|█████████ | 910/1000 [00:03<00:00, 298.61it/s]
PROCESSING TASKS | Running permutations: 94%|█████████▍| 940/1000 [00:03<00:00, 298.23it/s]
PROCESSING TASKS | Running permutations: 97%|█████████▋| 970/1000 [00:03<00:00, 289.14it/s]
PROCESSING TASKS | Running permutations: 100%|█████████▉| 999/1000 [00:03<00:00, 288.08it/s]
PROCESSING TASKS | Running permutations: 100%|██████████| 1000/1000 [00:03<00:00, 287.52it/s]
COLLECTING RESULTS | Running permutations: 0%| | 0/1000 [00:00<?, ?it/s]
COLLECTING RESULTS | Running permutations: 100%|██████████| 1000/1000 [00:00<00:00, 463407.80it/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",
)

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.820 seconds)