py-neuromodulation 0.0.2__py3-none-any.whl → 0.0.3__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 (204) hide show
  1. docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
  2. docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +233 -0
  3. docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  4. docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
  5. docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  6. docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
  7. docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  8. docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
  9. docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +239 -0
  10. docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  11. docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
  12. docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  13. docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
  14. docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  15. docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +76 -0
  16. docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +97 -0
  17. docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +240 -0
  18. docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +233 -0
  19. docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +63 -0
  20. docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  21. docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +210 -0
  22. docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +192 -0
  23. docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +219 -0
  24. docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +121 -0
  25. docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +68 -0
  26. docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  27. docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +189 -0
  28. docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  29. docs/source/auto_examples/plot_0_first_demo.py +189 -0
  30. docs/source/auto_examples/plot_1_example_BIDS.py +240 -0
  31. docs/source/auto_examples/plot_2_example_add_feature.py +76 -0
  32. docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +219 -0
  33. docs/source/auto_examples/plot_4_example_gridPointProjection.py +210 -0
  34. docs/source/auto_examples/plot_5_example_rmap_computing.py +64 -0
  35. docs/source/auto_examples/plot_6_real_time_demo.py +121 -0
  36. docs/source/conf.py +105 -0
  37. examples/plot_0_first_demo.py +189 -0
  38. examples/plot_1_example_BIDS.py +240 -0
  39. examples/plot_2_example_add_feature.py +76 -0
  40. examples/plot_3_example_sharpwave_analysis.py +219 -0
  41. examples/plot_4_example_gridPointProjection.py +210 -0
  42. examples/plot_5_example_rmap_computing.py +64 -0
  43. examples/plot_6_real_time_demo.py +121 -0
  44. packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +4 -0
  45. packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +104 -0
  46. packages/realtime_decoding/build/lib/realtime_decoding/features.py +163 -0
  47. packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +15 -0
  48. packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +345 -0
  49. packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +54 -0
  50. packages/tmsi/build/lib/TMSiFileFormats/__init__.py +37 -0
  51. packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +36 -0
  52. packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +200 -0
  53. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +496 -0
  54. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +236 -0
  55. packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +977 -0
  56. packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +35 -0
  57. packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +116 -0
  58. packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +294 -0
  59. packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +229 -0
  60. packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +102 -0
  61. packages/tmsi/build/lib/TMSiPlotters/__init__.py +2 -0
  62. packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +39 -0
  63. packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +234 -0
  64. packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +440 -0
  65. packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +44 -0
  66. packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +446 -0
  67. packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +589 -0
  68. packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +1326 -0
  69. packages/tmsi/build/lib/TMSiSDK/__init__.py +54 -0
  70. packages/tmsi/build/lib/TMSiSDK/device.py +588 -0
  71. packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +34 -0
  72. packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +1764 -0
  73. packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +34 -0
  74. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +1366 -0
  75. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +520 -0
  76. packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +165 -0
  77. packages/tmsi/build/lib/TMSiSDK/error.py +95 -0
  78. packages/tmsi/build/lib/TMSiSDK/sample_data.py +63 -0
  79. packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +99 -0
  80. packages/tmsi/build/lib/TMSiSDK/settings.py +45 -0
  81. packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +111 -0
  82. packages/tmsi/build/lib/__init__.py +4 -0
  83. packages/tmsi/build/lib/apex_sdk/__init__.py +34 -0
  84. packages/tmsi/build/lib/apex_sdk/device/__init__.py +41 -0
  85. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +1009 -0
  86. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +239 -0
  87. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +668 -0
  88. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +1611 -0
  89. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +38 -0
  90. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +57 -0
  91. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +44 -0
  92. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +150 -0
  93. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +36 -0
  94. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +48 -0
  95. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +108 -0
  96. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +39 -0
  97. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +77 -0
  98. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +150 -0
  99. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +129 -0
  100. packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +59 -0
  101. packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +57 -0
  102. packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +83 -0
  103. packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +201 -0
  104. packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +103 -0
  105. packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +43 -0
  106. packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +50 -0
  107. packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +118 -0
  108. packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +33 -0
  109. packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +44 -0
  110. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +50 -0
  111. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +136 -0
  112. packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +126 -0
  113. packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +113 -0
  114. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +134 -0
  115. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +60 -0
  116. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +42 -0
  117. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +42 -0
  118. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +72 -0
  119. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +98 -0
  120. py_neuromodulation/{helper.py → _write_example_dataset_helper.py} +1 -1
  121. py_neuromodulation/nm_EpochStream.py +2 -3
  122. py_neuromodulation/nm_IO.py +43 -70
  123. py_neuromodulation/nm_RMAP.py +308 -11
  124. py_neuromodulation/nm_analysis.py +1 -1
  125. py_neuromodulation/nm_artifacts.py +25 -0
  126. py_neuromodulation/nm_bispectra.py +64 -29
  127. py_neuromodulation/nm_bursts.py +44 -30
  128. py_neuromodulation/nm_coherence.py +2 -1
  129. py_neuromodulation/nm_features.py +4 -2
  130. py_neuromodulation/nm_filter.py +63 -32
  131. py_neuromodulation/nm_filter_preprocessing.py +91 -0
  132. py_neuromodulation/nm_fooof.py +47 -29
  133. py_neuromodulation/nm_mne_connectivity.py +1 -1
  134. py_neuromodulation/nm_normalization.py +50 -74
  135. py_neuromodulation/nm_oscillatory.py +151 -31
  136. py_neuromodulation/nm_plots.py +13 -10
  137. py_neuromodulation/nm_rereference.py +10 -8
  138. py_neuromodulation/nm_run_analysis.py +28 -13
  139. py_neuromodulation/nm_sharpwaves.py +103 -136
  140. py_neuromodulation/nm_stats.py +44 -30
  141. py_neuromodulation/nm_stream_abc.py +18 -10
  142. py_neuromodulation/nm_stream_offline.py +181 -40
  143. py_neuromodulation/utils/_logging.py +24 -0
  144. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -142
  145. py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
  146. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
  147. py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
  148. tests/__init__.py +0 -0
  149. tests/conftest.py +117 -0
  150. tests/test_all_examples.py +10 -0
  151. tests/test_all_features.py +63 -0
  152. tests/test_bispectra.py +70 -0
  153. tests/test_bursts.py +105 -0
  154. tests/test_feature_sampling_rates.py +143 -0
  155. tests/test_fooof.py +16 -0
  156. tests/test_initalization_offline_stream.py +41 -0
  157. tests/test_multiprocessing.py +58 -0
  158. tests/test_nan_values.py +29 -0
  159. tests/test_nm_filter.py +95 -0
  160. tests/test_nm_resample.py +63 -0
  161. tests/test_normalization_settings.py +146 -0
  162. tests/test_notch_filter.py +31 -0
  163. tests/test_osc_features.py +424 -0
  164. tests/test_preprocessing_filter.py +151 -0
  165. tests/test_rereference.py +171 -0
  166. tests/test_sampling.py +57 -0
  167. tests/test_settings_change_after_init.py +76 -0
  168. tests/test_sharpwave.py +165 -0
  169. tests/test_target_channel_add.py +100 -0
  170. tests/test_timing.py +80 -0
  171. py_neuromodulation/data/README +0 -6
  172. py_neuromodulation/data/dataset_description.json +0 -8
  173. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
  174. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
  175. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
  176. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p +0 -0
  177. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
  178. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
  179. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_decoding_performance.png +0 -0
  180. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv +0 -11
  181. py_neuromodulation/data/participants.json +0 -32
  182. py_neuromodulation/data/participants.tsv +0 -2
  183. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
  184. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
  185. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
  186. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  187. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
  188. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
  189. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
  190. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
  191. py_neuromodulation/grid_cortex.tsv +0 -40
  192. py_neuromodulation/grid_subcortex.tsv +0 -1429
  193. py_neuromodulation/nm_settings.json +0 -290
  194. py_neuromodulation/plots/STN_surf.mat +0 -0
  195. py_neuromodulation/plots/Vertices.mat +0 -0
  196. py_neuromodulation/plots/faces.mat +0 -0
  197. py_neuromodulation/plots/grid.mat +0 -0
  198. py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
  199. py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
  200. py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
  201. py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
  202. py_neuromodulation-0.0.2.dist-info/RECORD +0 -73
  203. /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
  204. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
