Skip to content

Commit cd5bdce

Browse files
Update tuto about clustering statistics
1 parent ca9dfd0 commit cd5bdce

File tree

1 file changed

+94
-34
lines changed

1 file changed

+94
-34
lines changed

tutorial/UCLAWorkshop/06 - Statistics.ipynb

Lines changed: 94 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"import mne\n",
3030
"import matplotlib.pyplot as plt\n",
3131
"import scipy.sparse as sp\n",
32+
"import scipy.stats as sp_stats\n",
3233
"from collections import OrderedDict\n",
3334
"\n",
3435
"# Import HyPyP modules\n",
@@ -140,10 +141,12 @@
140141
"source": [
141142
"# Simulate multiple samples (e.g., across subjects or trials)\n",
142143
"n_samples = 20\n",
143-
"# Flatten the PSD for each participant; assume n_tests = number of channels\n",
144-
"psd_flat = data_psd.reshape(2, -1) # shape: (2, n_tests)\n",
144+
"noise_level = 1e-6\n",
145+
"\n",
146+
"psd = data_psd\n",
147+
"\n",
145148
"# Create a sample data array by replicating and adding slight random noise\n",
146-
"data_sample = np.array([psd_flat[0] + 1e-6 * np.random.randn(psd_flat.shape[1]) for _ in range(n_samples)])\n",
149+
"data_sample = np.array([psd[0, :, 0] + noise_level * np.random.randn(psd.shape[1]) + noise_level * np.concat([np.zeros((21,1 )), np.ones((10, 1))]).flatten() for _ in range(n_samples)])\n",
147150
"# Add a frequency dimension (here, 1 frequency bin)\n",
148151
"data_sample = data_sample[..., np.newaxis] # shape: (n_samples, n_tests, 1)\n",
149152
"print(\"Data for statsCond shape:\", data_sample.shape)\n",
@@ -153,22 +156,9 @@
153156
"print(\"Permutation t-test completed.\")\n",
154157
"print(\"T_obs shape:\", T_obs.shape)\n",
155158
"\n",
156-
"# Average T_obs over frequency bins to obtain one value per sensor\n",
157-
"n_channels = len(preproc_S1.info['ch_names']) # e.g., 31\n",
158-
"n_freq = T_obs.shape[0] // n_channels # here, 248/31 = 8\n",
159-
"T_obs_avg = T_obs.reshape(n_channels, n_freq).mean(axis=1)\n",
160-
"\n",
161-
"# Similarly, average T_obs_plot if needed:\n",
162-
"T_obs_plot_avg = T_obs_plot.reshape(n_channels, n_freq).mean(axis=1)\n",
163-
"print(\"Averaged T_obs shape:\", T_obs_avg.shape)\n",
164-
"\n",
165159
"# Plot sensor-level T-values (all sensors) using the averaged T-values\n",
166-
"viz.plot_significant_sensors(T_obs_plot=T_obs_avg, epochs=preproc_S1)\n",
167-
"print(\"Sensor-level T-values plotted (all sensors).\")\n",
168-
"\n",
169-
"# Plot only significant sensors (after FDR correction) using averaged values\n",
170-
"viz.plot_significant_sensors(T_obs_plot=T_obs_plot_avg, epochs=preproc_S1)\n",
171-
"print(\"Significant sensors T-values plotted (FDR corrected).\")"
160+
"viz.plot_significant_sensors(T_obs_plot=T_obs, epochs=preproc_S1, significant=T_obs_plot)\n",
161+
"print(\"Sensor-level T-values plotted (all sensors).\")"
172162
]
173163
},
174164
{
@@ -182,6 +172,16 @@
182172
"We first compute an a priori connectivity matrix (using `con_matrix`) and convert it to a SciPy sparse matrix."
183173
]
184174
},
175+
{
176+
"cell_type": "code",
177+
"execution_count": null,
178+
"id": "729937df",
179+
"metadata": {},
180+
"outputs": [],
181+
"source": [
182+
"psd[0, :, 0]"
183+
]
184+
},
185185
{
186186
"cell_type": "code",
187187
"execution_count": null,
@@ -190,15 +190,14 @@
190190
"outputs": [],
191191
"source": [
192192
"# Simulate two conditions by adding small noise to the PSD data\n",
193-
"noise_level = 1e-6\n",
194-
"data_condition1 = data_psd + noise_level * np.random.randn(*data_psd.shape)\n",
195-
"data_condition2 = data_psd + noise_level * np.random.randn(*data_psd.shape) - 0.05 # slight difference\n",
193+
"data_condition1 = np.array([psd[0, :, 0] + noise_level * np.random.randn(psd.shape[1]) for _ in range(n_samples)])\n",
194+
"data_condition2 = np.array([psd[0, :, 0] + noise_level * np.random.randn(psd.shape[1]) + noise_level * np.concat([np.zeros((21,1 )), np.ones((10, 1))]).flatten() for _ in range(n_samples)])\n",
196195
"\n",
197196
"# Create a list of data arrays (one per condition)\n",
198197
"data_list = [data_condition1, data_condition2]\n",
199198
"\n",
200199
"# Create connectivity matrix for a priori sensor connectivity using participant 1's sensor layout\n",
201-
"con_matrixTuple = stats.con_matrix(preproc_S1, freqs_mean=psd1.freq_list, draw=True)\n",
200+
"con_matrixTuple = stats.con_matrix(preproc_S1, freqs_mean=[psd1.freq_list[0]], draw=True)\n",
202201
"ch_con_freq = con_matrixTuple.ch_con_freq\n",
203202
"\n",
204203
"# Convert ch_con_freq to a SciPy sparse matrix (choose bsr or csr; here we use csr)\n",
@@ -207,7 +206,7 @@
207206
"# Run cluster-level permutation test (e.g., 5000 permutations, alpha=0.05)\n",
208207
"F_obs, clusters, cluster_p_values, H0_cluster, F_obs_plot = stats.statscondCluster(\n",
209208
" data_list,\n",
210-
" freqs_mean=psd1.freq_list,\n",
209+
" freqs_mean=[psd1.freq_list[0]],\n",
211210
" ch_con_freq=ch_con_freq_sparse,\n",
212211
" tail=0,\n",
213212
" n_permutations=5000,\n",
@@ -218,14 +217,8 @@
218217
"print(\"F_obs shape:\", F_obs.shape)\n",
219218
"print(\"Number of clusters found:\", len(cluster_p_values))\n",
220219
"\n",
221-
"# Average F_obs_plot over frequency bins to obtain one value per sensor\n",
222-
"F_obs_plot_avg = F_obs_plot.mean(axis=1)\n",
223220
"plt.figure()\n",
224-
"plt.bar(range(len(F_obs_plot_avg)), F_obs_plot_avg)\n",
225-
"plt.xlabel(\"Test index (sensor)\")\n",
226-
"plt.ylabel(\"F-statistic\")\n",
227-
"plt.title(\"Significant Cluster F-statistics (after correction)\")\n",
228-
"plt.show()"
221+
"viz.plot_significant_sensors(T_obs_plot=F_obs, epochs=preproc_S1, significant=F_obs_plot)"
229222
]
230223
},
231224
{
@@ -239,6 +232,16 @@
239232
"We use the connectivity results (here simulated as `result_intra` and `values`) from previous analyses."
240233
]
241234
},
235+
{
236+
"cell_type": "code",
237+
"execution_count": null,
238+
"id": "8cf01121",
239+
"metadata": {},
240+
"outputs": [],
241+
"source": [
242+
"result_intra[0].shape"
243+
]
244+
},
242245
{
243246
"cell_type": "code",
244247
"execution_count": null,
@@ -252,20 +255,37 @@
252255
"n_channels = len(preproc_S1.info['ch_names'])\n",
253256
"result_intra = [np.random.rand(n_channels, n_channels), np.random.rand(n_channels, n_channels)]\n",
254257
"\n",
258+
"mask = np.zeros(result_intra[0].shape)\n",
259+
"mask[26:,:][:, 22:] = 1\n",
260+
"\n",
255261
"# Create fake groups for intra-brain connectivity analysis by replicating and adding slight noise\n",
256262
"Alpha_Low = [\n",
257-
" np.array([result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape),\n",
258-
" result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape)]),\n",
263+
" np.array([result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape) + noise_level * mask,\n",
264+
" result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape) + noise_level * mask,\n",
265+
" result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape) + noise_level * mask,\n",
266+
" result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape) + noise_level * mask,\n",
267+
" result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape) + noise_level * mask]),\n",
259268
" np.array([result_intra[1] + np.random.normal(0, noise_level, result_intra[1].shape),\n",
269+
" result_intra[1] + np.random.normal(0, noise_level, result_intra[1].shape),\n",
270+
" result_intra[1] + np.random.normal(0, noise_level, result_intra[1].shape),\n",
271+
" result_intra[1] + np.random.normal(0, noise_level, result_intra[1].shape),\n",
260272
" result_intra[1] + np.random.normal(0, noise_level, result_intra[1].shape)])\n",
261273
"]\n",
262274
"\n",
263275
"# Create fake groups for inter-brain connectivity analysis.\n",
264276
"# For demonstration, assume \"values\" is a connectivity matrix (e.g., from inter-brain analysis) of shape (n_channels, n_channels)\n",
265277
"values = np.random.rand(n_channels, n_channels)\n",
266278
"data_inter = [\n",
267-
" np.array([values, values + np.random.normal(0, noise_level, values.shape)]),\n",
268-
" np.array([result_intra[0], result_intra[0] + np.random.normal(0, noise_level, result_intra[0].shape)])\n",
279+
" np.array([values, \n",
280+
" values, \n",
281+
" values, \n",
282+
" values, \n",
283+
" values]),\n",
284+
" np.array([values + np.random.normal(0, noise_level, result_intra[0].shape),\n",
285+
" values + np.random.normal(0, noise_level, result_intra[0].shape),\n",
286+
" values + np.random.normal(0, noise_level, result_intra[0].shape),\n",
287+
" values + np.random.normal(0, noise_level, result_intra[0].shape),\n",
288+
" values + np.random.normal(0, noise_level, result_intra[0].shape)])\n",
269289
"]\n",
270290
"\n",
271291
"print(\"Fake groups for connectivity analysis created. Shapes:\",\n",
@@ -308,6 +328,26 @@
308328
"print(\"Intra-brain connectivity cluster test completed.\")"
309329
]
310330
},
331+
{
332+
"cell_type": "code",
333+
"execution_count": null,
334+
"id": "55695d0c",
335+
"metadata": {},
336+
"outputs": [],
337+
"source": [
338+
"cluster_p_values_intra"
339+
]
340+
},
341+
{
342+
"cell_type": "code",
343+
"execution_count": null,
344+
"id": "9da75ec5",
345+
"metadata": {},
346+
"outputs": [],
347+
"source": [
348+
"viz.viz_2D_topomap_intra(preproc_S1, preproc_S2, F_obs_plot_intra, F_obs_plot_intra, threshold='auto', steps=10, lab=True)"
349+
]
350+
},
311351
{
312352
"cell_type": "markdown",
313353
"id": "7d9d8185-0b39-4ca9-a347-2c2d54754a89",
@@ -336,6 +376,26 @@
336376
"print(\"Inter-brain connectivity cluster test completed.\")"
337377
]
338378
},
379+
{
380+
"cell_type": "code",
381+
"execution_count": null,
382+
"id": "b61140f1",
383+
"metadata": {},
384+
"outputs": [],
385+
"source": [
386+
"cluster_p_values_inter"
387+
]
388+
},
389+
{
390+
"cell_type": "code",
391+
"execution_count": null,
392+
"id": "f6e85e25",
393+
"metadata": {},
394+
"outputs": [],
395+
"source": [
396+
"viz.viz_2D_topomap_inter(preproc_S1, preproc_S2, F_obs_plot_inter, threshold='auto', steps=10, lab=True)"
397+
]
398+
},
339399
{
340400
"cell_type": "markdown",
341401
"id": "7c3f53d3-61f7-45bd-95c4-dee64ec82c74",

0 commit comments

Comments
 (0)