py-neuromodulation 0.0.3__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.
Files changed (233) hide show
  1. py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii +0 -0
  2. py_neuromodulation/ConnectivityDecoding/_get_grid_hull.m +34 -0
  3. py_neuromodulation/ConnectivityDecoding/_get_grid_whole_brain.py +95 -0
  4. py_neuromodulation/ConnectivityDecoding/_helper_write_connectome.py +107 -0
  5. py_neuromodulation/ConnectivityDecoding/mni_coords_cortical_surface.mat +0 -0
  6. py_neuromodulation/ConnectivityDecoding/mni_coords_whole_brain.mat +0 -0
  7. py_neuromodulation/ConnectivityDecoding/rmap_func_all.nii +0 -0
  8. py_neuromodulation/ConnectivityDecoding/rmap_struc.nii +0 -0
  9. py_neuromodulation/FieldTrip.py +589 -589
  10. py_neuromodulation/__init__.py +74 -13
  11. py_neuromodulation/_write_example_dataset_helper.py +83 -65
  12. py_neuromodulation/data/README +6 -0
  13. py_neuromodulation/data/dataset_description.json +8 -0
  14. py_neuromodulation/data/participants.json +32 -0
  15. py_neuromodulation/data/participants.tsv +2 -0
  16. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +5 -0
  17. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +11 -0
  18. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +11 -0
  19. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  20. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +18 -0
  21. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +35 -0
  22. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +13 -0
  23. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +2 -0
  24. py_neuromodulation/grid_cortex.tsv +40 -0
  25. py_neuromodulation/grid_subcortex.tsv +1429 -0
  26. py_neuromodulation/liblsl/libpugixml.so.1.12 +0 -0
  27. py_neuromodulation/liblsl/linux/bionic_amd64/liblsl.1.16.2.so +0 -0
  28. py_neuromodulation/liblsl/linux/bookworm_amd64/liblsl.1.16.2.so +0 -0
  29. py_neuromodulation/liblsl/linux/focal_amd46/liblsl.1.16.2.so +0 -0
  30. py_neuromodulation/liblsl/linux/jammy_amd64/liblsl.1.16.2.so +0 -0
  31. py_neuromodulation/liblsl/linux/jammy_x86/liblsl.1.16.2.so +0 -0
  32. py_neuromodulation/liblsl/linux/noble_amd64/liblsl.1.16.2.so +0 -0
  33. py_neuromodulation/liblsl/macos/amd64/liblsl.1.16.2.dylib +0 -0
  34. py_neuromodulation/liblsl/macos/arm64/liblsl.1.16.0.dylib +0 -0
  35. py_neuromodulation/liblsl/windows/amd64/liblsl.1.16.2.dll +0 -0
  36. py_neuromodulation/liblsl/windows/x86/liblsl.1.16.2.dll +0 -0
  37. py_neuromodulation/nm_IO.py +413 -417
  38. py_neuromodulation/nm_RMAP.py +496 -531
  39. py_neuromodulation/nm_analysis.py +993 -1074
  40. py_neuromodulation/nm_artifacts.py +30 -25
  41. py_neuromodulation/nm_bispectra.py +154 -168
  42. py_neuromodulation/nm_bursts.py +292 -198
  43. py_neuromodulation/nm_coherence.py +251 -205
  44. py_neuromodulation/nm_database.py +149 -0
  45. py_neuromodulation/nm_decode.py +918 -992
  46. py_neuromodulation/nm_define_nmchannels.py +300 -302
  47. py_neuromodulation/nm_features.py +144 -116
  48. py_neuromodulation/nm_filter.py +219 -219
  49. py_neuromodulation/nm_filter_preprocessing.py +79 -91
  50. py_neuromodulation/nm_fooof.py +139 -159
  51. py_neuromodulation/nm_generator.py +45 -37
  52. py_neuromodulation/nm_hjorth_raw.py +52 -73
  53. py_neuromodulation/nm_kalmanfilter.py +71 -58
  54. py_neuromodulation/nm_linelength.py +21 -33
  55. py_neuromodulation/nm_logger.py +66 -0
  56. py_neuromodulation/nm_mne_connectivity.py +149 -112
  57. py_neuromodulation/nm_mnelsl_generator.py +90 -0
  58. py_neuromodulation/nm_mnelsl_stream.py +116 -0
  59. py_neuromodulation/nm_nolds.py +96 -93
  60. py_neuromodulation/nm_normalization.py +173 -214
  61. py_neuromodulation/nm_oscillatory.py +423 -448
  62. py_neuromodulation/nm_plots.py +585 -612
  63. py_neuromodulation/nm_preprocessing.py +83 -0
  64. py_neuromodulation/nm_projection.py +370 -394
  65. py_neuromodulation/nm_rereference.py +97 -95
  66. py_neuromodulation/nm_resample.py +59 -50
  67. py_neuromodulation/nm_run_analysis.py +325 -435
  68. py_neuromodulation/nm_settings.py +289 -68
  69. py_neuromodulation/nm_settings.yaml +244 -0
  70. py_neuromodulation/nm_sharpwaves.py +423 -401
  71. py_neuromodulation/nm_stats.py +464 -480
  72. py_neuromodulation/nm_stream.py +398 -0
  73. py_neuromodulation/nm_stream_abc.py +166 -218
  74. py_neuromodulation/nm_types.py +193 -0
  75. py_neuromodulation/plots/STN_surf.mat +0 -0
  76. py_neuromodulation/plots/Vertices.mat +0 -0
  77. py_neuromodulation/plots/faces.mat +0 -0
  78. py_neuromodulation/plots/grid.mat +0 -0
  79. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info}/METADATA +185 -182
  80. py_neuromodulation-0.0.5.dist-info/RECORD +83 -0
  81. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info}/WHEEL +1 -2
  82. {py_neuromodulation-0.0.3.dist-info → py_neuromodulation-0.0.5.dist-info/licenses}/LICENSE +21 -21
  83. docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  84. docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -233
  85. docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  86. docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  87. docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  88. docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  89. docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  90. docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -68
  91. docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -239
  92. docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  93. docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -97
  94. docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  95. docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -192
  96. docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  97. docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +0 -76
  98. docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +0 -97
  99. docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +0 -240
  100. docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +0 -233
  101. docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +0 -63
  102. docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +0 -219
  103. docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +0 -210
  104. docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +0 -192
  105. docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +0 -219
  106. docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +0 -121
  107. docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +0 -68
  108. docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +0 -64
  109. docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +0 -189
  110. docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +0 -210
  111. docs/source/auto_examples/plot_0_first_demo.py +0 -189
  112. docs/source/auto_examples/plot_1_example_BIDS.py +0 -240
  113. docs/source/auto_examples/plot_2_example_add_feature.py +0 -76
  114. docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +0 -219
  115. docs/source/auto_examples/plot_4_example_gridPointProjection.py +0 -210
  116. docs/source/auto_examples/plot_5_example_rmap_computing.py +0 -64
  117. docs/source/auto_examples/plot_6_real_time_demo.py +0 -121
  118. docs/source/conf.py +0 -105
  119. examples/plot_0_first_demo.py +0 -189
  120. examples/plot_1_example_BIDS.py +0 -240
  121. examples/plot_2_example_add_feature.py +0 -76
  122. examples/plot_3_example_sharpwave_analysis.py +0 -219
  123. examples/plot_4_example_gridPointProjection.py +0 -210
  124. examples/plot_5_example_rmap_computing.py +0 -64
  125. examples/plot_6_real_time_demo.py +0 -121
  126. packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +0 -4
  127. packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +0 -104
  128. packages/realtime_decoding/build/lib/realtime_decoding/features.py +0 -163
  129. packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +0 -15
  130. packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +0 -345
  131. packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +0 -54
  132. packages/tmsi/build/lib/TMSiFileFormats/__init__.py +0 -37
  133. packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +0 -36
  134. packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +0 -200
  135. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +0 -496
  136. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +0 -236
  137. packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +0 -977
  138. packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +0 -35
  139. packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +0 -116
  140. packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +0 -294
  141. packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +0 -229
  142. packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +0 -102
  143. packages/tmsi/build/lib/TMSiPlotters/__init__.py +0 -2
  144. packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +0 -39
  145. packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +0 -234
  146. packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +0 -440
  147. packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +0 -44
  148. packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +0 -446
  149. packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +0 -589
  150. packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +0 -1326
  151. packages/tmsi/build/lib/TMSiSDK/__init__.py +0 -54
  152. packages/tmsi/build/lib/TMSiSDK/device.py +0 -588
  153. packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +0 -34
  154. packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +0 -1764
  155. packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +0 -34
  156. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +0 -1366
  157. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +0 -520
  158. packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +0 -165
  159. packages/tmsi/build/lib/TMSiSDK/error.py +0 -95
  160. packages/tmsi/build/lib/TMSiSDK/sample_data.py +0 -63
  161. packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +0 -99
  162. packages/tmsi/build/lib/TMSiSDK/settings.py +0 -45
  163. packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +0 -111
  164. packages/tmsi/build/lib/__init__.py +0 -4
  165. packages/tmsi/build/lib/apex_sdk/__init__.py +0 -34
  166. packages/tmsi/build/lib/apex_sdk/device/__init__.py +0 -41
  167. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +0 -1009
  168. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +0 -239
  169. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +0 -668
  170. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +0 -1611
  171. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +0 -38
  172. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +0 -57
  173. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +0 -44
  174. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +0 -150
  175. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +0 -36
  176. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +0 -48
  177. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +0 -108
  178. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +0 -39
  179. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +0 -77
  180. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +0 -150
  181. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +0 -129
  182. packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +0 -59
  183. packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +0 -57
  184. packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +0 -83
  185. packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +0 -201
  186. packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +0 -103
  187. packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +0 -43
  188. packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +0 -50
  189. packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +0 -118
  190. packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +0 -33
  191. packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +0 -44
  192. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +0 -50
  193. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +0 -136
  194. packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +0 -126
  195. packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +0 -113
  196. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +0 -134
  197. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +0 -60
  198. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +0 -42
  199. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +0 -42
  200. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +0 -72
  201. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +0 -98
  202. py_neuromodulation/nm_EpochStream.py +0 -92
  203. py_neuromodulation/nm_across_patient_decoding.py +0 -927
  204. py_neuromodulation/nm_cohortwrapper.py +0 -435
  205. py_neuromodulation/nm_eval_timing.py +0 -239
  206. py_neuromodulation/nm_features_abc.py +0 -39
  207. py_neuromodulation/nm_stream_offline.py +0 -358
  208. py_neuromodulation/utils/_logging.py +0 -24
  209. py_neuromodulation-0.0.3.dist-info/RECORD +0 -188
  210. py_neuromodulation-0.0.3.dist-info/top_level.txt +0 -5
  211. tests/__init__.py +0 -0
  212. tests/conftest.py +0 -117
  213. tests/test_all_examples.py +0 -10
  214. tests/test_all_features.py +0 -63
  215. tests/test_bispectra.py +0 -70
  216. tests/test_bursts.py +0 -105
  217. tests/test_feature_sampling_rates.py +0 -143
  218. tests/test_fooof.py +0 -16
  219. tests/test_initalization_offline_stream.py +0 -41
  220. tests/test_multiprocessing.py +0 -58
  221. tests/test_nan_values.py +0 -29
  222. tests/test_nm_filter.py +0 -95
  223. tests/test_nm_resample.py +0 -63
  224. tests/test_normalization_settings.py +0 -146
  225. tests/test_notch_filter.py +0 -31
  226. tests/test_osc_features.py +0 -424
  227. tests/test_preprocessing_filter.py +0 -151
  228. tests/test_rereference.py +0 -171
  229. tests/test_sampling.py +0 -57
  230. tests/test_settings_change_after_init.py +0 -76
  231. tests/test_sharpwave.py +0 -165
  232. tests/test_target_channel_add.py +0 -100
  233. tests/test_timing.py +0 -80
