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
|
@@ -1,394 +1,370 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
self.
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
self.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
self.
|
|
37
|
-
self.
|
|
38
|
-
|
|
39
|
-
self.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
self.
|
|
44
|
-
|
|
45
|
-
self.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.
|
|
52
|
-
self.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
self.
|
|
56
|
-
self.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
self.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
self.
|
|
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
|
-
self.ecog_channels
|
|
233
|
-
self.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
self.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
for
|
|
257
|
-
self.
|
|
258
|
-
[
|
|
259
|
-
ch_idx
|
|
260
|
-
for ch_idx, ch in enumerate(
|
|
261
|
-
if ch.startswith(
|
|
262
|
-
]
|
|
263
|
-
)
|
|
264
|
-
self.
|
|
265
|
-
[
|
|
266
|
-
ch
|
|
267
|
-
for _, ch in enumerate(
|
|
268
|
-
if ch.startswith(
|
|
269
|
-
]
|
|
270
|
-
)
|
|
271
|
-
if self.
|
|
272
|
-
# get feature_names; given by
|
|
273
|
-
self.feature_names = [
|
|
274
|
-
feature_name[len(self.
|
|
275
|
-
for feature_name in self.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
Parameters
|
|
373
|
-
----------
|
|
374
|
-
dat_cortex : np.ndarray, optional
|
|
375
|
-
cortical features, by default None
|
|
376
|
-
dat_subcortex : np.ndarray, optional
|
|
377
|
-
subcortical features, by default None
|
|
378
|
-
|
|
379
|
-
Returns
|
|
380
|
-
-------
|
|
381
|
-
proj_cortex : np.ndarray
|
|
382
|
-
projected cortical features, by detault None
|
|
383
|
-
proj_subcortex : np.ndarray
|
|
384
|
-
projected subcortical features, by detault None
|
|
385
|
-
"""
|
|
386
|
-
proj_cortex = None
|
|
387
|
-
proj_subcortex = None
|
|
388
|
-
|
|
389
|
-
if dat_cortex is not None:
|
|
390
|
-
proj_cortex = self.proj_matrix_cortex @ dat_cortex
|
|
391
|
-
if dat_subcortex is not None:
|
|
392
|
-
proj_subcortex = self.proj_matrix_subcortex @ dat_subcortex
|
|
393
|
-
|
|
394
|
-
return proj_cortex, proj_subcortex
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from py_neuromodulation.nm_types import NMBaseModel
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from py_neuromodulation.nm_settings import NMSettings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProjectionSettings(NMBaseModel):
|
|
12
|
+
max_dist_mm: float = Field(default=20.0, gt=0.0)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Projection:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
settings: "NMSettings",
|
|
19
|
+
grid_cortex: pd.DataFrame,
|
|
20
|
+
grid_subcortex: pd.DataFrame,
|
|
21
|
+
coords: dict,
|
|
22
|
+
nm_channels: pd.DataFrame,
|
|
23
|
+
plot_projection: bool = False,
|
|
24
|
+
) -> None:
|
|
25
|
+
self.grid_cortex = grid_cortex
|
|
26
|
+
self.grid_subcortex = grid_subcortex
|
|
27
|
+
self.coords = coords
|
|
28
|
+
self.nm_channels = nm_channels
|
|
29
|
+
self.project_cortex = settings.postprocessing.project_cortex
|
|
30
|
+
self.project_subcortex = settings.postprocessing.project_subcortex
|
|
31
|
+
self.max_dist_cortex = settings.project_cortex_settings.max_dist_mm
|
|
32
|
+
self.max_dist_subcortex = settings.project_subcortex_settings.max_dist_mm
|
|
33
|
+
self.ecog_channels: list # None case never handled, no need for default value
|
|
34
|
+
self.lfp_channels: list # None case never handled, no need for default value
|
|
35
|
+
|
|
36
|
+
self.idx_chs_ecog: list = [] # feature series indexes for ecog channels
|
|
37
|
+
self.names_chs_ecog: list = [] # feature series name of ecog features
|
|
38
|
+
self.idx_chs_lfp: list = [] # feature series indexes for lfp channels
|
|
39
|
+
self.names_chs_lfp: list = [] # feature series name of lfp features
|
|
40
|
+
self.feature_names: list | None = None
|
|
41
|
+
self.initialized: bool = False
|
|
42
|
+
|
|
43
|
+
self.remove_not_used_ch_from_coords() # remove beforehand non used channels from coords
|
|
44
|
+
|
|
45
|
+
if len(self.coords["cortex_left"]["positions"]) == 0:
|
|
46
|
+
self.sess_right = True
|
|
47
|
+
self.ecog_strip = self.coords["cortex_right"]["positions"]
|
|
48
|
+
self.ecog_strip_names = self.coords["cortex_right"]["ch_names"]
|
|
49
|
+
elif len(self.coords["cortex_right"]["positions"]) == 0:
|
|
50
|
+
self.sess_right = False
|
|
51
|
+
self.ecog_strip = self.coords["cortex_left"]["positions"]
|
|
52
|
+
self.ecog_strip_names = self.coords["cortex_left"]["ch_names"]
|
|
53
|
+
|
|
54
|
+
if self.sess_right and len(self.coords["subcortex_right"]["positions"]) > 0:
|
|
55
|
+
self.lfp_elec = self.coords["subcortex_right"]["positions"]
|
|
56
|
+
self.lfp_elec_names = self.coords["subcortex_right"]["ch_names"]
|
|
57
|
+
elif (
|
|
58
|
+
self.sess_right is False
|
|
59
|
+
and len(self.coords["subcortex_left"]["positions"]) > 0
|
|
60
|
+
):
|
|
61
|
+
self.lfp_elec = self.coords["subcortex_left"]["positions"]
|
|
62
|
+
self.lfp_elec_names = self.coords["subcortex_left"]["ch_names"]
|
|
63
|
+
|
|
64
|
+
self._initialize_channels()
|
|
65
|
+
|
|
66
|
+
(
|
|
67
|
+
self.proj_matrix_cortex,
|
|
68
|
+
self.proj_matrix_subcortex,
|
|
69
|
+
) = self.calc_projection_matrix()
|
|
70
|
+
|
|
71
|
+
if self.project_cortex:
|
|
72
|
+
self.active_cortex_gridpoints = np.nonzero(
|
|
73
|
+
self.proj_matrix_cortex.sum(axis=1)
|
|
74
|
+
)[0]
|
|
75
|
+
if self.project_subcortex:
|
|
76
|
+
self.active_subcortex_gridpoints = np.nonzero(
|
|
77
|
+
self.proj_matrix_subcortex.sum(axis=1)
|
|
78
|
+
)[0]
|
|
79
|
+
|
|
80
|
+
if plot_projection:
|
|
81
|
+
from py_neuromodulation.nm_plots import NM_Plot
|
|
82
|
+
|
|
83
|
+
nmplotter = NM_Plot(
|
|
84
|
+
ecog_strip=self.ecog_strip,
|
|
85
|
+
grid_cortex=self.grid_cortex.to_numpy(),
|
|
86
|
+
grid_subcortex=self.grid_subcortex.to_numpy(),
|
|
87
|
+
sess_right=self.sess_right,
|
|
88
|
+
proj_matrix_cortex=self.proj_matrix_cortex,
|
|
89
|
+
)
|
|
90
|
+
nmplotter.plot_cortex()
|
|
91
|
+
|
|
92
|
+
def remove_not_used_ch_from_coords(self):
|
|
93
|
+
ch_not_used = self.nm_channels.query('(used==0) or (status=="bad")').name
|
|
94
|
+
if len(ch_not_used) > 0:
|
|
95
|
+
for ch in ch_not_used:
|
|
96
|
+
for key_ in self.coords:
|
|
97
|
+
for idx, ch_coords in enumerate(self.coords[key_]["ch_names"]):
|
|
98
|
+
if ch.startswith(ch_coords):
|
|
99
|
+
# delete index
|
|
100
|
+
self.coords[key_]["positions"] = np.delete(
|
|
101
|
+
self.coords[key_]["positions"], idx, axis=0
|
|
102
|
+
)
|
|
103
|
+
self.coords[key_]["ch_names"].remove(ch)
|
|
104
|
+
|
|
105
|
+
def calc_proj_matrix(
|
|
106
|
+
self,
|
|
107
|
+
max_dist: float,
|
|
108
|
+
grid: np.ndarray,
|
|
109
|
+
coord_array: np.ndarray,
|
|
110
|
+
) -> np.ndarray:
|
|
111
|
+
"""Calculate projection matrix."""
|
|
112
|
+
|
|
113
|
+
channels = coord_array.shape[0]
|
|
114
|
+
distance_matrix = np.zeros([grid.shape[1], channels])
|
|
115
|
+
|
|
116
|
+
for project_point in range(grid.shape[1]):
|
|
117
|
+
for channel in range(coord_array.shape[0]):
|
|
118
|
+
distance_matrix[project_point, channel] = np.linalg.norm(
|
|
119
|
+
grid[:, project_point] - coord_array[channel, :]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
proj_matrix = np.zeros(distance_matrix.shape)
|
|
123
|
+
for grid_point in range(distance_matrix.shape[0]):
|
|
124
|
+
used_channels = np.where(distance_matrix[grid_point, :] < max_dist)[0]
|
|
125
|
+
|
|
126
|
+
rec_distances = distance_matrix[grid_point, used_channels]
|
|
127
|
+
sum_distances: float = np.sum(1 / rec_distances)
|
|
128
|
+
|
|
129
|
+
for _, used_channel in enumerate(used_channels):
|
|
130
|
+
proj_matrix[grid_point, used_channel] = (
|
|
131
|
+
1 / distance_matrix[grid_point, used_channel]
|
|
132
|
+
) / sum_distances
|
|
133
|
+
return proj_matrix
|
|
134
|
+
|
|
135
|
+
def calc_projection_matrix(self):
|
|
136
|
+
"""Calculates a projection matrix based on the used coordinate arrays
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
proj_matrix_cortex (np.array)
|
|
141
|
+
cortical projection_matrix in shape [grid contacts, channel contact] defaults to None
|
|
142
|
+
proj_matrix_subcortex (np.array)
|
|
143
|
+
subcortical projection_matrix in shape [grid contacts, channel contact] defaults to None
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
proj_matrix_run = np.empty(2, dtype=object)
|
|
147
|
+
|
|
148
|
+
if self.sess_right:
|
|
149
|
+
if self.project_cortex:
|
|
150
|
+
cortex_grid_right = np.copy(self.grid_cortex)
|
|
151
|
+
cortex_grid_right[:, 0] = cortex_grid_right[:, 0] * -1
|
|
152
|
+
self.cortex_grid_right = np.array(cortex_grid_right.T)
|
|
153
|
+
else:
|
|
154
|
+
self.cortex_grid_right = None
|
|
155
|
+
|
|
156
|
+
if self.project_subcortex:
|
|
157
|
+
subcortex_grid_right = np.copy(self.grid_subcortex)
|
|
158
|
+
subcortex_grid_right[:, 0] = subcortex_grid_right[:, 0] * -1
|
|
159
|
+
self.subcortex_grid_right = np.array(subcortex_grid_right).T
|
|
160
|
+
else:
|
|
161
|
+
self.subcortex_grid_right = None
|
|
162
|
+
|
|
163
|
+
grid_session = [self.cortex_grid_right, self.subcortex_grid_right]
|
|
164
|
+
|
|
165
|
+
else:
|
|
166
|
+
if self.project_cortex:
|
|
167
|
+
self.cortex_grid_left = np.array(self.grid_cortex.T)
|
|
168
|
+
else:
|
|
169
|
+
self.cortex_grid_left = None
|
|
170
|
+
if self.project_subcortex:
|
|
171
|
+
self.subcortex_grid_left = np.array(self.grid_subcortex.T)
|
|
172
|
+
else:
|
|
173
|
+
self.subcortex_grid_left = None
|
|
174
|
+
|
|
175
|
+
grid_session = [self.cortex_grid_left, self.subcortex_grid_left]
|
|
176
|
+
|
|
177
|
+
coord_array = [
|
|
178
|
+
self.ecog_strip if grid_session[0] is not None else None,
|
|
179
|
+
self.lfp_elec if grid_session[1] is not None else None,
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
for loc_, grid in enumerate(grid_session):
|
|
183
|
+
if loc_ == 0: # cortex
|
|
184
|
+
max_dist = self.max_dist_cortex
|
|
185
|
+
elif loc_ == 1: # subcortex
|
|
186
|
+
max_dist = self.max_dist_subcortex
|
|
187
|
+
|
|
188
|
+
if grid_session[loc_] is not None:
|
|
189
|
+
proj_matrix_run[loc_] = self.calc_proj_matrix(
|
|
190
|
+
max_dist, grid, coord_array[loc_]
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return proj_matrix_run[0], proj_matrix_run[1] # cortex, subcortex
|
|
194
|
+
|
|
195
|
+
def _initialize_channels(self) -> None:
|
|
196
|
+
"""Initialize channel names via nm_channel new_name column"""
|
|
197
|
+
|
|
198
|
+
if self.project_cortex:
|
|
199
|
+
self.ecog_channels = self.nm_channels.query(
|
|
200
|
+
'(type=="ecog") and (used == 1) and (status=="good")'
|
|
201
|
+
).name.to_list()
|
|
202
|
+
|
|
203
|
+
chs_ecog = self.ecog_channels.copy()
|
|
204
|
+
for ecog_channel in chs_ecog:
|
|
205
|
+
if ecog_channel not in self.ecog_strip_names:
|
|
206
|
+
self.ecog_channels.remove(ecog_channel)
|
|
207
|
+
# write ecog_channels to be new_name
|
|
208
|
+
self.ecog_channels = list(
|
|
209
|
+
self.nm_channels.query("name == @self.ecog_channels").new_name
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if self.project_subcortex:
|
|
213
|
+
self.lfp_channels = self.nm_channels.query(
|
|
214
|
+
'(type=="lfp" or type=="seeg" or type=="dbs") \
|
|
215
|
+
and (used == 1) and (status=="good")'
|
|
216
|
+
).name.to_list()
|
|
217
|
+
# project only channels that are in the coords
|
|
218
|
+
# this also deletes channels of the other hemisphere
|
|
219
|
+
chs_lfp = self.lfp_channels.copy()
|
|
220
|
+
for lfp_channel in chs_lfp:
|
|
221
|
+
if lfp_channel not in self.lfp_elec_names:
|
|
222
|
+
self.lfp_channels.remove(lfp_channel)
|
|
223
|
+
# write lfp_channels to be new_name
|
|
224
|
+
self.lfp_channels = list(
|
|
225
|
+
self.nm_channels.query("name == @self.lfp_channels").new_name
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def init_projection_run(self, feature_dict: dict) -> None:
|
|
229
|
+
"""Initialize indexes for respective channels in feature series computed by nm_features.py"""
|
|
230
|
+
# here it is assumed that only one hemisphere is recorded at a time!
|
|
231
|
+
if self.project_cortex:
|
|
232
|
+
for ecog_channel in self.ecog_channels:
|
|
233
|
+
self.idx_chs_ecog.append(
|
|
234
|
+
[
|
|
235
|
+
ch_idx
|
|
236
|
+
for ch_idx, ch in enumerate(feature_dict.keys())
|
|
237
|
+
if ch.startswith(ecog_channel)
|
|
238
|
+
]
|
|
239
|
+
)
|
|
240
|
+
self.names_chs_ecog.append(
|
|
241
|
+
[
|
|
242
|
+
ch
|
|
243
|
+
for _, ch in enumerate(feature_dict.keys())
|
|
244
|
+
if ch.startswith(ecog_channel)
|
|
245
|
+
]
|
|
246
|
+
)
|
|
247
|
+
if self.names_chs_ecog:
|
|
248
|
+
# get feature_names; given by ECoG sequency of features
|
|
249
|
+
self.feature_names = [
|
|
250
|
+
feature_name[len(self.ecog_channels[0]) + 1 :]
|
|
251
|
+
for feature_name in self.names_chs_ecog[0]
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
if self.project_subcortex:
|
|
255
|
+
# for lfp_channels select here only the ones from the correct hemisphere!
|
|
256
|
+
for lfp_channel in self.lfp_channels:
|
|
257
|
+
self.idx_chs_lfp.append(
|
|
258
|
+
[
|
|
259
|
+
ch_idx
|
|
260
|
+
for ch_idx, ch in enumerate(feature_dict.keys())
|
|
261
|
+
if ch.startswith(lfp_channel)
|
|
262
|
+
]
|
|
263
|
+
)
|
|
264
|
+
self.names_chs_lfp.append(
|
|
265
|
+
[
|
|
266
|
+
ch
|
|
267
|
+
for _, ch in enumerate(feature_dict.keys())
|
|
268
|
+
if ch.startswith(lfp_channel)
|
|
269
|
+
]
|
|
270
|
+
)
|
|
271
|
+
if not self.feature_names and self.names_chs_lfp:
|
|
272
|
+
# get feature_names; given by LFP sequency of features
|
|
273
|
+
self.feature_names = [
|
|
274
|
+
feature_name[len(self.lfp_channels[0]) + 1 :]
|
|
275
|
+
for feature_name in self.names_chs_lfp[0]
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
self.initialized = True
|
|
279
|
+
|
|
280
|
+
def project_features(self, feature_dict: dict) -> None:
|
|
281
|
+
"""Project data, given idx_chs_ecog/stn"""
|
|
282
|
+
|
|
283
|
+
if not self.initialized:
|
|
284
|
+
self.init_projection_run(feature_dict)
|
|
285
|
+
|
|
286
|
+
dat_cortex = (
|
|
287
|
+
np.array(
|
|
288
|
+
[
|
|
289
|
+
np.array([feature_dict[ch] for ch in ch_names])
|
|
290
|
+
for ch_names in self.names_chs_ecog
|
|
291
|
+
]
|
|
292
|
+
)
|
|
293
|
+
if self.project_cortex
|
|
294
|
+
else None
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
dat_subcortex = (
|
|
298
|
+
np.array(
|
|
299
|
+
[
|
|
300
|
+
np.array([feature_dict[ch] for ch in ch_names])
|
|
301
|
+
for ch_names in self.names_chs_lfp
|
|
302
|
+
]
|
|
303
|
+
)
|
|
304
|
+
if self.project_subcortex
|
|
305
|
+
else None
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# project data
|
|
309
|
+
# get_projected_cortex_subcortex_data can return None
|
|
310
|
+
# but None is not handled and will throw error in the code below
|
|
311
|
+
|
|
312
|
+
proj_cortex_array: np.ndarray
|
|
313
|
+
proj_subcortex_array: np.ndarray
|
|
314
|
+
(proj_cortex_array, proj_subcortex_array) = (
|
|
315
|
+
self.get_projected_cortex_subcortex_data(dat_cortex, dat_subcortex)
|
|
316
|
+
) # type: ignore # Ignore None return
|
|
317
|
+
|
|
318
|
+
features_new: dict = {}
|
|
319
|
+
# proj_cortex_array has shape grid_points x feature_number
|
|
320
|
+
if self.project_cortex:
|
|
321
|
+
features_new = features_new | {
|
|
322
|
+
"gridcortex_"
|
|
323
|
+
+ str(act_grid_point)
|
|
324
|
+
+ "_"
|
|
325
|
+
+ feature_name: proj_cortex_array[act_grid_point, feature_idx]
|
|
326
|
+
for feature_idx, feature_name in enumerate(self.feature_names) # type: ignore # Empty list handled above
|
|
327
|
+
for act_grid_point in self.active_cortex_gridpoints
|
|
328
|
+
}
|
|
329
|
+
if self.project_subcortex:
|
|
330
|
+
features_new = features_new | {
|
|
331
|
+
"gridsubcortex_"
|
|
332
|
+
+ str(act_grid_point)
|
|
333
|
+
+ "_"
|
|
334
|
+
+ feature_name: proj_subcortex_array[act_grid_point, feature_idx]
|
|
335
|
+
for feature_idx, feature_name in enumerate(self.feature_names) # type: ignore # Empty list handled above
|
|
336
|
+
for act_grid_point in self.active_subcortex_gridpoints
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
feature_dict.update(features_new)
|
|
340
|
+
|
|
341
|
+
def get_projected_cortex_subcortex_data(
|
|
342
|
+
self,
|
|
343
|
+
dat_cortex: np.ndarray | None = None,
|
|
344
|
+
dat_subcortex: np.ndarray | None = None,
|
|
345
|
+
) -> tuple[np.ndarray | None, np.ndarray | None]:
|
|
346
|
+
"""Project cortical and subcortical data to predefined projection matrices
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
dat_cortex : np.ndarray, optional
|
|
351
|
+
cortical features, by default None
|
|
352
|
+
dat_subcortex : np.ndarray, optional
|
|
353
|
+
subcortical features, by default None
|
|
354
|
+
|
|
355
|
+
Returns
|
|
356
|
+
-------
|
|
357
|
+
proj_cortex : np.ndarray
|
|
358
|
+
projected cortical features, by detault None
|
|
359
|
+
proj_subcortex : np.ndarray
|
|
360
|
+
projected subcortical features, by detault None
|
|
361
|
+
"""
|
|
362
|
+
proj_cortex = None
|
|
363
|
+
proj_subcortex = None
|
|
364
|
+
|
|
365
|
+
if dat_cortex is not None:
|
|
366
|
+
proj_cortex = self.proj_matrix_cortex @ dat_cortex
|
|
367
|
+
if dat_subcortex is not None:
|
|
368
|
+
proj_subcortex = self.proj_matrix_subcortex @ dat_subcortex
|
|
369
|
+
|
|
370
|
+
return proj_cortex, proj_subcortex
|