@@ -4,9 +4,11 @@ from pybispectra import compute_fft, get_example_data_paths, WaveShape
4
4
 
5
5
  from py_neuromodulation import nm_features_abc
6
6
 
7
- class Bispectra(nm_features_abc.Feature):
8
7
 
9
- def __init__(self, settings: dict, ch_names: Iterable[str], sfreq: int | float) -> None:
8
+ class Bispectra(nm_features_abc.Feature):
9
+ def __init__(
10
+ self, settings: dict, ch_names: Iterable[str], sfreq: int | float
11
+ ) -> None:
10
12
  super().__init__(settings, ch_names, sfreq)
11
13
  self.sfreq = sfreq
12
14
  self.ch_names = ch_names
@@ -40,18 +42,14 @@ class Bispectra(nm_features_abc.Feature):
40
42
  )
41
43
 
42
44
  test_range("f1s", s["bispectrum"]["f1s"])
43
- test_range("f2s", s["bispectrum"]["f2s"])
45
+ test_range("f2s", s["bispectrum"]["f2s"])
44
46
 
45
- for feature_name, val in s["bispectrum"][
46
- "components"
47
- ].items():
47
+ for feature_name, val in s["bispectrum"]["components"].items():
48
48
  assert isinstance(
49
49
  val, bool
50
50
  ), f"bispectrum component {feature_name} has to be of type bool, got {val}"
