py-neuromodulation 0.0.4__py3-none-any.whl → 0.0.6__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.
Files changed (109) hide show
  1. py_neuromodulation/ConnectivityDecoding/_get_grid_hull.m +34 -34
  2. py_neuromodulation/ConnectivityDecoding/_get_grid_whole_brain.py +95 -106
  3. py_neuromodulation/ConnectivityDecoding/_helper_write_connectome.py +107 -119
  4. py_neuromodulation/__init__.py +80 -13
  5. py_neuromodulation/{nm_RMAP.py → analysis/RMAP.py} +496 -531
  6. py_neuromodulation/analysis/__init__.py +4 -0
  7. py_neuromodulation/{nm_decode.py → analysis/decode.py} +918 -992
  8. py_neuromodulation/{nm_analysis.py → analysis/feature_reader.py} +994 -1074
  9. py_neuromodulation/{nm_plots.py → analysis/plots.py} +627 -612
  10. py_neuromodulation/{nm_stats.py → analysis/stats.py} +458 -480
  11. py_neuromodulation/data/README +6 -6
  12. py_neuromodulation/data/dataset_description.json +8 -8
  13. py_neuromodulation/data/participants.json +32 -32
  14. py_neuromodulation/data/participants.tsv +2 -2
  15. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +5 -5
  16. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +11 -11
  17. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +11 -11
  18. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +18 -18
  19. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +35 -35
  20. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +13 -13
  21. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +2 -2
  22. py_neuromodulation/default_settings.yaml +241 -0
  23. py_neuromodulation/features/__init__.py +31 -0
  24. py_neuromodulation/features/bandpower.py +165 -0
  25. py_neuromodulation/features/bispectra.py +157 -0
  26. py_neuromodulation/features/bursts.py +297 -0
  27. py_neuromodulation/features/coherence.py +255 -0
  28. py_neuromodulation/features/feature_processor.py +121 -0
  29. py_neuromodulation/features/fooof.py +142 -0
  30. py_neuromodulation/features/hjorth_raw.py +57 -0
  31. py_neuromodulation/features/linelength.py +21 -0
  32. py_neuromodulation/features/mne_connectivity.py +148 -0
  33. py_neuromodulation/features/nolds.py +94 -0
  34. py_neuromodulation/features/oscillatory.py +249 -0
  35. py_neuromodulation/features/sharpwaves.py +432 -0
  36. py_neuromodulation/filter/__init__.py +3 -0
  37. py_neuromodulation/filter/kalman_filter.py +67 -0
  38. py_neuromodulation/filter/kalman_filter_external.py +1890 -0
  39. py_neuromodulation/filter/mne_filter.py +128 -0
  40. py_neuromodulation/filter/notch_filter.py +93 -0
  41. py_neuromodulation/grid_cortex.tsv +40 -40
  42. py_neuromodulation/liblsl/libpugixml.so.1.12 +0 -0
  43. py_neuromodulation/liblsl/linux/bionic_amd64/liblsl.1.16.2.so +0 -0
  44. py_neuromodulation/liblsl/linux/bookworm_amd64/liblsl.1.16.2.so +0 -0
  45. py_neuromodulation/liblsl/linux/focal_amd46/liblsl.1.16.2.so +0 -0
  46. py_neuromodulation/liblsl/linux/jammy_amd64/liblsl.1.16.2.so +0 -0
  47. py_neuromodulation/liblsl/linux/jammy_x86/liblsl.1.16.2.so +0 -0
  48. py_neuromodulation/liblsl/linux/noble_amd64/liblsl.1.16.2.so +0 -0
  49. py_neuromodulation/liblsl/macos/amd64/liblsl.1.16.2.dylib +0 -0
  50. py_neuromodulation/liblsl/macos/arm64/liblsl.1.16.0.dylib +0 -0
  51. py_neuromodulation/liblsl/windows/amd64/liblsl.1.16.2.dll +0 -0
  52. py_neuromodulation/liblsl/windows/x86/liblsl.1.16.2.dll +0 -0
  53. py_neuromodulation/processing/__init__.py +10 -0
  54. py_neuromodulation/{nm_artifacts.py → processing/artifacts.py} +29 -25
  55. py_neuromodulation/processing/data_preprocessor.py +77 -0
  56. py_neuromodulation/processing/filter_preprocessing.py +78 -0
  57. py_neuromodulation/processing/normalization.py +175 -0
  58. py_neuromodulation/{nm_projection.py → processing/projection.py} +370 -394
  59. py_neuromodulation/{nm_rereference.py → processing/rereference.py} +97 -95
  60. py_neuromodulation/{nm_resample.py → processing/resample.py} +56 -50
  61. py_neuromodulation/stream/__init__.py +3 -0
  62. py_neuromodulation/stream/data_processor.py +325 -0
  63. py_neuromodulation/stream/generator.py +53 -0
  64. py_neuromodulation/stream/mnelsl_player.py +94 -0
  65. py_neuromodulation/stream/mnelsl_stream.py +120 -0
  66. py_neuromodulation/stream/settings.py +292 -0
  67. py_neuromodulation/stream/stream.py +427 -0
  68. py_neuromodulation/utils/__init__.py +2 -0
  69. py_neuromodulation/{nm_define_nmchannels.py → utils/channels.py} +305 -302
  70. py_neuromodulation/utils/database.py +149 -0
  71. py_neuromodulation/utils/io.py +378 -0
  72. py_neuromodulation/utils/keyboard.py +52 -0
  73. py_neuromodulation/utils/logging.py +66 -0
  74. py_neuromodulation/utils/types.py +251 -0
  75. {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.6.dist-info}/METADATA +28 -33
  76. py_neuromodulation-0.0.6.dist-info/RECORD +89 -0
  77. {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.6.dist-info}/WHEEL +1 -1
  78. {py_neuromodulation-0.0.4.dist-info → py_neuromodulation-0.0.6.dist-info}/licenses/LICENSE +21 -21
  79. py_neuromodulation/FieldTrip.py +0 -589
  80. py_neuromodulation/_write_example_dataset_helper.py +0 -65
  81. py_neuromodulation/nm_EpochStream.py +0 -92
  82. py_neuromodulation/nm_IO.py +0 -417
  83. py_neuromodulation/nm_across_patient_decoding.py +0 -927
  84. py_neuromodulation/nm_bispectra.py +0 -168
  85. py_neuromodulation/nm_bursts.py +0 -198
  86. py_neuromodulation/nm_coherence.py +0 -205
  87. py_neuromodulation/nm_cohortwrapper.py +0 -435
  88. py_neuromodulation/nm_eval_timing.py +0 -239
  89. py_neuromodulation/nm_features.py +0 -116
  90. py_neuromodulation/nm_features_abc.py +0 -39
  91. py_neuromodulation/nm_filter.py +0 -219
  92. py_neuromodulation/nm_filter_preprocessing.py +0 -91
  93. py_neuromodulation/nm_fooof.py +0 -159
  94. py_neuromodulation/nm_generator.py +0 -37
  95. py_neuromodulation/nm_hjorth_raw.py +0 -73
  96. py_neuromodulation/nm_kalmanfilter.py +0 -58
  97. py_neuromodulation/nm_linelength.py +0 -33
  98. py_neuromodulation/nm_mne_connectivity.py +0 -112
  99. py_neuromodulation/nm_nolds.py +0 -93
  100. py_neuromodulation/nm_normalization.py +0 -214
  101. py_neuromodulation/nm_oscillatory.py +0 -448
  102. py_neuromodulation/nm_run_analysis.py +0 -435
  103. py_neuromodulation/nm_settings.json +0 -338
  104. py_neuromodulation/nm_settings.py +0 -68
  105. py_neuromodulation/nm_sharpwaves.py +0 -401
  106. py_neuromodulation/nm_stream_abc.py +0 -218
  107. py_neuromodulation/nm_stream_offline.py +0 -359
  108. py_neuromodulation/utils/_logging.py +0 -24
  109. py_neuromodulation-0.0.4.dist-info/RECORD +0 -72
