py-neuromodulation 0.0.4__py3-none-any.whl → 0.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_neuromodulation/ConnectivityDecoding/_get_grid_hull.m +34 -34
- py_neuromodulation/ConnectivityDecoding/_get_grid_whole_brain.py +95 -106
- py_neuromodulation/ConnectivityDecoding/_helper_write_connectome.py +107 -119
- py_neuromodulation/FieldTrip.py +589 -589
- py_neuromodulation/__init__.py +74 -13
- py_neuromodulation/_write_example_dataset_helper.py +83 -65
- py_neuromodulation/data/README +6 -6
- py_neuromodulation/data/dataset_description.json +8 -8
- py_neuromodulation/data/participants.json +32 -32
- py_neuromodulation/data/participants.tsv +2 -2
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +5 -5
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +11 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +11 -11
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +18 -18
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +35 -35
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +13 -13
- py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +2 -2
- py_neuromodulation/grid_cortex.tsv +40 -40
- py_neuromodulation/liblsl/libpugixml.so.1.12 +0 -0
- py_neuromodulation/liblsl/linux/bionic_amd64/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/linux/bookworm_amd64/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/linux/focal_amd46/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/linux/jammy_amd64/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/linux/jammy_x86/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/linux/noble_amd64/liblsl.1.16.2.so +0 -0
- py_neuromodulation/liblsl/macos/amd64/liblsl.1.16.2.dylib +0 -0
- py_neuromodulation/liblsl/macos/arm64/liblsl.1.16.0.dylib +0 -0
- py_neuromodulation/liblsl/windows/amd64/liblsl.1.16.2.dll +0 -0
- py_neuromodulation/liblsl/windows/x86/liblsl.1.16.2.dll +0 -0
- py_neuromodulation/nm_IO.py +413 -417
- py_neuromodulation/nm_RMAP.py +496 -531
- py_neuromodulation/nm_analysis.py +993 -1074
- py_neuromodulation/nm_artifacts.py +30 -25
- py_neuromodulation/nm_bispectra.py +154 -168
- py_neuromodulation/nm_bursts.py +292 -198
- py_neuromodulation/nm_coherence.py +251 -205
- py_neuromodulation/nm_database.py +149 -0
- py_neuromodulation/nm_decode.py +918 -992
- py_neuromodulation/nm_define_nmchannels.py +300 -302
- py_neuromodulation/nm_features.py +144 -116
- py_neuromodulation/nm_filter.py +219 -219
- py_neuromodulation/nm_filter_preprocessing.py +79 -91
- py_neuromodulation/nm_fooof.py +139 -159
- py_neuromodulation/nm_generator.py +45 -37
- py_neuromodulation/nm_hjorth_raw.py +52 -73
- py_neuromodulation/nm_kalmanfilter.py +71 -58
- py_neuromodulation/nm_linelength.py +21 -33
- py_neuromodulation/nm_logger.py +66 -0
- py_neuromodulation/nm_mne_connectivity.py +149 -112
- py_neuromodulation/nm_mnelsl_generator.py +90 -0
- py_neuromodulation/nm_mnelsl_stream.py +116 -0
- py_neuromodulation/nm_nolds.py +96 -93
- py_neuromodulation/nm_normalization.py +173 -214
- py_neuromodulation/nm_oscillatory.py +423 -448
- py_neuromodulation/nm_plots.py +585 -612
- py_neuromodulation/nm_preprocessing.py +83 -0
- py_neuromodulation/nm_projection.py +370 -394
- py_neuromodulation/nm_rereference.py +97 -95
- py_neuromodulation/nm_resample.py +59 -50
- py_neuromodulation/nm_run_analysis.py +325 -435
- py_neuromodulation/nm_settings.py +289 -68
- py_neuromodulation/nm_settings.yaml +244 -0
- py_neuromodulation/nm_sharpwaves.py +423 -401
- py_neuromodulation/nm_stats.py +464 -480
- py_neuromodulation/nm_stream.py +398 -0
- py_neuromodulation/nm_stream_abc.py +166 -218
- py_neuromodulation/nm_types.py +193 -0
- {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.5.dist-info}/METADATA +29 -26
- py_neuromodulation-0.0.5.dist-info/RECORD +83 -0
- {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.5.dist-info}/WHEEL +1 -1
- {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.5.dist-info}/licenses/LICENSE +21 -21
- py_neuromodulation/nm_EpochStream.py +0 -92
- py_neuromodulation/nm_across_patient_decoding.py +0 -927
- py_neuromodulation/nm_cohortwrapper.py +0 -435
- py_neuromodulation/nm_eval_timing.py +0 -239
- py_neuromodulation/nm_features_abc.py +0 -39
- py_neuromodulation/nm_settings.json +0 -338
- py_neuromodulation/nm_stream_offline.py +0 -359
- py_neuromodulation/utils/_logging.py +0 -24
- py_neuromodulation-0.0.4.dist-info/RECORD +0 -72
py_neuromodulation/nm_RMAP.py
CHANGED
|
@@ -1,531 +1,496 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if
|
|
94
|
-
if
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
self.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
161
|
-
|
|
162
|
-
def
|
|
163
|
-
"""
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
self.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
),
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
def
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
path_dir
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
fp_pairs = []
|
|
498
|
-
|
|
499
|
-
for cohort in cohorts_train.keys():
|
|
500
|
-
for sub in cohorts_train[cohort]:
|
|
501
|
-
fps_name, fps = self.get_fingerprints_from_path_with_cond(
|
|
502
|
-
path_dir=path_dir,
|
|
503
|
-
str_to_keep=f"{cohort}_{sub}_ROI",
|
|
504
|
-
keep=True,
|
|
505
|
-
)
|
|
506
|
-
|
|
507
|
-
for fp, fp_name in zip(fps, fps_name):
|
|
508
|
-
ch = fp_name[
|
|
509
|
-
fp_name.find("ROI") + 4 : fp_name.find("func") - 1
|
|
510
|
-
]
|
|
511
|
-
corr_val = self.get_corr_numba(fp_test, fp)
|
|
512
|
-
fp_pairs.append([cohort, sub, ch, corr_val])
|
|
513
|
-
|
|
514
|
-
idx_max = np.argmax(np.array(fp_pairs)[:, 3])
|
|
515
|
-
return fp_pairs[idx_max][0:3]
|
|
516
|
-
|
|
517
|
-
def plot_performance_prediction_correlation(
|
|
518
|
-
per_left_out, per_predict, out_path_save: str = None
|
|
519
|
-
):
|
|
520
|
-
df_plt_corr = pd.DataFrame()
|
|
521
|
-
df_plt_corr["test_performance"] = per_left_out
|
|
522
|
-
df_plt_corr["struct_conn_predict"] = (
|
|
523
|
-
per_predict # change "struct" with "funct" for functional connectivity
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
nm_plots.reg_plot(
|
|
527
|
-
x_col="test_performance",
|
|
528
|
-
y_col="struct_conn_predict",
|
|
529
|
-
data=df_plt_corr,
|
|
530
|
-
out_path_save=out_path_save,
|
|
531
|
-
)
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pathlib import PurePath, Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# from numba import jit
|
|
6
|
+
import scipy.io as sio
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import nibabel as nib
|
|
9
|
+
from matplotlib import pyplot as plt
|
|
10
|
+
|
|
11
|
+
from py_neuromodulation.nm_plots import reg_plot
|
|
12
|
+
from py_neuromodulation.nm_types import _PathLike
|
|
13
|
+
from py_neuromodulation import PYNM_DIR
|
|
14
|
+
|
|
15
|
+
LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL = [256, 385, 417, 447, 819, 914]
|
|
16
|
+
LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN = [
|
|
17
|
+
1,
|
|
18
|
+
8,
|
|
19
|
+
16,
|
|
20
|
+
33,
|
|
21
|
+
34,
|
|
22
|
+
35,
|
|
23
|
+
36,
|
|
24
|
+
37,
|
|
25
|
+
51,
|
|
26
|
+
75,
|
|
27
|
+
77,
|
|
28
|
+
78,
|
|
29
|
+
99,
|
|
30
|
+
109,
|
|
31
|
+
115,
|
|
32
|
+
136,
|
|
33
|
+
155,
|
|
34
|
+
170,
|
|
35
|
+
210,
|
|
36
|
+
215,
|
|
37
|
+
243,
|
|
38
|
+
352,
|
|
39
|
+
359,
|
|
40
|
+
361,
|
|
41
|
+
415,
|
|
42
|
+
416,
|
|
43
|
+
422,
|
|
44
|
+
529,
|
|
45
|
+
567,
|
|
46
|
+
569,
|
|
47
|
+
622,
|
|
48
|
+
623,
|
|
49
|
+
625,
|
|
50
|
+
627,
|
|
51
|
+
632,
|
|
52
|
+
633,
|
|
53
|
+
634,
|
|
54
|
+
635,
|
|
55
|
+
639,
|
|
56
|
+
640,
|
|
57
|
+
641,
|
|
58
|
+
643,
|
|
59
|
+
644,
|
|
60
|
+
650,
|
|
61
|
+
661,
|
|
62
|
+
663,
|
|
63
|
+
667,
|
|
64
|
+
683,
|
|
65
|
+
684,
|
|
66
|
+
685,
|
|
67
|
+
704,
|
|
68
|
+
708,
|
|
69
|
+
722,
|
|
70
|
+
839,
|
|
71
|
+
840,
|
|
72
|
+
905,
|
|
73
|
+
993,
|
|
74
|
+
1011,
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ConnectivityChannelSelector:
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
whole_brain_connectome: bool = True,
|
|
82
|
+
func_connectivity: bool = True,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""ConnectivityChannelSelector
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
whole_brain_connectome : bool, optional
|
|
89
|
+
if True a 1236 whole-brain point grid is chosen,
|
|
90
|
+
if False, a 1025 point grid of the cortical hull is loaded,
|
|
91
|
+
by default True
|
|
92
|
+
func_connectivity : bool, optional
|
|
93
|
+
if true, functional connectivity fMRI is loaded,
|
|
94
|
+
if false structural dMRIby, default True
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
self.connectome_name = self._get_connectome_name(
|
|
98
|
+
whole_brain_connectome, func_connectivity
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self.whole_brain_connectome = whole_brain_connectome
|
|
102
|
+
self.func_connectivity = func_connectivity
|
|
103
|
+
|
|
104
|
+
self.PATH_CONN_DECODING = PYNM_DIR / "ConnectivityDecoding"
|
|
105
|
+
|
|
106
|
+
if whole_brain_connectome:
|
|
107
|
+
self.PATH_GRID = PurePath(
|
|
108
|
+
self.PATH_CONN_DECODING,
|
|
109
|
+
"mni_coords_whole_brain.mat",
|
|
110
|
+
)
|
|
111
|
+
self.grid = sio.loadmat(self.PATH_GRID)["downsample_ctx"]
|
|
112
|
+
if not func_connectivity:
|
|
113
|
+
# reduce the grid to only valid points that are not in LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN
|
|
114
|
+
self.grid = np.delete(
|
|
115
|
+
self.grid,
|
|
116
|
+
LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN,
|
|
117
|
+
axis=0,
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
self.PATH_GRID = PurePath(
|
|
121
|
+
self.PATH_CONN_DECODING,
|
|
122
|
+
"mni_coords_cortical_surface.mat",
|
|
123
|
+
)
|
|
124
|
+
self.grid = sio.loadmat(self.PATH_GRID)["downsample_ctx"]
|
|
125
|
+
if not func_connectivity:
|
|
126
|
+
# reduce the grid to only valid points that are not in LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL
|
|
127
|
+
self.grid = np.delete(
|
|
128
|
+
self.grid, LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL, axis=0
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if func_connectivity:
|
|
132
|
+
self.RMAP_arr = nib.load(
|
|
133
|
+
PurePath(self.PATH_CONN_DECODING, "RMAP_func_all.nii")
|
|
134
|
+
).get_fdata()
|
|
135
|
+
else:
|
|
136
|
+
self.RMAP_arr = nib.load(
|
|
137
|
+
PurePath(self.PATH_CONN_DECODING, "RMAP_struc.nii")
|
|
138
|
+
).get_fdata()
|
|
139
|
+
|
|
140
|
+
def _get_connectome_name(self, whole_brain_connectome: str, func_connectivity: str):
|
|
141
|
+
connectome_name = "connectome_"
|
|
142
|
+
if whole_brain_connectome:
|
|
143
|
+
connectome_name += "whole_brain_"
|
|
144
|
+
else:
|
|
145
|
+
connectome_name += "hull_"
|
|
146
|
+
if func_connectivity:
|
|
147
|
+
connectome_name += "func"
|
|
148
|
+
else:
|
|
149
|
+
connectome_name += "struc"
|
|
150
|
+
return connectome_name
|
|
151
|
+
|
|
152
|
+
def get_available_connectomes(self) -> list:
|
|
153
|
+
"""Return list of saved connectomes in the
|
|
154
|
+
package folder/ConnectivityDecoding/connectome_folder/ folder.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
list_connectomes: list
|
|
159
|
+
"""
|
|
160
|
+
return list(Path(self.PATH_CONN_DECODING, "connectome_folder").iterdir())
|
|
161
|
+
|
|
162
|
+
def plot_grid(self) -> None:
|
|
163
|
+
"""Plot the loaded template grid that passed coordinates are matched to."""
|
|
164
|
+
|
|
165
|
+
fig = plt.figure()
|
|
166
|
+
ax = fig.add_subplot(111, projection="3d")
|
|
167
|
+
ax.scatter(self.grid[:, 0], self.grid[:, 1], self.grid[:, 2], s=50, alpha=0.2)
|
|
168
|
+
plt.show()
|
|
169
|
+
|
|
170
|
+
def get_closest_node(self, coord: list | np.ndarray) -> tuple[list, list]:
|
|
171
|
+
"""Given a list or np.array of coordinates, return the closest nodes in the
|
|
172
|
+
grid and their indices.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
coord : np.ndarray
|
|
177
|
+
MNI coordinates with shape (num_channels, 3)
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
Tuple[list, list]
|
|
182
|
+
Grid coordinates, grid indices
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
idx_ = []
|
|
186
|
+
for c in coord:
|
|
187
|
+
dist = np.linalg.norm(self.grid - c, axis=1)
|
|
188
|
+
idx_.append(np.argmin(dist))
|
|
189
|
+
|
|
190
|
+
return [self.grid[idx] for idx in idx_], idx_
|
|
191
|
+
|
|
192
|
+
def get_rmap_correlations(
|
|
193
|
+
self, fps: list | np.ndarray, RMAP_use: np.ndarray | None = None
|
|
194
|
+
) -> list:
|
|
195
|
+
"""Calculate correlations of passed fingerprints with the RMAP
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
fps : Union[list, np.array]
|
|
200
|
+
List of fingerprints
|
|
201
|
+
RMAP_use : np.ndarray, optional
|
|
202
|
+
Passed RMAP, by default None
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
List
|
|
207
|
+
correlation values
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
RMAP_ = self.RMAP_arr if RMAP_use is None else RMAP_use
|
|
211
|
+
RMAP_ = RMAP_.flatten()
|
|
212
|
+
corrs = []
|
|
213
|
+
for fp in fps:
|
|
214
|
+
corrs.append(np.corrcoef(RMAP_, fp.flatten())[0][1])
|
|
215
|
+
return corrs
|
|
216
|
+
|
|
217
|
+
def load_connectome(
|
|
218
|
+
self,
|
|
219
|
+
whole_brain_connectome: bool | None = None,
|
|
220
|
+
func_connectivity: bool | None = None,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""Load connectome, if not available download connectome from
|
|
223
|
+
Zenodo.
|
|
224
|
+
|
|
225
|
+
Parameters
|
|
226
|
+
----------
|
|
227
|
+
whole_brain_connectome : bool, optional
|
|
228
|
+
if true whole brain connectome
|
|
229
|
+
if false cortical hull grid connectome, by default None
|
|
230
|
+
func_connectivity : bool, optional
|
|
231
|
+
if true fMRI if false dMRI, by default None
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
if whole_brain_connectome is not None:
|
|
235
|
+
self.whole_brain_connectome = whole_brain_connectome
|
|
236
|
+
if func_connectivity is not None:
|
|
237
|
+
self.func_connectivity = func_connectivity
|
|
238
|
+
|
|
239
|
+
self.connectome_name = self._get_connectome_name(
|
|
240
|
+
self.whole_brain_connectome, self.func_connectivity
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
PATH_CONNECTOME = Path(
|
|
244
|
+
self.PATH_CONN_DECODING,
|
|
245
|
+
"connectome_folder",
|
|
246
|
+
self.connectome_name + ".mat",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if not PATH_CONNECTOME.exists():
|
|
250
|
+
user_input = input(
|
|
251
|
+
"Do you want to download the connectome? (yes/no): "
|
|
252
|
+
).lower()
|
|
253
|
+
if user_input == "yes":
|
|
254
|
+
self._download_connectome()
|
|
255
|
+
elif user_input == "no":
|
|
256
|
+
print("Connectome missing, has to be downloaded")
|
|
257
|
+
|
|
258
|
+
self.connectome = sio.loadmat(PATH_CONNECTOME)
|
|
259
|
+
|
|
260
|
+
def get_grid_fingerprints(self, grid_idx: list | np.ndarray) -> list:
|
|
261
|
+
return [self.connectome[str(grid_idx)] for grid_idx in grid_idx]
|
|
262
|
+
|
|
263
|
+
def download_connectome(
|
|
264
|
+
self,
|
|
265
|
+
):
|
|
266
|
+
|
|
267
|
+
from urllib.request import urlretrieve
|
|
268
|
+
|
|
269
|
+
# download the connectome from the Zenodo API
|
|
270
|
+
print("Downloading the connectome...")
|
|
271
|
+
|
|
272
|
+
record_id = "10804702"
|
|
273
|
+
file_name = self.connectome_name
|
|
274
|
+
|
|
275
|
+
filepath = Path(self.PATH_CONN_DECODING, "connectome_folder")
|
|
276
|
+
filepath.mkdir(parents=True, exist_ok=True)
|
|
277
|
+
|
|
278
|
+
urlretrieve(
|
|
279
|
+
f"https://zenodo.org/api/records/{record_id}/files/{file_name}/content",
|
|
280
|
+
filepath / f"{self.connectome_name}.mat",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class RMAPCross_Val_ChannelSelector:
|
|
285
|
+
def __init__(self) -> None:
|
|
286
|
+
pass
|
|
287
|
+
|
|
288
|
+
def load_fingerprint(self, path_nii) -> None:
|
|
289
|
+
"""Return Nifti fingerprint"""
|
|
290
|
+
epi_img = nib.load(path_nii)
|
|
291
|
+
self.affine = epi_img.affine
|
|
292
|
+
fp = epi_img.get_fdata()
|
|
293
|
+
return fp
|
|
294
|
+
|
|
295
|
+
def load_all_fingerprints(self, path_dir: str, cond_str: str = "_AvgR_Fz.nii"):
|
|
296
|
+
if cond_str is not None:
|
|
297
|
+
l_fps = list(filter(lambda k: cond_str in str(k), Path(path_dir).iterdir()))
|
|
298
|
+
else:
|
|
299
|
+
l_fps = list(Path(path_dir).iterdir())
|
|
300
|
+
|
|
301
|
+
return l_fps, [self.load_fingerprint(PurePath(path_dir, f)) for f in l_fps]
|
|
302
|
+
|
|
303
|
+
def get_fingerprints_from_path_with_cond(
|
|
304
|
+
self,
|
|
305
|
+
path_dir: _PathLike,
|
|
306
|
+
str_to_omit: str = "",
|
|
307
|
+
str_to_keep: str = "",
|
|
308
|
+
keep: bool = True,
|
|
309
|
+
) -> tuple[list, list]:
|
|
310
|
+
l_fps = []
|
|
311
|
+
|
|
312
|
+
if keep and str_to_keep:
|
|
313
|
+
l_fps = list(
|
|
314
|
+
filter(
|
|
315
|
+
lambda k: "_AvgR_Fz.nii" in str(k) and str_to_keep in str(k),
|
|
316
|
+
Path(path_dir).iterdir(),
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
elif not keep and str_to_omit:
|
|
321
|
+
l_fps = list(
|
|
322
|
+
filter(
|
|
323
|
+
lambda k: "_AvgR_Fz.nii" in str(k) and str_to_omit not in str(k),
|
|
324
|
+
Path(path_dir).iterdir(),
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
return l_fps, [self.load_fingerprint(PurePath(path_dir, f)) for f in l_fps]
|
|
329
|
+
|
|
330
|
+
@staticmethod
|
|
331
|
+
def save_Nii(
|
|
332
|
+
fp: np.ndarray,
|
|
333
|
+
affine: np.ndarray,
|
|
334
|
+
name: str = "img.nii",
|
|
335
|
+
reshape: bool = True,
|
|
336
|
+
):
|
|
337
|
+
if reshape:
|
|
338
|
+
fp = np.reshape(fp, (91, 109, 91), order="F")
|
|
339
|
+
|
|
340
|
+
img = nib.nifti1.Nifti1Image(fp, affine=affine)
|
|
341
|
+
|
|
342
|
+
nib.save(img, name)
|
|
343
|
+
|
|
344
|
+
def get_RMAP(self, X: np.ndarray, y: np.ndarray):
|
|
345
|
+
# faster than calculate_RMap_numba
|
|
346
|
+
# https://stackoverflow.com/questions/71252740/correlating-an-array-row-wise-with-a-vector/71253141#71253141
|
|
347
|
+
|
|
348
|
+
r = (
|
|
349
|
+
len(y) * np.sum(X * y[None, :], axis=-1) - (np.sum(X, axis=-1) * np.sum(y))
|
|
350
|
+
) / (
|
|
351
|
+
np.sqrt(
|
|
352
|
+
(len(y) * np.sum(X**2, axis=-1) - np.sum(X, axis=-1) ** 2)
|
|
353
|
+
* (len(y) * np.sum(y**2) - np.sum(y) ** 2)
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
return r
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
# @jit(nopython=True)
|
|
360
|
+
def calculate_RMap_numba(fp, performances):
|
|
361
|
+
# The RMap also needs performances; for every fingerprint / channel
|
|
362
|
+
# Save the corresponding performance
|
|
363
|
+
# for every voxel; correlate it with performances
|
|
364
|
+
|
|
365
|
+
arr = fp[0].flatten()
|
|
366
|
+
NUM_VOXELS = arr.shape[0]
|
|
367
|
+
LEN_FPS = len(fp)
|
|
368
|
+
fp_arr = np.empty((NUM_VOXELS, LEN_FPS))
|
|
369
|
+
for fp_idx, fp_ in enumerate(fp):
|
|
370
|
+
fp_arr[:, fp_idx] = fp_.flatten()
|
|
371
|
+
|
|
372
|
+
RMAP = np.zeros(NUM_VOXELS)
|
|
373
|
+
for voxel in range(NUM_VOXELS):
|
|
374
|
+
corr_val = np.corrcoef(fp_arr[voxel, :], performances)[0][1]
|
|
375
|
+
|
|
376
|
+
RMAP[voxel] = corr_val
|
|
377
|
+
|
|
378
|
+
return RMAP
|
|
379
|
+
|
|
380
|
+
@staticmethod
|
|
381
|
+
# @jit(nopython=True)
|
|
382
|
+
def get_corr_numba(fp, fp_test):
|
|
383
|
+
val = np.corrcoef(fp_test, fp)[0][1]
|
|
384
|
+
return val
|
|
385
|
+
|
|
386
|
+
def leave_one_ch_out_cv(self, l_fps_names: list, l_fps_dat: list, l_per: list):
|
|
387
|
+
# l_fps_dat is not flattened
|
|
388
|
+
|
|
389
|
+
per_left_out = []
|
|
390
|
+
per_predict = []
|
|
391
|
+
|
|
392
|
+
for idx_left_out, f_left_out in enumerate(l_fps_names):
|
|
393
|
+
# print(idx_left_out)
|
|
394
|
+
l_cv = l_fps_dat.copy()
|
|
395
|
+
per_cv = l_per.copy()
|
|
396
|
+
|
|
397
|
+
l_cv.pop(idx_left_out)
|
|
398
|
+
per_cv.pop(idx_left_out)
|
|
399
|
+
|
|
400
|
+
conn_arr = []
|
|
401
|
+
for f in l_cv:
|
|
402
|
+
conn_arr.append(f.flatten())
|
|
403
|
+
conn_arr = np.array(conn_arr)
|
|
404
|
+
|
|
405
|
+
rmap_cv = np.nan_to_num(self.get_RMAP(conn_arr.T, np.array(per_cv)))
|
|
406
|
+
|
|
407
|
+
per_predict.append(
|
|
408
|
+
np.nan_to_num(
|
|
409
|
+
self.get_corr_numba(rmap_cv, l_fps_dat[idx_left_out].flatten())
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
per_left_out.append(l_per[idx_left_out])
|
|
413
|
+
return per_left_out, per_predict
|
|
414
|
+
|
|
415
|
+
def leave_one_sub_out_cv(
|
|
416
|
+
self, l_fps_names: list, l_fps_dat: list, l_per: list, sub_list: list
|
|
417
|
+
):
|
|
418
|
+
# l_fps_dat assume non flatted arrays
|
|
419
|
+
# each fp including the sub_list string will be iteratively removed for test set
|
|
420
|
+
|
|
421
|
+
per_predict = []
|
|
422
|
+
per_left_out = []
|
|
423
|
+
|
|
424
|
+
for subject_test in sub_list:
|
|
425
|
+
# print(subject_test)
|
|
426
|
+
idx_test = [idx for idx, f in enumerate(l_fps_names) if subject_test in f]
|
|
427
|
+
idx_train = [
|
|
428
|
+
idx for idx, f in enumerate(l_fps_names) if subject_test not in f
|
|
429
|
+
]
|
|
430
|
+
l_cv = list(np.array(l_fps_dat)[idx_train])
|
|
431
|
+
per_cv = list(np.array(l_per)[idx_train])
|
|
432
|
+
|
|
433
|
+
conn_arr = []
|
|
434
|
+
for f in l_cv:
|
|
435
|
+
conn_arr.append(f.flatten())
|
|
436
|
+
conn_arr = np.array(conn_arr)
|
|
437
|
+
rmap_cv = np.nan_to_num(self.get_RMAP(conn_arr.T, np.array(per_cv)))
|
|
438
|
+
|
|
439
|
+
for idx in idx_test:
|
|
440
|
+
per_predict.append(
|
|
441
|
+
np.nan_to_num(
|
|
442
|
+
self.get_corr_numba(rmap_cv, l_fps_dat[idx].flatten())
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
per_left_out.append(l_per[idx])
|
|
446
|
+
return per_left_out, per_predict
|
|
447
|
+
|
|
448
|
+
def get_highest_corr_sub_ch(
|
|
449
|
+
self,
|
|
450
|
+
cohort_test: str,
|
|
451
|
+
sub_test: str,
|
|
452
|
+
ch_test: str,
|
|
453
|
+
cohorts_train: dict,
|
|
454
|
+
path_dir: str = r"C:\Users\ICN_admin\OneDrive - Charité - Universitätsmedizin Berlin\Connectomics\DecodingToolbox_BerlinPittsburgh_Beijing\functional_connectivity",
|
|
455
|
+
):
|
|
456
|
+
fp_test = self.get_fingerprints_from_path_with_cond(
|
|
457
|
+
path_dir=path_dir,
|
|
458
|
+
str_to_keep=f"{cohort_test}_{sub_test}_ROI_{ch_test}",
|
|
459
|
+
keep=True,
|
|
460
|
+
)[1][
|
|
461
|
+
0
|
|
462
|
+
].flatten() # index 1 for getting the array, 0 for the list fp that was found
|
|
463
|
+
|
|
464
|
+
fp_pairs = []
|
|
465
|
+
|
|
466
|
+
for cohort in cohorts_train.keys():
|
|
467
|
+
for sub in cohorts_train[cohort]:
|
|
468
|
+
fps_name, fps = self.get_fingerprints_from_path_with_cond(
|
|
469
|
+
path_dir=path_dir,
|
|
470
|
+
str_to_keep=f"{cohort}_{sub}_ROI",
|
|
471
|
+
keep=True,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
for fp, fp_name in zip(fps, fps_name):
|
|
475
|
+
ch = fp_name[fp_name.find("ROI") + 4 : fp_name.find("func") - 1]
|
|
476
|
+
corr_val = self.get_corr_numba(fp_test, fp)
|
|
477
|
+
fp_pairs.append([cohort, sub, ch, corr_val])
|
|
478
|
+
|
|
479
|
+
idx_max = np.argmax(np.array(fp_pairs)[:, 3])
|
|
480
|
+
return fp_pairs[idx_max][0:3]
|
|
481
|
+
|
|
482
|
+
def plot_performance_prediction_correlation(
|
|
483
|
+
per_left_out, per_predict, out_path_save: str | None = None
|
|
484
|
+
):
|
|
485
|
+
df_plt_corr = pd.DataFrame()
|
|
486
|
+
df_plt_corr["test_performance"] = per_left_out
|
|
487
|
+
df_plt_corr["struct_conn_predict"] = (
|
|
488
|
+
per_predict # change "struct" with "funct" for functional connectivity
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
reg_plot(
|
|
492
|
+
x_col="test_performance",
|
|
493
|
+
y_col="struct_conn_predict",
|
|
494
|
+
data=df_plt_corr,
|
|
495
|
+
out_path_save=out_path_save,
|
|
496
|
+
)
|