51
-
52
- for feature_name, val in s["bispectrum"][
53
- "bispectrum_features"
54
- ].items():
51
+
52
+ for feature_name, val in s["bispectrum"]["bispectrum_features"].items():
55
53
  assert isinstance(
56
54
  val, bool
57
55
  ), f"bispectrum feature {feature_name} has to be of type bool, got {val}"
@@ -66,10 +64,15 @@ class Bispectra(nm_features_abc.Feature):
66
64
  f"specified frequency_ranges_hz: {s['frequency_ranges_hz']}"
67
65
  )
68
66
 
69
- def compute_bs_features(self, spectrum_ch: np.array, features_compute: dict, ch_name: str, component: str, f_band: str) -> dict:
70
-
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:
71
75
  for bispectrum_feature in self.s["bispectrum"]["bispectrum_features"]:
72
-
73
76
  if bispectrum_feature == "mean":
74
77
  func = np.nanmean
75
78
  if bispectrum_feature == "sum":
@@ -78,9 +81,25 @@ class Bispectra(nm_features_abc.Feature):
78
81
  func = np.nanvar
79
82
 
80
83
  if f_band is not None:
81
- str_feature = "_".join([ch_name, "Bispectrum", component, bispectrum_feature, f_band])
84
+ str_feature = "_".join(
85
+ [
86
+ ch_name,
87
+ "Bispectrum",
88
+ component,
89
+ bispectrum_feature,
90
+ f_band,
91
+ ]
92
+ )
82
93
  else:
83
- str_feature = "_".join([ch_name, "Bispectrum", component, bispectrum_feature, "whole_fband_range"])
94
+ str_feature = "_".join(
95
+ [
96
+ ch_name,
97
+ "Bispectrum",
98
+ component,
99
+ bispectrum_feature,
100
+ "whole_fband_range",
101
+ ]
102
+ )
84
103
 
85
104
  features_compute[str_feature] = func(spectrum_ch)
86
105
 
@@ -89,22 +108,29 @@ class Bispectra(nm_features_abc.Feature):
89
108
  def calc_feature(self, data: np.array, features_compute: dict) -> dict:
90
109
  for ch_idx, ch_name in enumerate(self.ch_names):