@@ -1,25 +1,30 @@
1
- from pyparrm import PARRM
2
-
3
-
4
- class PARRMArtifactRejection:
5
- def __init__(self, data, sampling_freq, artefact_freq, verbose=False):
6
- self.data = data
7
- self.sampling_freq = sampling_freq
8
- self.artefact_freq = artefact_freq
9
- self.verbose = verbose
10
-
11
- self.parrm = PARRM(
12
- data=data,
13
- sampling_freq=sampling_freq,
14
- artefact_freq=artefact_freq,
15
- verbose=False,
16
- )
17
-
18
- def filter_data(self):
19
- self.parrm.find_period()
20
- self.parrm.create_filter(
21
- filter_direction="both",
22
- )
23
- filtered_data = self.parrm.filter_data()
24
-
25
- return filtered_data
1
+ from pyparrm import PARRM
2
+
3
+
4
+ class PARRMArtifactRejection:
5
+ """
6
+ This module enables training of a PARRM filter before computation,
7
+ that can in real-time then be applied.
8
+ https://pyparrm.readthedocs.io/en/stable/
9
+ """
10
+ def __init__(self, data, sampling_freq, artefact_freq, verbose=False):
11
+ self.data = data
12
+ self.sampling_freq = sampling_freq
13
+ self.artefact_freq = artefact_freq
14
+ self.verbose = verbose
15
+
16
+ self.parrm = PARRM(
17
+ data=data,
18
+ sampling_freq=sampling_freq,
19
+ artefact_freq=artefact_freq,
20
+ verbose=False,
21
+ )
22
+
23
+ def filter_data(self):
24
+ self.parrm.find_period()
25
+ self.parrm.create_filter(
26
+ filter_direction="both",
27
+ )
28
+ filtered_data = self.parrm.filter_data()
29
+
30
+ return filtered_data
@@ -1,168 +1,154 @@
1
- from typing import Iterable
2
- import numpy as np
3
- from pybispectra import compute_fft, get_example_data_paths, WaveShape
4
-
5
- from py_neuromodulation import nm_features_abc
6
-
7
-
8
- class Bispectra(nm_features_abc.Feature):
9
- def __init__(
10
- self, settings: dict, ch_names: Iterable[str], sfreq: int | float
11
- ) -> None:
12
- super().__init__(settings, ch_names, sfreq)
13
- self.sfreq = sfreq
14
- self.ch_names = ch_names
15
- self.s = settings
16
- self.f1s = settings["bispectrum"]["f1s"]
17
- self.f2s = settings["bispectrum"]["f2s"]
18
-
19
- @staticmethod
20
- def test_settings(
21
- settings: dict,
22
- ch_names: Iterable[str],
23
- sfreq: int | float,
24
- ):
25
- s = settings
26
-
27
- def test_range(f_name, filter_range):
28
- assert isinstance(
29
- filter_range[0],
30
- int,
31
- ), f"bispectrum frequency range {f_name} needs to be of type int, got {filter_range[0]}"
32
- assert isinstance(
33
- filter_range[1],
34
- int,
35
- ), f"bispectrum frequency range {f_name} needs to be of type int, got {filter_range[1]}"
36
- assert (
37
- filter_range[1] > filter_range[0]
38
- ), f"second frequency range value needs to be higher than first one, got {filter_range}"
39
- assert filter_range[0] < sfreq and filter_range[1] < sfreq, (
40
- "filter frequency range has to be smaller than sfreq, "
41
- f"got sfreq {sfreq} and filter range {filter_range}"
42
- )
43
-
44
- test_range("f1s", s["bispectrum"]["f1s"])
45
- test_range("f2s", s["bispectrum"]["f2s"])
46
-
47
- for feature_name, val in s["bispectrum"]["components"].items():
48
- assert isinstance(
49
- val, bool
50
- ), f"bispectrum component {feature_name} has to be of type bool, got {val}"
51
-
52
- for feature_name, val in s["bispectrum"]["bispectrum_features"].items():
53
- assert isinstance(
54
- val, bool
55
- ), f"bispectrum feature {feature_name} has to be of type bool, got {val}"
56
-
57
- assert (
58
- f_band_bispectrum in s["frequency_ranges_hz"]
59
- for f_band_bispectrum in s["bispectrum"]["frequency_bands"]
60
- ), (
61
- "bispectrum selected frequency bands don't match the ones"
62
- "specified in s['frequency_ranges_hz']"
63
- f"bispectrum frequency bands: {s['bispectrum']['frequency_bands']}"
64
- f"specified frequency_ranges_hz: {s['frequency_ranges_hz']}"
65
- )
66
-
67
- def compute_bs_features(
68
- self,
69
- spectrum_ch: np.array,
70
- features_compute: dict,
71
- ch_name: str,
72
- component: str,
73
- f_band: str,
74
- ) -> dict:
75
- for bispectrum_feature in self.s["bispectrum"]["bispectrum_features"]:
76
- if bispectrum_feature == "mean":
77
- func = np.nanmean
78
- if bispectrum_feature == "sum":
79
- func = np.nansum
80
- if bispectrum_feature == "var":
81
- func = np.nanvar
82
-
83
- if f_band is not None:
84
- str_feature = "_".join(
85
- [
86
- ch_name,
87
- "Bispectrum",
88
- component,
89
- bispectrum_feature,
90
- f_band,
91
- ]
92
- )
93
- else:
94
- str_feature = "_".join(
95
- [
96
- ch_name,
97
- "Bispectrum",
98
- component,
99
- bispectrum_feature,
100
- "whole_fband_range",
101
- ]
102
- )
103
-
104
- features_compute[str_feature] = func(spectrum_ch)
105
-
106
- return features_compute
107
-
108
- def calc_feature(self, data: np.array, features_compute: dict) -> dict:
109
- for ch_idx, ch_name in enumerate(self.ch_names):
110
- fft_coeffs, freqs = compute_fft(
111
- data=np.expand_dims(data[ch_idx, :], axis=(0, 1)),
112
- sampling_freq=self.sfreq,
113
- n_points=data.shape[1],
114
- verbose=False,
115
- )
116
-
117
- f_spectrum_range = freqs[
118
- np.logical_and(
119
- freqs >= np.min([self.f1s, self.f2s]),
120
- freqs <= np.max([self.f1s, self.f2s]),
121
- )
122
- ]
123
-
124
- waveshape = WaveShape(
125
- data=fft_coeffs,
126
- freqs=freqs,
127
- sampling_freq=self.sfreq,
128
- verbose=False,
129
- )
130
-
131
- waveshape.compute(
132
- f1s=(self.f1s[0], self.f1s[-1]), f2s=(self.f2s[0], self.f2s[-1])
133
- )
134
-
135
- bispectrum = np.squeeze(waveshape.results._data)
136
-
137
- for component in self.s["bispectrum"]["components"]:
138
- if self.s["bispectrum"]["components"][component]:
139
- if component == "real":
140
- spectrum_ch = bispectrum.real
141
- if component == "imag":
142
- spectrum_ch = bispectrum.imag
143
- if component == "absolute":
144
- spectrum_ch = np.abs(bispectrum)
145
- if component == "phase":
146
- spectrum_ch = np.angle(bispectrum)
147
-
148
- for fb in self.s["bispectrum"]["frequency_bands"]:
149
- range_ = (
150
- f_spectrum_range >= self.s["frequency_ranges_hz"][fb][0]
151
- ) & (
152
- f_spectrum_range <= self.s["frequency_ranges_hz"][fb][1]
153
- )
154
- # waveshape.results.plot()
155
- data_bs = spectrum_ch[range_, range_]
156
-
157
- features_compute = self.compute_bs_features(
158
- data_bs, features_compute, ch_name, component, fb
159
- )
160
-
161
- if self.s["bispectrum"][
162
- "compute_features_for_whole_fband_range"
163
- ]:
164
- features_compute = self.compute_bs_features(
165
- spectrum_ch, features_compute, ch_name, component, None
166
- )
167
-
168
- return features_compute
1
+ from collections.abc import Iterable
2
+ from pydantic import field_validator
3
+ from py_neuromodulation.nm_types import NMBaseModel
4
+ from typing import TYPE_CHECKING, Callable
5
+
6
+ import numpy as np
7
+
8
+ from py_neuromodulation.nm_features import NMFeature
9
+ from py_neuromodulation.nm_types import BoolSelector, FrequencyRange
10
+
11
+ if TYPE_CHECKING:
12
+ from py_neuromodulation.nm_settings import NMSettings
13
+
14
+
15
+ class BispectraComponents(BoolSelector):
16
+ absolute: bool = True
17
+ real: bool = True
18
+ imag: bool = True
19
+ phase: bool = True
20
+
21
+
22
+ class BispectraFeatures(BoolSelector):
23
+ mean: bool = True
24
+ sum: bool = True
25
+ var: bool = True
26
+
27
+
28
+ class BispectraSettings(NMBaseModel):
29
+ f1s: FrequencyRange = FrequencyRange(5, 35)
30
+ f2s: FrequencyRange = FrequencyRange(5, 35)
31
+ compute_features_for_whole_fband_range: bool = True
32
+ frequency_bands: list[str] = ["theta", "alpha", "low_beta", "high_beta"]
33
+
34
+ components: BispectraComponents = BispectraComponents()
35
+ bispectrum_features: BispectraFeatures = BispectraFeatures()
36
+
37
+ @field_validator("f1s", "f2s")
38
+ def test_range(cls, filter_range):
39
+ assert (
40
+ filter_range[1] > filter_range[0]
41
+ ), f"second frequency range value needs to be higher than first one, got {filter_range}"
42
+ return filter_range
43
+
44
+ @field_validator("frequency_bands")
45
+ def fbands_spaces_to_underscores(cls, frequency_bands):
46
+ return [f.replace(" ", "_") for f in frequency_bands]
47
+
48
+
49
+ FEATURE_DICT: dict[str, Callable] = {
50
+ "mean": np.nanmean,
51
+ "sum": np.nansum,
52
+ "var": np.nanvar,
53
+ }
54
+
55
+ COMPONENT_DICT: dict[str, Callable] = {
56
+ "real": lambda obj: getattr(obj, "real"),
57
+ "imag": lambda obj: getattr(obj, "imag"),
58
+ "absolute": np.abs,
59
+ "phase": np.angle,
60
+ }
61
+
62
+
63
+ class Bispectra(NMFeature):
64
+ def __init__(
65
+ self, settings: "NMSettings", ch_names: Iterable[str], sfreq: float
66
+ ) -> None:
67
+ self.sfreq = sfreq
68
+ self.ch_names = ch_names
69
+ self.frequency_ranges_hz = settings.frequency_ranges_hz
70
+ self.settings: BispectraSettings = settings.bispectrum
71
+
72
+ assert all(
73
+ f_band_bispectrum in settings.frequency_ranges_hz
74
+ for f_band_bispectrum in self.settings.frequency_bands
75
+ ), (
76
+ "bispectrum selected frequency bands don't match the ones"
77
+ "specified in s['frequency_ranges_hz']"
78
+ f"bispectrum frequency bands: {self.settings.frequency_bands}"
79
+ f"specified frequency_ranges_hz: {settings.frequency_ranges_hz}"
80
+ )
81
+
82
+ self.used_features = self.settings.bispectrum_features.get_enabled()
83
+
84
+ self.min_freq = min(
85
+ self.settings.f1s.frequency_low_hz, self.settings.f2s.frequency_low_hz
86
+ )
87
+ self.max_freq = max(
88
+ self.settings.f1s.frequency_high_hz, self.settings.f2s.frequency_high_hz
89
+ )
90
+
91
+ # self.freqs: np.ndarray = np.array([]) # In case we pre-computed this
92
+
93
+ def calc_feature(self, data: np.ndarray) -> dict:
94
+ from pybispectra import compute_fft, WaveShape
95
+
96
+ # PyBispectra's compute_fft uses PQDM to parallelize the calculation per channel
97
+ # Is this necessary? Maybe the overhead of parallelization is not worth it
98
+ # considering that we incur in it once per batch of data
99
+ fft_coeffs, freqs = compute_fft(
100
+ data=np.expand_dims(data, axis=(0)),
101
+ sampling_freq=self.sfreq,
102
+ n_points=data.shape[1],
103
+ verbose=False,
104
+ )
105
+
106
+ # freqs is batch independent, except for the last batch perhaps (if it has different shape)
107
+ # but it's computed by compute_fft regardless so no advantage in pre-computing it
108
+ # if not self.freqs = self.freqs = np.fft.rfftfreq(n=data.shape[1], d = 1 / sfreq)
109
+
110
+ # fft_coeffs shape: [epochs, channels, frequencies]
111
+
112
+ f_spectrum_range = freqs[
113
+ np.logical_and(freqs >= self.min_freq, freqs <= self.max_freq)
114
+ ]
115
+
116
+ waveshape = WaveShape(
117
+ data=fft_coeffs,
118
+ freqs=freqs,
119
+ sampling_freq=self.sfreq,
120
+ verbose=False,
121
+ )
122
+
123
+ waveshape.compute(
124
+ f1s=tuple(self.settings.f1s), # type: ignore
125
+ f2s=tuple(self.settings.f2s), # type: ignore
126
+ )
127
+
128
+ feature_results = {}
129
+ for ch_idx, ch_name in enumerate(self.ch_names):
130
+ bispectrum = waveshape._bicoherence[
131
+ ch_idx
132
+ ] # Same as waveshape.results._data, skips a copy
133
+
134
+ for component in self.settings.components.get_enabled():
135
+ spectrum_ch = COMPONENT_DICT[component](bispectrum)
136
+
137
+ for fb in self.settings.frequency_bands:
138
+ range_ = (f_spectrum_range >= self.frequency_ranges_hz[fb][0]) & (
139
+ f_spectrum_range <= self.frequency_ranges_hz[fb][1]
140
+ )
141
+ # waveshape.results.plot()
142
+ data_bs = spectrum_ch[range_, range_]
143
+
144
+ for bispectrum_feature in self.used_features:
145
+ feature_results[
146
+ f"{ch_name}_Bispectrum_{component}_{bispectrum_feature}_{fb}"
147
+ ] = FEATURE_DICT[bispectrum_feature](data_bs)
148
+
149
+ if self.settings.compute_features_for_whole_fband_range:
150
+ feature_results[
151
+ f"{ch_name}_Bispectrum_{component}_{bispectrum_feature}_whole_fband_range"
152
+ ] = FEATURE_DICT[bispectrum_feature](spectrum_ch)
153
+
154
+ return feature_results