@@ -1,480 +1,458 @@
1
- import random
2
- import copy
3
-
4
- import matplotlib.pyplot as plt
5
-
6
- # from numba import njit
7
- import numpy as np
8
- import pandas as pd
9
- import scipy.stats as stats
10
-
11
-
12
- from skimage import measure
13
- from sklearn.linear_model import LinearRegression
14
- from sklearn.model_selection import KFold
15
- import statsmodels.api as sm
16
-
17
-
18
- def fitlm(x, y):
19
- return sm.OLS(y, sm.add_constant(x)).fit()
20
-
21
-
22
- def fitlm_kfold(x, y, kfold_splits=5):
23
- model = LinearRegression()
24
- if isinstance(x, type(np.array([]))) or isinstance(x, type([])):
25
- x = pd.DataFrame(x)
26
- if isinstance(y, type(np.array([]))) or isinstance(y, type([])):
27
- y = pd.DataFrame(y)
28
- scores, coeffs = [], np.zeros(x.shape[1])
29
- kfold = KFold(n_splits=kfold_splits, shuffle=True, random_state=42)
30
- for i, (train, test) in enumerate(kfold.split(x, y)):
31
- model.fit(x.iloc[train, :], y.iloc[train, :])
32
- score = model.score(x.iloc[test, :], y.iloc[test, :])
33
- # mdl = fitlm(np.squeeze(y.iloc[test,:].transpose()), np.squeeze(model.predict(x.iloc[test, :])))
34
- scores.append(score)
35
- coeffs = np.vstack((coeffs, model.coef_))
36
- coeffs = list(np.delete(coeffs, 0))
37
- return scores, coeffs, model, ["scores", "coeffs", "model"]
38
-
39
-
40
- def zscore(data):
41
- return (data - data.mean()) / data.std()
42
-
43
-
44
- def permutationTestSpearmansRho(x, y, plot_distr=True, x_unit=None, p=5000):
45
- """
46
- Calculate permutation test for multiple repetitions of Spearmans Rho
47
- https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
48
-
49
- x (np array) : first distibution e.g. R^2
50
- y (np array) : second distribution e.g. UPDRS
51
- plot_distr (boolean) : if True: permutation histplot and ground truth will be
52
- plotted
53
- x_unit (str) : histplot xlabel
54
- p (int): number of permutations
55
-
56
- returns:
57
- gT (float) : estimated ground truth, here spearman's rho
58
- p (float) : p value of permutation test
59
- """
60
-
61
- # compute ground truth difference
62
- gT = stats.spearmanr(x, y)[0]
63
- #
64
- pV = np.array((x, y))
65
- # Initialize permutation:
66
- pD = []
67
- # Permutation loop:
68
- args_order = np.arange(0, pV.shape[1], 1)
69
- args_order_2 = np.arange(0, pV.shape[1], 1)
70
- for i in range(0, p):
71
- # Shuffle the data:
72
- random.shuffle(args_order)
73
- random.shuffle(args_order_2)
74
- # Compute permuted absolute difference of your two sampled
75
- # distributions and store it in pD:
76
- pD.append(stats.spearmanr(pV[0, args_order], pV[1, args_order_2])[0])
77
-
78
- # calculate p value
79
- if gT < 0:
80
- p_val = len(np.where(pD <= gT)[0]) / p
81
- else:
82
- p_val = len(np.where(pD >= gT)[0]) / p
83
-
84
- if plot_distr is True:
85
- plt.hist(pD, bins=30, label="permutation results")
86
- plt.axvline(gT, color="orange", label="ground truth")
87
- plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
88
- plt.xlabel(x_unit)
89
- plt.legend()
90
- plt.show()
91
- return gT, p_val
92
-
93
-
94
- def permutationTest(x, y, plot_distr=True, x_unit=None, p=5000):
95
- """
96
- Calculate permutation test
97
- https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
98
-
99
- x (np array) : first distr.
100
- y (np array) : first distr.
101
- plot_distr (boolean) : if True: plot permutation histplot and ground truth
102
- x_unit (str) : histplot xlabel
103
- p (int): number of permutations
104
-
105
- returns:
106
- gT (float) : estimated ground truth, here absolute difference of
107
- distribution means
108
- p (float) : p value of permutation test
109
-
110
- """
111
- # Compute ground truth difference
112
- gT = np.abs(np.average(x) - np.average(y))
113
-
114
- pV = np.concatenate((x, y), axis=0)
115
- pS = copy.copy(pV)
116
- # Initialize permutation:
117
- pD = []
118
- # Permutation loop:
119
- for i in range(0, p):
120
- # Shuffle the data:
121
- random.shuffle(pS)
122
- # Compute permuted absolute difference of your two sampled
123
- # distributions and store it in pD:
124
- pD.append(
125
- np.abs(
126
- np.average(pS[0 : int(len(pS) / 2)])
127
- - np.average(pS[int(len(pS) / 2) :])
128
- )
129
- )
130
-
131
- # Calculate p-value
132
- if gT < 0:
133
- p_val = len(np.where(pD <= gT)[0]) / p
134
- else:
135
- p_val = len(np.where(pD >= gT)[0]) / p
136
-
137
- if plot_distr is True:
138
- plt.hist(pD, bins=30, label="permutation results")
139
- plt.axvline(gT, color="orange", label="ground truth")
140
- plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
141
- plt.xlabel(x_unit)
142
- plt.legend()
143
- plt.show()
144
- return gT, p_val
145
-
146
-
147
- def permutationTest_relative(x, y, plot_distr=True, x_unit=None, p=5000):
148
- """
149
- Calculate permutation test
150
- https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
151
-
152
- x (np array) : first distr.
153
- y (np array) : first distr.
154
- plot_distr (boolean) : if True: plot permutation histplot and ground truth
155
- x_unit (str) : histplot xlabel
156
- p (int): number of permutations
157
-
158
- returns:
159
- gT (float) : estimated ground truth, here absolute difference of
160
- distribution means
161
- p (float) : p value of permutation test
162
-
163
- """
164
- gT = np.abs(np.average(x) - np.average(y))
165
- pD = []
166
- for i in range(0, p):
167
- l_ = []
168
- for i in range(x.shape[0]):
169
- if random.randint(0, 1) == 1:
170
- l_.append((x[i], y[i]))
171
- else:
172
- l_.append((y[i], x[i]))
173
- pD.append(
174
- np.abs(
175
- np.average(np.array(l_)[:, 0]) - np.average(np.array(l_)[:, 1])
176
- )
177
- )
178
- if gT < 0:
179
- p_val = len(np.where(pD <= gT)[0]) / p
180
- else:
181
- p_val = len(np.where(pD >= gT)[0]) / p
182
-
183
- if plot_distr is True:
184
- plt.hist(pD, bins=30, label="permutation results")
185
- plt.axvline(gT, color="orange", label="ground truth")
186
- plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
187
- plt.xlabel(x_unit)
188
- plt.legend()
189
- plt.show()
190
-
191
- return gT, p_val
192
-
193
-
194
- # @njit
195
- def permutation_numba_onesample(x, y, n_perm, two_tailed=True):
196
- """Perform permutation test with one-sample distribution.
197
-
198
- Parameters
199
- ----------
200
- x : array_like
201
- First distribution
202
- y : int or float
203
- Baseline against which to check for statistical significane
204
- n_perm : int
205
- Number of permutations
206
- two_tailed : bool, default: True
207
- Set to False if you would like to perform a one-sampled permutation
208
- test, else True
209
- two_tailed : bool, default: True
210
- Set to False if you would like to perform a one-tailed permutation
211
- test, else True
212
-
213
- Returns
214
- -------
215
- float
216
- Estimated difference of distribution from baseline
217
- float
218
- P-value of permutation test
219
- """
220
- if two_tailed is True:
221
- zeroed = x - y
222
- z = np.abs(np.mean(zeroed))
223
- p = np.empty(n_perm)
224
- # Run the simulation n_perm times
225
- for i in np.arange(n_perm):
226
- sign = np.random.choice(
227
- a=np.array([-1.0, 1.0]), size=len(x), replace=True
228
- )
229
- p[i] = np.abs(np.mean(zeroed * sign))
230
- else:
231
- zeroed = x - y
232
- z = np.mean(zeroed)
233
- p = np.empty(n_perm)
234
- # Run the simulation n_perm times
235
- for i in np.arange(n_perm):
236
- sign = np.random.choice(
237
- a=np.array([-1.0, 1.0]), size=len(x), replace=True
238
- )
239
- p[i] = np.mean(zeroed * sign)
240
- # Return p-value
241
- return z, (np.sum(p >= z)) / n_perm
242
-
243
-
244
- # @njit
245
- def permutation_numba_twosample(x, y, n_perm, two_tailed=True):
246
- """Perform permutation test.
247
-
248
- Parameters
249
- ----------
250
- x : array_like
251
- First distribution
252
- y : array_like
253
- Second distribution
254
- n_perm : int
255
- Number of permutations
256
- two_tailed : bool, default: True
257
- Set to False if you would like to perform a one-sampled permutation
258
- test, else True
259
- two_tailed : bool, default: True
260
- Set to False if you would like to perform a one-tailed permutation
261
- test, else True
262
-
263
- Returns
264
- -------
265
- float
266
- Estimated difference of distribution means
267
- float
268
- P-value of permutation test
269
- """
270
- if two_tailed is True:
271
- z = np.abs(np.mean(x) - np.mean(y))
272
- pS = np.concatenate((x, y), axis=0)
273
- half = int(len(pS) / 2)
274
- p = np.empty(n_perm)
275
- # Run the simulation n_perm times
276
- for i in np.arange(0, n_perm):
277
- # Shuffle the data
278
- np.random.shuffle(pS)
279
- # Compute permuted absolute difference of the two sampled
280
- # distributions
281
- p[i] = np.abs(np.mean(pS[:half]) - np.mean(pS[half:]))
282
- else:
283
- z = np.mean(x) - np.mean(y)
284
- pS = np.concatenate((x, y), axis=0)
285
- half = int(len(pS) / 2)
286
- p = np.empty(n_perm)
287
- # Run the simulation n_perm times
288
- for i in np.arange(0, n_perm):
289
- # Shuffle the data
290
- np.random.shuffle(pS)
291
- # Compute permuted absolute difference of the two sampled
292
- # distributions
293
- p[i] = np.mean(pS[:half]) - np.mean(pS[half:])
294
- return z, (np.sum(p >= z)) / n_perm
295
-
296
-
297
- def cluster_wise_p_val_correction(p_arr, p_sig=0.05, num_permutations=10000):
298
- """Obtain cluster-wise corrected p values.
299
-
300
- Based on: https://github.com/neuromodulation/wjn_toolbox/blob/4745557040ad26f3b8498ca5d0c5d5dece2d3ba1/mypcluster.m
301
- https://garstats.wordpress.com/2018/09/06/cluster/
302
-
303
- Arguments
304
- ---------
305
- p_arr (np.array) : ndim, can be time series or image
306
- p_sig (float) : significance level
307
- num_permutations (int) : no. of random permutations of cluster comparisons
308
-
309
- Returns
310
- -------
311
- p (float) : significance level of highest cluster
312
- p_min_index : indices of significant samples
313
- """
314
- labels, num_clusters = measure.label(p_arr <= p_sig, return_num=True)
315
-
316
- # loop through clusters of p_val series or image
317
- index_cluster = {}
318
- p_cluster_sum = np.zeros(num_clusters)
319
- for cluster_i in np.arange(num_clusters):
320
- # first cluster is assigned to be 1 from measure.label
321
- index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[0]
322
- p_cluster_sum[cluster_i] = np.sum(
323
- np.array(1 - p_arr)[index_cluster[cluster_i]]
324
- )
325
- # p_min corresponds to the most unlikely cluster
326
- p_min = np.max(p_cluster_sum)
327
-
328
- p_min_index = index_cluster[np.argmax(p_cluster_sum)]
329
-
330
- # loop through random permutation cycles
331
- r_per_arr = np.zeros(num_permutations)
332
- for r in range(num_permutations):
333
- r_per = np.random.randint(
334
- low=0, high=p_arr.shape[0], size=p_arr.shape[0]
335
- )
336
-
337
- labels, num_clusters = measure.label(
338
- p_arr[r_per] <= p_sig, return_num=True
339
- )
340
-
341
- index_cluster = {}
342
- if num_clusters == 0:
343
- r_per_arr[r] = 0
344
- else:
345
- p_cluster_sum = np.zeros(num_clusters)
346
- for cluster_i in np.arange(num_clusters):
347
- index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[
348
- 0
349
- ] # first cluster is assigned to be 1 from measure.label
350
- p_cluster_sum[cluster_i] = np.sum(
351
- np.array(1 - p_arr[r_per])[index_cluster[cluster_i]]
352
- )
353
- # corresponds to the most unlikely cluster
354
- r_per_arr[r] = np.max(p_cluster_sum)
355
-
356
- sorted_r = np.sort(r_per_arr)
357
-
358
- def find_arg_nearest(array, value):
359
- array = np.asarray(array)
360
- idx = (np.abs(array - value)).argmin()
361
- return idx
362
-
363
- p = 1 - find_arg_nearest(sorted_r, p_min) / num_permutations
364
-
365
- return p, p_min_index
366
-
367
-
368
- # @njit
369
- def cluster_wise_p_val_correction_numba(p_arr, p_sig, n_perm):
370
- """Calculate significant clusters and their corresponding p-values.
371
-
372
- Based on:
373
- https://github.com/neuromodulation/wjn_toolbox/blob/4745557040ad26f3b8498ca5d0c5d5dece2d3ba1/mypcluster.m
374
- https://garstats.wordpress.com/2018/09/06/cluster/
375
-
376
- Arguments
377
- ---------
378
- p_arr : array-like
379
- Array of p-values. WARNING: MUST be one-dimensional
380
- p_sig : float
381
- Significance level
382
- n_perm : int
383
- No. of random permutations for building cluster null-distribution
384
-
385
- Returns
386
- -------
387
- p : list of floats
388
- List of p-values for each cluster
389
- p_min_index : list of numpy array
390
- List of indices of each significant cluster
391
- """
392
-
393
- def cluster(iterable):
394
- """Cluster 1-D array of boolean values.
395
-
396
- Parameters
397
- ----------
398
- iterable : array-like of bool
399
- Array to be clustered.
400
-
401
- Returns
402
- -------
403
- cluster_labels : np.array
404
- Array of shape (len(iterable), 1), where each value indicates the
405
- number of the cluster. Values are 0 if the item does not belong to
406
- a cluster
407
- cluster_count : int
408
- Number of detected cluster. Corresponds to the highest value in
409
- cluster_labels
410
- """
411
- cluster_labels = np.zeros((len(iterable), 1))
412
- cluster_count = 0
413
- cluster_len = 0
414
- for idx, item in enumerate(iterable):
415
- if item:
416
- cluster_labels[idx] = cluster_count + 1
417
- cluster_len += 1
418
- elif cluster_len == 0:
419
- pass
420
- else:
421
- cluster_len = 0
422
- cluster_count += 1
423
- if cluster_len >= 1:
424
- cluster_count += 1
425
- return cluster_labels, cluster_count
426
-
427
- def calculate_null_distribution(p_arr_, p_sig_, n_perm_):
428
- """Calculate null distribution of clusters.
429
-
430
- Parameters
431
- ----------
432
- p_arr_ : numpy array
433
- Array of p-values
434
- p_sig_ : float
435
- Significance level (p-value)
436
- n_perm_ : int
437
- No. of random permutations
438
-
439
- Returns
440
- -------
441
- r_per_arr : numpy array
442
- Null distribution of shape (n_perm_)
443
- """
444
- # loop through random permutation cycles
445
- r_per_arr = np.zeros(n_perm_)
446
- for r in range(n_perm_):
447
- r_per = np.random.randint(
448
- low=0, high=p_arr_.shape[0], size=p_arr_.shape[0]
449
- )
450
- labels_, n_clusters = cluster(p_arr_[r_per] <= p_sig_)
451
-
452
- cluster_ind = {}
453
- if n_clusters == 0:
454
- r_per_arr[r] = 0
455
- else:
456
- p_sum = np.zeros(n_clusters)
457
- for ind in range(n_clusters):
458
- cluster_ind[ind] = np.where(labels_ == ind + 1)[0]
459
- p_sum[ind] = np.sum(
460
- np.asarray(1 - p_arr_[r_per])[cluster_ind[ind]]
461
- )
462
- r_per_arr[r] = np.max(p_sum)
463
- return r_per_arr
464
-
465
- labels, num_clusters = cluster(p_arr <= p_sig)
466
-
467
- null_distr = calculate_null_distribution(p_arr, p_sig, n_perm)
468
- # Loop through clusters of p_val series or image
469
- clusters = []
470
- p_vals = [np.float64(x) for x in range(0)]
471
- # Cluster labels start at 1
472
- for cluster_i in range(num_clusters):
473
- index_cluster = np.where(labels == cluster_i + 1)[0]
474
- p_cluster_sum = np.sum(np.asarray(1 - p_arr)[index_cluster])
475
- p_val = 1 - np.sum(p_cluster_sum >= null_distr) / n_perm
476
- if p_val <= p_sig:
477
- clusters.append(index_cluster)
478
- p_vals.append(p_val)
479
-
480
- return p_vals, clusters
1
+ import random
2
+ import copy
3
+
4
+ import matplotlib.pyplot as plt
5
+
6
+ # from numba import njit
7
+ import numpy as np
8
+ import pandas as pd
9
+ import scipy.stats as stats
10
+
11
+
12
+ def fitlm_kfold(x, y, kfold_splits=5):
13
+ from sklearn.linear_model import LinearRegression
14
+ from sklearn.model_selection import KFold
15
+
16
+ model = LinearRegression()
17
+ if isinstance(x, type(np.array([]))) or isinstance(x, type([])):
18
+ x = pd.DataFrame(x)
19
+ if isinstance(y, type(np.array([]))) or isinstance(y, type([])):
20
+ y = pd.DataFrame(y)
21
+ scores, coeffs = [], np.zeros(x.shape[1])
22
+ kfold = KFold(n_splits=kfold_splits, shuffle=True, random_state=42)
23
+ for i, (train, test) in enumerate(kfold.split(x, y)):
24
+ model.fit(x.iloc[train, :], y.iloc[train, :])
25
+ score = model.score(x.iloc[test, :], y.iloc[test, :])
26
+ scores.append(score)
27
+ coeffs = np.vstack((coeffs, model.coef_))
28
+ coeffs = list(np.delete(coeffs, 0))
29
+ return scores, coeffs, model, ["scores", "coeffs", "model"]
30
+
31
+
32
+ def zscore(data):
33
+ return (data - data.mean()) / data.std()
34
+
35
+
36
+ def permutationTestSpearmansRho(x, y, plot_distr=True, x_unit=None, p=5000):
37
+ """
38
+ Calculate permutation test for multiple repetitions of Spearmans Rho
39
+ https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
40
+
41
+ x (np array) : first distibution e.g. R^2
42
+ y (np array) : second distribution e.g. UPDRS
43
+ plot_distr (boolean) : if True: permutation histplot and ground truth will be
44
+ plotted
45
+ x_unit (str) : histplot xlabel
46
+ p (int): number of permutations
47
+
48
+ returns:
49
+ gT (float) : estimated ground truth, here spearman's rho
50
+ p (float) : p value of permutation test
51
+ """
52
+
53
+ # compute ground truth difference
54
+ gT = stats.spearmanr(x, y)[0]
55
+ #
56
+ pV = np.array((x, y))
57
+ # Initialize permutation:
58
+ pD = []
59
+ # Permutation loop:
60
+ args_order = np.arange(0, pV.shape[1], 1)
61
+ args_order_2 = np.arange(0, pV.shape[1], 1)
62
+ for i in range(0, p):
63
+ # Shuffle the data:
64
+ random.shuffle(args_order)
65
+ random.shuffle(args_order_2)
66
+ # Compute permuted absolute difference of your two sampled
67
+ # distributions and store it in pD:
68
+ pD.append(stats.spearmanr(pV[0, args_order], pV[1, args_order_2])[0])
69
+
70
+ # calculate p value
71
+ if gT < 0:
72
+ p_val = len(np.where(pD <= gT)[0]) / p
73
+ else:
74
+ p_val = len(np.where(pD >= gT)[0]) / p
75
+
76
+ if plot_distr:
77
+ plt.hist(pD, bins=30, label="permutation results")
78
+ plt.axvline(gT, color="orange", label="ground truth")
79
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
80
+ plt.xlabel(x_unit)
81
+ plt.legend()
82
+ plt.show()
83
+ return gT, p_val
84
+
85
+
86
+ def permutationTest(x, y, plot_distr=True, x_unit=None, p=5000):
87
+ """
88
+ Calculate permutation test
89
+ https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
90
+
91
+ x (np array) : first distr.
92
+ y (np array) : first distr.
93
+ plot_distr (boolean) : if True: plot permutation histplot and ground truth
94
+ x_unit (str) : histplot xlabel
95
+ p (int): number of permutations
96
+
97
+ returns:
98
+ gT (float) : estimated ground truth, here absolute difference of
99
+ distribution means
100
+ p (float) : p value of permutation test
101
+
102
+ """
103
+ # Compute ground truth difference
104
+ gT = np.abs(np.average(x) - np.average(y))
105
+
106
+ pV = np.concatenate((x, y), axis=0)
107
+ pS = copy.copy(pV)
108
+ # Initialize permutation:
109
+ pD = []
110
+ # Permutation loop:
111
+ for i in range(0, p):
112
+ # Shuffle the data:
113
+ random.shuffle(pS)
114
+ # Compute permuted absolute difference of your two sampled
115
+ # distributions and store it in pD:
116
+ pD.append(
117
+ np.abs(
118
+ np.average(pS[0 : int(len(pS) / 2)])
119
+ - np.average(pS[int(len(pS) / 2) :])
120
+ )
121
+ )
122
+
123
+ # Calculate p-value
124
+ if gT < 0:
125
+ p_val = len(np.where(pD <= gT)[0]) / p
126
+ else:
127
+ p_val = len(np.where(pD >= gT)[0]) / p
128
+
129
+ if plot_distr:
130
+ plt.hist(pD, bins=30, label="permutation results")
131
+ plt.axvline(gT, color="orange", label="ground truth")
132
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
133
+ plt.xlabel(x_unit)
134
+ plt.legend()
135
+ plt.show()
136
+ return gT, p_val
137
+
138
+
139
+ def permutationTest_relative(x, y, plot_distr=True, x_unit=None, p=5000):
140
+ """
141
+ Calculate permutation test
142
+ https://towardsdatascience.com/how-to-assess-statistical-significance-in-your-data-with-permutation-tests-8bb925b2113d
143
+
144
+ x (np array) : first distr.
145
+ y (np array) : first distr.
146
+ plot_distr (boolean) : if True: plot permutation histplot and ground truth
147
+ x_unit (str) : histplot xlabel
148
+ p (int): number of permutations
149
+
150
+ returns:
151
+ gT (float) : estimated ground truth, here absolute difference of
152
+ distribution means
153
+ p (float) : p value of permutation test
154
+
155
+ """
156
+ gT = np.abs(np.average(x) - np.average(y))
157
+ pD = []
158
+ for i in range(0, p):
159
+ l_ = []
160
+ for i in range(x.shape[0]):
161
+ if random.randint(0, 1) == 1:
162
+ l_.append((x[i], y[i]))
163
+ else:
164
+ l_.append((y[i], x[i]))
165
+ pD.append(
166
+ np.abs(np.average(np.array(l_)[:, 0]) - np.average(np.array(l_)[:, 1]))
167
+ )
168
+ if gT < 0:
169
+ p_val = len(np.where(pD <= gT)[0]) / p
170
+ else:
171
+ p_val = len(np.where(pD >= gT)[0]) / p
172
+
173
+ if plot_distr:
174
+ plt.hist(pD, bins=30, label="permutation results")
175
+ plt.axvline(gT, color="orange", label="ground truth")
176
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
177
+ plt.xlabel(x_unit)
178
+ plt.legend()
179
+ plt.show()
180
+
181
+ return gT, p_val
182
+
183
+
184
+ # @njit
185
+ def permutation_numba_onesample(x, y, n_perm, two_tailed=True):
186
+ """Perform permutation test with one-sample distribution.
187
+
188
+ Parameters
189
+ ----------
190
+ x : array_like
191
+ First distribution
192
+ y : int or float
193
+ Baseline against which to check for statistical significane
194
+ n_perm : int
195
+ Number of permutations
196
+ two_tailed : bool, default: True
197
+ Set to False if you would like to perform a one-sampled permutation
198
+ test, else True
199
+ two_tailed : bool, default: True
200
+ Set to False if you would like to perform a one-tailed permutation
201
+ test, else True
202
+
203
+ Returns
204
+ -------
205
+ float
206
+ Estimated difference of distribution from baseline
207
+ float
208
+ P-value of permutation test
209
+ """
210
+ if two_tailed:
211
+ zeroed = x - y
212
+ z = np.abs(np.mean(zeroed))
213
+ p = np.empty(n_perm)
214
+ # Run the simulation n_perm times
215
+ for i in np.arange(n_perm):
216
+ sign = np.random.choice(a=np.array([-1.0, 1.0]), size=len(x), replace=True)
217
+ p[i] = np.abs(np.mean(zeroed * sign))
218
+ else:
219
+ zeroed = x - y
220
+ z = np.mean(zeroed)
221
+ p = np.empty(n_perm)
222
+ # Run the simulation n_perm times
223
+ for i in np.arange(n_perm):
224
+ sign = np.random.choice(a=np.array([-1.0, 1.0]), size=len(x), replace=True)
225
+ p[i] = np.mean(zeroed * sign)
226
+ # Return p-value
227
+ return z, (np.sum(p >= z)) / n_perm
228
+
229
+
230
+ # @njit
231
+ def permutation_numba_twosample(x, y, n_perm, two_tailed=True):
232
+ """Perform permutation test.
233
+
234
+ Parameters
235
+ ----------
236
+ x : array_like
237
+ First distribution
238
+ y : array_like
239
+ Second distribution
240
+ n_perm : int
241
+ Number of permutations
242
+ two_tailed : bool, default: True
243
+ Set to False if you would like to perform a one-sampled permutation
244
+ test, else True
245
+ two_tailed : bool, default: True
246
+ Set to False if you would like to perform a one-tailed permutation
247
+ test, else True
248
+
249
+ Returns
250
+ -------
251
+ float
252
+ Estimated difference of distribution means
253
+ float
254
+ P-value of permutation test
255
+ """
256
+ if two_tailed:
257
+ z = np.abs(np.mean(x) - np.mean(y))
258
+ pS = np.concatenate((x, y), axis=0)
259
+ half = int(len(pS) / 2)
260
+ p = np.empty(n_perm)
261
+ # Run the simulation n_perm times
262
+ for i in np.arange(0, n_perm):
263
+ # Shuffle the data
264
+ np.random.shuffle(pS)
265
+ # Compute permuted absolute difference of the two sampled
266
+ # distributions
267
+ p[i] = np.abs(np.mean(pS[:half]) - np.mean(pS[half:]))
268
+ else:
269
+ z = np.mean(x) - np.mean(y)
270
+ pS = np.concatenate((x, y), axis=0)
271
+ half = int(len(pS) / 2)
272
+ p = np.empty(n_perm)
273
+ # Run the simulation n_perm times
274
+ for i in np.arange(0, n_perm):
275
+ # Shuffle the data
276
+ np.random.shuffle(pS)
277
+ # Compute permuted absolute difference of the two sampled
278
+ # distributions
279
+ p[i] = np.mean(pS[:half]) - np.mean(pS[half:])
280
+ return z, (np.sum(p >= z)) / n_perm
281
+
282
+
283
+ def cluster_wise_p_val_correction(p_arr, p_sig=0.05, num_permutations=10000):
284
+ """Obtain cluster-wise corrected p values.
285
+
286
+ Based on: https://github.com/neuromodulation/wjn_toolbox/blob/4745557040ad26f3b8498ca5d0c5d5dece2d3ba1/mypcluster.m
287
+ https://garstats.wordpress.com/2018/09/06/cluster/
288
+
289
+ Arguments
290
+ ---------
291
+ p_arr (np.array) : ndim, can be time series or image
292
+ p_sig (float) : significance level
293
+ num_permutations (int) : no. of random permutations of cluster comparisons
294
+
295
+ Returns
296
+ -------
297
+ p (float) : significance level of highest cluster
298
+ p_min_index : indices of significant samples
299
+ """
300
+ from scipy.ndimage import label as measure_label
301
+
302
+ labels, num_clusters = measure_label(p_arr <= p_sig)
303
+
304
+ # loop through clusters of p_val series or image
305
+ index_cluster = {}
306
+ p_cluster_sum = np.zeros(num_clusters)
307
+ for cluster_i in np.arange(num_clusters):
308
+ # first cluster is assigned to be 1 from measure.label
309
+ index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[0]
310
+ p_cluster_sum[cluster_i] = np.sum(np.array(1 - p_arr)[index_cluster[cluster_i]])
311
+ # p_min corresponds to the most unlikely cluster
312
+ p_min = np.max(p_cluster_sum)
313
+
314
+ p_min_index = index_cluster[np.argmax(p_cluster_sum)]
315
+
316
+ # loop through random permutation cycles
317
+ r_per_arr = np.zeros(num_permutations)
318
+ for r in range(num_permutations):
319
+ r_per = np.random.randint(low=0, high=p_arr.shape[0], size=p_arr.shape[0])
320
+
321
+ labels, num_clusters = measure_label(p_arr[r_per] <= p_sig, return_num=True)
322
+
323
+ index_cluster = {}
324
+ if num_clusters == 0:
325
+ r_per_arr[r] = 0
326
+ else:
327
+ p_cluster_sum = np.zeros(num_clusters)
328
+ for cluster_i in np.arange(num_clusters):
329
+ index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[
330
+ 0
331
+ ] # first cluster is assigned to be 1 from measure.label
332
+ p_cluster_sum[cluster_i] = np.sum(
333
+ np.array(1 - p_arr[r_per])[index_cluster[cluster_i]]
334
+ )
335
+ # corresponds to the most unlikely cluster
336
+ r_per_arr[r] = np.max(p_cluster_sum)
337
+
338
+ sorted_r = np.sort(r_per_arr)
339
+
340
+ def find_arg_nearest(array, value):
341
+ array = np.asarray(array)
342
+ idx = (np.abs(array - value)).argmin()
343
+ return idx
344
+
345
+ p = 1 - find_arg_nearest(sorted_r, p_min) / num_permutations
346
+
347
+ return p, p_min_index
348
+
349
+
350
+ # @njit
351
+ def cluster_wise_p_val_correction_numba(p_arr, p_sig, n_perm):
352
+ """Calculate significant clusters and their corresponding p-values.
353
+
354
+ Based on:
355
+ https://github.com/neuromodulation/wjn_toolbox/blob/4745557040ad26f3b8498ca5d0c5d5dece2d3ba1/mypcluster.m
356
+ https://garstats.wordpress.com/2018/09/06/cluster/
357
+
358
+ Arguments
359
+ ---------
360
+ p_arr : array-like
361
+ Array of p-values. WARNING: MUST be one-dimensional
362
+ p_sig : float
363
+ Significance level
364
+ n_perm : int
365
+ No. of random permutations for building cluster null-distribution
366
+
367
+ Returns
368
+ -------
369
+ p : list of floats
370
+ List of p-values for each cluster
371
+ p_min_index : list of numpy array
372
+ List of indices of each significant cluster
373
+ """
374
+
375
+ def cluster(iterable):
376
+ """Cluster 1-D array of boolean values.
377
+
378
+ Parameters
379
+ ----------
380
+ iterable : array-like of bool
381
+ Array to be clustered.
382
+
383
+ Returns
384
+ -------
385
+ cluster_labels : np.ndarray
386
+ Array of shape (len(iterable), 1), where each value indicates the
387
+ number of the cluster. Values are 0 if the item does not belong to
388
+ a cluster
389
+ cluster_count : int
390
+ Number of detected cluster. Corresponds to the highest value in
391
+ cluster_labels
392
+ """
393
+ cluster_labels = np.zeros((len(iterable), 1))
394
+ cluster_count = 0
395
+ cluster_len = 0
396
+ for idx, item in enumerate(iterable):
397
+ if item:
398
+ cluster_labels[idx] = cluster_count + 1
399
+ cluster_len += 1
400
+ elif cluster_len == 0:
401
+ pass
402
+ else:
403
+ cluster_len = 0
404
+ cluster_count += 1
405
+ if cluster_len >= 1:
406
+ cluster_count += 1
407
+ return cluster_labels, cluster_count
408
+
409
+ def calculate_null_distribution(p_arr_, p_sig_, n_perm_):
410
+ """Calculate null distribution of clusters.
411
+
412
+ Parameters
413
+ ----------
414
+ p_arr_ : numpy array
415
+ Array of p-values
416
+ p_sig_ : float
417
+ Significance level (p-value)
418
+ n_perm_ : int
419
+ No. of random permutations
420
+
421
+ Returns
422
+ -------
423
+ r_per_arr : numpy array
424
+ Null distribution of shape (n_perm_)
425
+ """
426
+ # loop through random permutation cycles
427
+ r_per_arr = np.zeros(n_perm_)
428
+ for r in range(n_perm_):
429
+ r_per = np.random.randint(low=0, high=p_arr_.shape[0], size=p_arr_.shape[0])
430
+ labels_, n_clusters = cluster(p_arr_[r_per] <= p_sig_)
431
+
432
+ cluster_ind = {}
433
+ if n_clusters == 0:
434
+ r_per_arr[r] = 0
435
+ else:
436
+ p_sum = np.zeros(n_clusters)
437
+ for ind in range(n_clusters):
438
+ cluster_ind[ind] = np.where(labels_ == ind + 1)[0]
439
+ p_sum[ind] = np.sum(np.asarray(1 - p_arr_[r_per])[cluster_ind[ind]])
440
+ r_per_arr[r] = np.max(p_sum)
441
+ return r_per_arr
442
+
443
+ labels, num_clusters = cluster(p_arr <= p_sig)
444
+
445
+ null_distr = calculate_null_distribution(p_arr, p_sig, n_perm)
446
+ # Loop through clusters of p_val series or image
447
+ clusters = []
448
+ p_vals = [np.float64(x) for x in range(0)]
449
+ # Cluster labels start at 1
450
+ for cluster_i in range(num_clusters):
451
+ index_cluster = np.where(labels == cluster_i + 1)[0]
452
+ p_cluster_sum = np.sum(np.asarray(1 - p_arr)[index_cluster])
453
+ p_val = 1 - np.sum(p_cluster_sum >= null_distr) / n_perm
454
+ if p_val <= p_sig:
455
+ clusters.append(index_cluster)
456
+ p_vals.append(p_val)
457
+
458
+ return p_vals, clusters