91
110
  fft_coeffs, freqs = compute_fft(
92
- data=np.expand_dims(data[ch_idx, :], axis=(0,1)),
111
+ data=np.expand_dims(data[ch_idx, :], axis=(0, 1)),
93
112
  sampling_freq=self.sfreq,
94
113
  n_points=data.shape[1],
95
- verbose=False,
114
+ verbose=False,
96
115
  )
97
116
 
98
- f_spectrum_range = freqs[np.logical_and(freqs >= np.min([self.f1s, self.f2s]), freqs <= np.max([self.f1s, self.f2s]))]
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
+ ]
99
123
 
100
124
  waveshape = WaveShape(
101
125
  data=fft_coeffs,
102
- freqs=freqs.astype(int),
126
+ freqs=freqs,
103
127
  sampling_freq=self.sfreq,
104
128
  verbose=False,
105
129
  )
106
130
 
107
- waveshape.compute(f1s=tuple(self.f1s), f2s=tuple(self.f2s))
131
+ waveshape.compute(
132
+ f1s=(self.f1s[0], self.f1s[-1]), f2s=(self.f2s[0], self.f2s[-1])
133
+ )
108
134
 
109
135
  bispectrum = np.squeeze(waveshape.results._data)
110
136
 
@@ -120,14 +146,23 @@ class Bispectra(nm_features_abc.Feature):
120
146
  spectrum_ch = np.angle(bispectrum)
121
147
 
122
148
  for fb in self.s["bispectrum"]["frequency_bands"]:
123
- range_ = (f_spectrum_range >= self.s["frequency_ranges_hz"][fb][0]) \
124
- & (f_spectrum_range <= self.s["frequency_ranges_hz"][fb][1])
125
- #waveshape.results.plot()
126
- data_bs = spectrum_ch[range_, range_]
127
-
128
- features_compute = self.compute_bs_features(data_bs, features_compute, ch_name, component, fb)
129
-
130
- if self.s["bispectrum"]["compute_features_for_whole_fband_range"]:
131
- features_compute = self.compute_bs_features(spectrum_ch, features_compute, ch_name, component, None)
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
+ )
132
167
 
133
168
  return features_compute
@@ -1,6 +1,7 @@
1
1
  import enum
2
2
  import numpy as np
3
3
  from typing import Iterable
4
+ from scipy import signal
4
5
 
5
6
  from py_neuromodulation import nm_features_abc, nm_filter
6
7
 
@@ -9,12 +10,16 @@ class Burst(nm_features_abc.Feature):
9
10
  def __init__(
10
11
  self, settings: dict, ch_names: Iterable[str], sfreq: float
11
12
  ) -> None:
12
-
13
13
  self.s = settings
14
14
  self.sfreq = sfreq
15
15
  self.ch_names = ch_names
16
16
  self.threshold = self.s["burst_settings"]["threshold"]
17
17
  self.time_duration_s = self.s["burst_settings"]["time_duration_s"]
18
+ self.samples_overlap = int(
19
+ self.sfreq
20
+ * (self.s["segment_length_features_ms"] / 1000)
21
+ / self.s["sampling_rate_features_hz"]
22
+ )
18
23
 
19
24
  self.fband_names = self.s["burst_settings"]["frequency_bands"]
20
25
  self.f_ranges = [
@@ -34,9 +39,11 @@ class Burst(nm_features_abc.Feature):
34
39
  )
35
40
  ).astype(int)
36
41
 
37
- self.num_max_samples_ring_buffer = int(self.sfreq * self.time_duration_s)
42
+ self.num_max_samples_ring_buffer = int(
43
+ self.sfreq * self.time_duration_s
44
+ )
38
45
 
39
- self.bandpass_filter = nm_filter.BandPassFilter(
46
+ self.bandpass_filter = nm_filter.MNEFilter(
40
47
  f_ranges=self.f_ranges,
41
48
  sfreq=self.sfreq,
42
49
  filter_length=self.sfreq - 1,
@@ -62,35 +69,40 @@ class Burst(nm_features_abc.Feature):
62
69
  ch_names: Iterable[str],
63
70
  sfreq: int | float,
64
71
  ):
65
- assert (
66
- isinstance(settings["burst_settings"]["threshold"], (float, int))
72
+ assert isinstance(
73
+ settings["burst_settings"]["threshold"], (float, int)
67
74
  ), f"burst settings threshold needs to be type int or float, got: {settings['burst_settings']['threshold']}"
68
75
  assert (
69
76
  0 < settings["burst_settings"]["threshold"] < 100
70
77
  ), f"burst setting threshold needs to be between 0 and 100, got: {settings['burst_settings']['threshold']}"
71
- assert (
72
- isinstance(settings["burst_settings"]["time_duration_s"], (float, int))
78
+ assert isinstance(
79
+ settings["burst_settings"]["time_duration_s"], (float, int)
73
80
  ), f"burst settings time_duration_s needs to be type int or float, got: {settings['burst_settings']['time_duration_s']}"
74
81
  assert (
75
82
  settings["burst_settings"]["time_duration_s"] > 0
76
83
  ), f"burst setting time_duration_s needs to be greater than 0, got: {settings['burst_settings']['time_duration_s']}"
77
84
 
78
85
  for fband_burst in settings["burst_settings"]["frequency_bands"]:
79
- assert (
80
- fband_burst in list(settings["frequency_ranges_hz"].keys())
86
+ assert fband_burst in list(
87
+ settings["frequency_ranges_hz"].keys()
81
88
  ), f"bursting {fband_burst} needs to be defined in settings['frequency_ranges_hz']"
82
89
 
83
- for burst_feature in settings["burst_settings"]["burst_features"].keys():
90
+ for burst_feature in settings["burst_settings"][
91
+ "burst_features"
92
+ ].keys():
84
93
  assert isinstance(
85
- settings["burst_settings"]["burst_features"][burst_feature], bool
94
+ settings["burst_settings"]["burst_features"][burst_feature],
95
+ bool,
86
96
  ), (
87
97
  f"bursting feature {burst_feature} needs to be type bool, "
88
98
  f"got: {settings['burst_settings']['burst_features'][burst_feature]}"
89
99
  )
90
- def calc_feature(self, data: np.array, features_compute: dict) -> dict:
91
100
 
101
+ def calc_feature(self, data: np.array, features_compute: dict) -> dict:
92
102
  # filter_data returns (n_channels, n_fbands, n_samples)
93
- filtered_data = self.bandpass_filter.filter_data(data)
103
+ filtered_data = np.abs(
104
+ signal.hilbert(self.bandpass_filter.filter_data(data), axis=2)
105
+ )
94
106
  for ch_idx, ch_name in enumerate(self.ch_names):
95
107
  for fband_idx, fband_name in enumerate(self.fband_names):
96
108
  new_dat = filtered_data[ch_idx, fband_idx, :]
@@ -98,8 +110,12 @@ class Burst(nm_features_abc.Feature):
98
110
  self.data_buffer[ch_name][fband_name] = new_dat
99
111
  else:
100
112
  self.data_buffer[ch_name][fband_name] = np.concatenate(
101
- (self.data_buffer[ch_name][fband_name], new_dat), axis=0
102
- )[-self.num_max_samples_ring_buffer:]
113
+ (
114
+ self.data_buffer[ch_name][fband_name],
115
+ new_dat[-self.samples_overlap :],
116
+ ),
117
+ axis=0,
118
+ )[-self.num_max_samples_ring_buffer :]
103
119
 
104
120
  # calc features
105
121
  burst_thr = np.percentile(
@@ -145,9 +161,9 @@ class Burst(nm_features_abc.Feature):
145
161
  if self.data_buffer[ch_name][fband_name][-1] > burst_thr:
146
162
  in_burst = True
147
163
 
148
- features_compute[
149
- f"{ch_name}_bursts_{fband_name}_in_burst"
150
- ] = in_burst
164
+ features_compute[f"{ch_name}_bursts_{fband_name}_in_burst"] = (
165
+ in_burst
166
+ )
151
167
  return features_compute
152
168
 
153
169
  @staticmethod
@@ -160,25 +176,23 @@ class Burst(nm_features_abc.Feature):
160
176
  bursts = np.zeros((beta_averp_norm.shape[0] + 1), dtype=bool)
161
177
  bursts[1:] = beta_averp_norm >= burst_thr
162
178
  deriv = np.diff(bursts)
163
- isburst = False
164
179
  burst_length = []
165
180
  burst_amplitude = []
166
- burst_start = 0
167
181
 
168
- for index, burst_state in enumerate(deriv):
169
- if burst_state == True:
170
- if isburst == True:
171
- burst_length.append(index - burst_start)
172
- burst_amplitude.append(beta_averp_norm[burst_start:index])
182
+ burst_time_points = np.where(deriv == True)[0]
173
183
 
174
- isburst = False
175
- else:
176
- burst_start = index
177
- isburst = True
184
+ for i in range(burst_time_points.size // 2):
185
+ burst_length.append(
186
+ burst_time_points[2 * i + 1] - burst_time_points[2 * i]
187
+ )
188
+ burst_amplitude.append(
189
+ beta_averp_norm[
190
+ burst_time_points[2 * i] : burst_time_points[2 * i + 1]
191
+ ]
192
+ )
178
193
 
179
194
  # the last burst length (in case isburst == True) is omitted,
180
195
  # since the true burst length cannot be estimated
181
-
182
196
  burst_length = np.array(burst_length) / sfreq
183
197
 
184
198
  return burst_amplitude, burst_length
@@ -111,7 +111,7 @@ class CoherenceObject:
111
111
 
112
112
  class NM_Coherence(nm_features_abc.Feature):
113
113
 
114
- coherence_objects: Iterable[CoherenceObject] = []
114
+
115
115
 
116
116
  def __init__(
117
117
  self, settings: dict, ch_names: Iterable[str], sfreq: float
@@ -119,6 +119,7 @@ class NM_Coherence(nm_features_abc.Feature):
119
119
  self.s = settings
120
120
  self.sfreq = sfreq
121
121
  self.ch_names = ch_names
122
+ self.coherence_objects: Iterable[CoherenceObject] = []
122
123
 
123
124
  for idx_coh in range(len(self.s["coherence"]["channels"])):
124
125
  fband_names = self.s["coherence"]["frequency_bands"]
@@ -11,7 +11,7 @@ from py_neuromodulation import (
11
11
  nm_oscillatory,
12
12
  nm_bursts,
13
13
  nm_linelength,
14
- nm_bispectra
14
+ nm_bispectra,
15
15
  )
16
16
 
17
17
 
@@ -56,6 +56,8 @@ class Features:
56
56
  FeatureClass = nm_oscillatory.STFT
57
57
  case "fft":
58
58
  FeatureClass = nm_oscillatory.FFT
59
+ case "welch":
60
+ FeatureClass = nm_oscillatory.Welch
59
61
  case "sharpwave_analysis":
60
62
  FeatureClass = nm_sharpwaves.SharpwaveAnalyzer
61
63
  case "fooof":
@@ -111,4 +113,4 @@ class Features:
111
113
  features_compute,
112
114
  )
113
115
 
114
- return features_compute
116
+ return features_compute
@@ -1,16 +1,23 @@
1
1
  """Module for filter functionality."""
2
+
3
+ import logging
4
+
5
+ logger = logging.getLogger("PynmLogger")
6
+
2
7
  import mne
3
8
  from mne.filter import _overlap_add_filter
4
9
  import numpy as np
5
10
 
6
11
 
7
- class BandPassFilter:
8
- """Bandpass filters data in given frequency ranges.
12
+ class MNEFilter:
13
+ """mne.filter wrapper
9
14
 
10
15
  This class stores for given frequency band ranges the filter
11
16
  coefficients with length "filter_len".
12
17
  The filters can then be used sequentially for band power estimation with
13
18
  apply_filter().
19
+ Note that this filter can be a bandpass, bandstop, lowpass, or highpass filter
20
+ depending on the frequency ranges given (see further details in mne.filter.create_filter).
14
21
 
15
22
  Parameters
16
23
  ----------
@@ -35,7 +42,7 @@ class BandPassFilter:
35
42
 
36
43
  def __init__(
37
44
  self,
38
- f_ranges: list[list[int | float | None]],
45
+ f_ranges: list[list[int | float | None]] | list[int | float | None],
39
46
  sfreq: int | float,
40
47
  filter_length: str | float = "999ms",
41
48
  l_trans_bandwidth: int | float | str = 4,
@@ -43,22 +50,36 @@ class BandPassFilter:
43
50
  verbose: bool | int | str | None = None,
44
51
  ) -> None:
45
52
  filter_bank = []
46
- # mne create_filter function only accepts str and int
53
+ # mne create_filter function only accepts str and int for filter_length
47
54
  if isinstance(filter_length, float):
48
55
  filter_length = int(filter_length)
49
56
 
57
+ if not isinstance(f_ranges[0], list):
58
+ f_ranges = [f_ranges]
59
+
50
60
  for f_range in f_ranges:
51
- filt = mne.filter.create_filter(
52
- None,
53
- sfreq,
54
- l_freq=f_range[0],
55
- h_freq=f_range[1],
56
- fir_design="firwin",
57
- l_trans_bandwidth=l_trans_bandwidth, # type: ignore
58
- h_trans_bandwidth=h_trans_bandwidth, # type: ignore
59
- filter_length=filter_length, # type: ignore
60
- verbose=verbose,
61
- )
61
+ try:
62
+ filt = mne.filter.create_filter(
63
+ None,
64
+ sfreq,
65
+ l_freq=f_range[0],
66
+ h_freq=f_range[1],
67
+ fir_design="firwin",
68
+ l_trans_bandwidth=l_trans_bandwidth, # type: ignore
69
+ h_trans_bandwidth=h_trans_bandwidth, # type: ignore
70
+ filter_length=filter_length, # type: ignore
71
+ verbose=verbose,
72
+ )
73
+ except:
74
+ filt = mne.filter.create_filter(
75
+ None,
76
+ sfreq,
77
+ l_freq=f_range[0],
78
+ h_freq=f_range[1],
79
+ fir_design="firwin",
80
+ verbose=verbose,
81
+ # filter_length=filter_length,
82
+ )
62
83
  filter_bank.append(filt)
63
84
  self.filter_bank = np.vstack(filter_bank)
64
85
 
@@ -77,10 +98,6 @@ class BandPassFilter:
77
98
  np.ndarray, shape (n_channels, n_fbands, n_samples)
78
99
  Filtered data.
79
100
 
80
- Raises
81
- ------
82
- ValueError
83
- If data.ndim > 2
84
101
  """
85
102
  if data.ndim > 2:
86
103
  raise ValueError(
@@ -89,15 +106,29 @@ class BandPassFilter:
89
106
  )
90
107
  if data.ndim == 1:
91
108
  data = np.expand_dims(data, axis=0)
109
+
92
110
  filtered = np.array(
93
111
  [
94
112
  [
95
- np.convolve(flt, chan, mode="same")
96
- for flt in self.filter_bank
113
+ np.convolve(filt, chan, mode="same")
114
+ for filt in self.filter_bank
97
115
  ]
98
116
  for chan in data
99
117
  ]
100
118
  )
119
+
120
+ # ensure here that the output dimension matches the input dimension
121
+ if data.shape[1] != filtered.shape[-1]:
122
+ # select the middle part of the filtered data
123
+ middle_index = filtered.shape[-1] // 2
124
+ filtered = filtered[
125
+ :,
126
+ :,
127
+ middle_index
128
+ - data.shape[1] // 2 : middle_index
129
+ + data.shape[1] // 2,
130
+ ]
131
+
101
132
  return filtered
102
133
 
103
134
 
@@ -125,7 +156,7 @@ class NotchFilter:
125
156
  # Code is copied from filter.py notch_filter
126
157
  if freqs.size == 0:
127
158
  self.filter_bank = None
128
- print(
159
+ logger.warning(
129
160
  "WARNING: notch_filter is activated but data is not being"
130
161
  f" filtered. This may be due to a low sampling frequency or"
131
162
  f" incorrect specifications. Make sure your settings are"
@@ -170,19 +201,19 @@ class NotchFilter:
170
201
  phase="zero",
171
202
  fir_window="hamming",
172
203
  fir_design="firwin",
173
- verbose=False
204
+ verbose=False,
174
205
  )
175
206
 
176
207
  def process(self, data: np.ndarray) -> np.ndarray:
177
208
  if self.filter_bank is None:
178
209
  return data
179
210
  return _overlap_add_filter(
180
- x=data,
181
- h=self.filter_bank,
182
- n_fft=None,
183
- phase="zero",
184
- picks=None,
185
- n_jobs=1,
186
- copy=True,
187
- pad="reflect_limited",
188
- )
211
+ x=data,
212
+ h=self.filter_bank,
213
+ n_fft=None,
214
+ phase="zero",
215
+ picks=None,
216
+ n_jobs=1,
217
+ copy=True,
218
+ pad="reflect_limited",
219
+ )
@@ -0,0 +1,91 @@
1
+ import numpy as np
2
+
3
+ from py_neuromodulation import nm_filter
4
+
5
+
6
+ class PreprocessingFilter:
7
+
8
+ def __init__(self, settings: dict, sfreq: int | float) -> None:
9
+ self.s = settings
10
+ self.sfreq = sfreq
11
+ self.filters = []
12
+
13
+ if self.s["preprocessing_filter"]["bandstop_filter"] is True:
14
+ self.filters.append(
15
+ nm_filter.MNEFilter(
16
+ f_ranges=[
17
+ self.s["preprocessing_filter"][
18
+ "bandstop_filter_settings"
19
+ ]["frequency_high_hz"],
20
+ self.s["preprocessing_filter"][
21
+ "bandstop_filter_settings"
22
+ ]["frequency_low_hz"],
23
+ ],
24
+ sfreq=self.sfreq,
25
+ filter_length=self.sfreq - 1,
26
+ verbose=False,
27
+ )
28
+ )
29
+
30
+ if self.s["preprocessing_filter"]["bandpass_filter"] is True:
31
+ self.filters.append(
32
+ nm_filter.MNEFilter(
33
+ f_ranges=[
34
+ self.s["preprocessing_filter"][
35
+ "bandpass_filter_settings"
36
+ ]["frequency_low_hz"],
37
+ self.s["preprocessing_filter"][
38
+ "bandpass_filter_settings"
39
+ ]["frequency_high_hz"],
40
+ ],
41
+ sfreq=self.sfreq,
42
+ filter_length=self.sfreq - 1,
43
+ verbose=False,
44
+ )
45
+ )
46
+ if self.s["preprocessing_filter"]["lowpass_filter"] is True:
47
+ self.filters.append(
48
+ nm_filter.MNEFilter(
49
+ f_ranges=[
50
+ None,
51
+ self.s["preprocessing_filter"][
52
+ "lowpass_filter_settings"
53
+ ]["frequency_cutoff_hz"],
54
+ ],
55
+ sfreq=self.sfreq,
56
+ filter_length=self.sfreq - 1,
57
+ verbose=False,
58
+ )
59
+ )
60
+ if self.s["preprocessing_filter"]["highpass_filter"] is True:
61
+ self.filters.append(
62
+ nm_filter.MNEFilter(
63
+ f_ranges=[
64
+ self.s["preprocessing_filter"][
65
+ "highpass_filter_settings"
66
+ ]["frequency_cutoff_hz"],
67
+ None,
68
+ ],
69
+ sfreq=self.sfreq,
70
+ filter_length=self.sfreq - 1,
71
+ verbose=False,
72
+ )
73
+ )
74
+
75
+ def process(self, data: np.ndarray) -> np.ndarray:
76
+ """Preprocess data according to the initialized list of PreprocessingFilter objects
77
+
78
+ Args:
79
+ data (numpy ndarray) :
80
+ shape(n_channels, n_samples) - data to be preprocessed.
81
+
82
+ Returns:
83
+ preprocessed_data (numpy ndarray):
84
+ shape(n_channels, n_samples) - preprocessed data
85
+ """
86
+
87
+ for filter in self.filters:
88
+ data = filter.filter_data(
89
+ data if len(data.shape) == 2 else data[:, 0, :]
90
+ )
91
+ return data if len(data.shape) == 2 else data[:, 0, :]