py-neuromodulation 0.0.1__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 (206) 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 +168 -0
  127. py_neuromodulation/nm_bursts.py +43 -31
  128. py_neuromodulation/nm_coherence.py +2 -1
  129. py_neuromodulation/nm_define_nmchannels.py +1 -1
  130. py_neuromodulation/nm_features.py +6 -1
  131. py_neuromodulation/nm_filter.py +63 -32
  132. py_neuromodulation/nm_filter_preprocessing.py +91 -0
  133. py_neuromodulation/nm_fooof.py +47 -29
  134. py_neuromodulation/nm_generator.py +10 -10
  135. py_neuromodulation/nm_mne_connectivity.py +1 -1
  136. py_neuromodulation/nm_normalization.py +50 -74
  137. py_neuromodulation/nm_oscillatory.py +151 -31
  138. py_neuromodulation/nm_plots.py +13 -10
  139. py_neuromodulation/nm_rereference.py +10 -8
  140. py_neuromodulation/nm_run_analysis.py +28 -13
  141. py_neuromodulation/nm_sharpwaves.py +103 -136
  142. py_neuromodulation/nm_stats.py +44 -30
  143. py_neuromodulation/nm_stream_abc.py +18 -10
  144. py_neuromodulation/nm_stream_offline.py +185 -43
  145. py_neuromodulation/utils/_logging.py +24 -0
  146. {py_neuromodulation-0.0.1.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -131
  147. py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
  148. {py_neuromodulation-0.0.1.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
  149. py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
  150. tests/__init__.py +0 -0
  151. tests/conftest.py +117 -0
  152. tests/test_all_examples.py +10 -0
  153. tests/test_all_features.py +63 -0
  154. tests/test_bispectra.py +70 -0
  155. tests/test_bursts.py +105 -0
  156. tests/test_feature_sampling_rates.py +143 -0
  157. tests/test_fooof.py +16 -0
  158. tests/test_initalization_offline_stream.py +41 -0
  159. tests/test_multiprocessing.py +58 -0
  160. tests/test_nan_values.py +29 -0
  161. tests/test_nm_filter.py +95 -0
  162. tests/test_nm_resample.py +63 -0
  163. tests/test_normalization_settings.py +146 -0
  164. tests/test_notch_filter.py +31 -0
  165. tests/test_osc_features.py +424 -0
  166. tests/test_preprocessing_filter.py +151 -0
  167. tests/test_rereference.py +171 -0
  168. tests/test_sampling.py +57 -0
  169. tests/test_settings_change_after_init.py +76 -0
  170. tests/test_sharpwave.py +165 -0
  171. tests/test_target_channel_add.py +100 -0
  172. tests/test_timing.py +80 -0
  173. py_neuromodulation/data/README +0 -6
  174. py_neuromodulation/data/dataset_description.json +0 -8
  175. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
  176. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
  177. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
  178. 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
  179. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
  180. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
  181. 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
  182. 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
  183. py_neuromodulation/data/participants.json +0 -32
  184. py_neuromodulation/data/participants.tsv +0 -2
  185. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
  186. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
  187. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
  188. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  189. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
  190. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
  191. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
  192. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
  193. py_neuromodulation/grid_cortex.tsv +0 -40
  194. py_neuromodulation/grid_subcortex.tsv +0 -1429
  195. py_neuromodulation/nm_settings.json +0 -261
  196. py_neuromodulation/plots/STN_surf.mat +0 -0
  197. py_neuromodulation/plots/Vertices.mat +0 -0
  198. py_neuromodulation/plots/faces.mat +0 -0
  199. py_neuromodulation/plots/grid.mat +0 -0
  200. py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
  201. py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
  202. py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
  203. py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
  204. py_neuromodulation-0.0.1.dist-info/RECORD +0 -72
  205. /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
  206. {py_neuromodulation-0.0.1.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
@@ -6,6 +6,9 @@ from matplotlib import gridspec
6
6
  from typing import Optional
7
7
  import seaborn as sb
8
8
  import pandas as pd
9
+ import logging
10
+
11
+ logger = logging.getLogger("PynmLogger")
9
12
 
10
13
  from py_neuromodulation import nm_IO, nm_stats
11
14
 
@@ -87,7 +90,9 @@ def plot_epoch(
87
90
  ):
88
91
  if z_score is None:
89
92
  X_epoch = stats.zscore(
90
- np.nan_to_num(np.nanmean(np.squeeze(X_epoch), axis=0)), axis=0
93
+ np.nan_to_num(np.nanmean(np.squeeze(X_epoch), axis=0)),
94
+ axis=0,
95
+ nan_policy="omit",
91
96
  ).T
92
97
  y_epoch = np.stack(np.array(y_epoch))
93
98
  plt.figure(figsize=(6, 6))
@@ -237,7 +242,7 @@ def plot_corr_matrix(
237
242
 
238
243
  if save_plot:
239
244
  plt.savefig(plt_path, bbox_inches="tight")
240
- print("Correlation matrix figure saved to " + str(plt_path))
245
+ logger.info(f"Correlation matrix figure saved to {plt_path}")
241
246
 
242
247
  if show_plot is False:
243
248
  plt.close()
@@ -329,7 +334,7 @@ def plot_epochs_avg(
329
334
 
330
335
  if normalize_data:
331
336
  X_epoch_mean = stats.zscore(
332
- np.nanmean(np.squeeze(X_epoch), axis=0), axis=0
337
+ np.nanmean(np.squeeze(X_epoch), axis=0), axis=0, nan_policy="omit"
333
338
  ).T
334
339
  else:
335
340
  X_epoch_mean = np.nanmean(np.squeeze(X_epoch), axis=0).T
@@ -385,7 +390,7 @@ def plot_epochs_avg(
385
390
  feature_name=feature_str_add,
386
391
  )
387
392
  plt.savefig(plt_path, bbox_inches="tight")
388
- print("Feature epoch average figure saved to: " + str(plt_path))
393
+ logger.info(f"Feature epoch average figure saved to: {str(plt_path)}")
389
394
  if show_plot is False:
390
395
  plt.close()
391
396
 
@@ -441,7 +446,6 @@ def plot_all_features(
441
446
  OUT_PATH: str = None,
442
447
  feature_file: str = None,
443
448
  ):
444
-
445
449
  if time_limit_high_s is not None:
446
450
  df = df[df["time"] < time_limit_high_s * 1000]
447
451
  if time_limit_low_s is not None:
@@ -449,7 +453,7 @@ def plot_all_features(
449
453
 
450
454
  cols_plt = [c for c in df.columns if c != "time"]
451
455
  if normalize is True:
452
- data_plt = stats.zscore(df[cols_plt])
456
+ data_plt = stats.zscore(df[cols_plt], nan_policy="omit")
453
457
  else:
454
458
  data_plt = df[cols_plt]
455
459
 
@@ -487,7 +491,6 @@ class NM_Plot:
487
491
  sess_right: Optional[bool] = False,
488
492
  proj_matrix_cortex: np.ndarray | None = None,
489
493
  ) -> None:
490
-
491
494
  self.grid_cortex = grid_cortex
492
495
  self.grid_subcortex = grid_subcortex
493
496
  self.ecog_strip = ecog_strip
@@ -510,7 +513,6 @@ class NM_Plot:
510
513
  ) = nm_IO.read_plot_modules()
511
514
 
512
515
  def plot_grid_elec_3d(self) -> None:
513
-
514
516
  plot_grid_elec_3d(np.array(self.grid_cortex), np.array(self.ecog_strip))
515
517
 
516
518
  def plot_cortex(
@@ -552,7 +554,6 @@ class NM_Plot:
552
554
  axes.axes.set_aspect("equal", anchor="C")
553
555
 
554
556
  if grid_cortex is not None:
555
-
556
557
  grid_color = (
557
558
  np.ones(grid_cortex.shape[0])
558
559
  if grid_color is None
@@ -604,6 +605,8 @@ class NM_Plot:
604
605
  feature_name=feature_str_add,
605
606
  )
606
607
  plt.savefig(plt_path, bbox_inches="tight")
607
- print("Feature epoch average figure saved to: " + str(plt_path))
608
+ logger.info(
609
+ f"Feature epoch average figure saved to: {str(plt_path)}"
610
+ )
608
611
  if show_plot is False:
609
612
  plt.close()
@@ -4,10 +4,8 @@ import pandas as pd
4
4
 
5
5
 
6
6
  class ReReferencer:
7
-
8
7
  ref_matrix: np.ndarray
9
8
 
10
-
11
9
  def __init__(
12
10
  self,
13
11
  sfreq: int | float,
@@ -28,11 +26,15 @@ class ReReferencer:
28
26
  ValueError: rereferencing using undefined channel
29
27
  ValueError: rereferencing to same channel
30
28
  """
31
- (channels_used,) = np.where((nm_channels.used == 1))
29
+ nm_channels = nm_channels[nm_channels["used"] == 1].reset_index(
30
+ drop=True
31
+ )
32
+ # (channels_used,) = np.where((nm_channels.used == 1))
32
33
 
33
34
  ch_names = nm_channels["name"].tolist()
34
35
 
35
- if len(ch_names) == 1:
36
+ # no re-referencing is being performed when there is a single channel present only
37
+ if nm_channels.shape[0] in (0, 1):
36
38
  self.ref_matrix = None
37
39
  return
38
40
 
@@ -48,8 +50,8 @@ class ReReferencer:
48
50
  ref_matrix = np.zeros((len(nm_channels), len(nm_channels)))
49
51
  for ind in range(len(nm_channels)):
50
52
  ref_matrix[ind, ind] = 1
51
- if ind not in channels_used:
52
- continue
53
+ # if ind not in channels_used:
54
+ # continue
53
55
  ref = refs[ind]
54
56
  if ref.lower() == "none" or pd.isnull(ref):
55
57
  ref_idx = None
@@ -84,10 +86,10 @@ class ReReferencer:
84
86
  shape(n_channels, n_samples) - data to be rereferenced.
85
87
 
86
88
  Returns:
87
- reref_data (numpy ndarray):
89
+ reref_data (numpy ndarray):
88
90
  shape(n_channels, n_samples) - rereferenced data
89
91
  """
90
92
  if self.ref_matrix is not None:
91
93
  return self.ref_matrix @ data
92
94
  else:
93
- return data
95
+ return data
@@ -1,9 +1,13 @@
1
1
  """This module contains the class to process a given batch of data."""
2
+
2
3
  from enum import Enum
3
4
  import math
4
5
  import os
5
6
  from time import time
6
7
  from typing import Protocol, Type
8
+ import logging
9
+
10
+ logger = logging.getLogger("PynmLogger")
7
11
 
8
12
  import numpy as np
9
13
  import pandas as pd
@@ -16,6 +20,7 @@ from py_neuromodulation import (
16
20
  nm_projection,
17
21
  nm_rereference,
18
22
  nm_resample,
23
+ nm_filter_preprocessing,
19
24
  )
20
25
 
21
26
  _PathLike = str | os.PathLike
@@ -25,8 +30,7 @@ class Preprocessor(Protocol):
25
30
  def process(self, data: np.ndarray) -> np.ndarray:
26
31
  pass
27
32
 
28
- def test_settings(self, settings: dict):
29
- ...
33
+ def test_settings(self, settings: dict): ...
30
34
 
31
35
 
32
36
  _PREPROCESSING_CONSTRUCTORS = [
@@ -73,7 +77,7 @@ class DataProcessor:
73
77
  notch_filter : nm_filter.NotchFilter,
74
78
  Notch Filter object, needs to be instantiated beforehand
75
79
  verbose : boolean
76
- if True, print out signal processed and computation time
80
+ if True, log signal processed and computation time
77
81
  """
78
82
  self.settings = self._load_settings(settings)
79
83
  self.nm_channels = self._load_nm_channels(nm_channels)
@@ -119,6 +123,12 @@ class DataProcessor:
119
123
  **self.settings.get(settings_str, {}),
120
124
  )
121
125
  self.preprocessors.append(preprocessor)
126
+ case "preprocessing_filter":
127
+ preprocessor = nm_filter_preprocessing.PreprocessingFilter(
128
+ settings=self.settings,
129
+ sfreq=self.sfreq_raw,
130
+ )
131
+ self.preprocessors.append(preprocessor)
122
132
  case _:
123
133
  raise ValueError(
124
134
  "Invalid preprocessing method. Must be one of"
@@ -331,7 +341,7 @@ class DataProcessor:
331
341
 
332
342
  nan_channels = np.isnan(data).any(axis=1)
333
343
 
334
- data = np.nan_to_num(data) # [self.feature_idx, :]needs to be before preprocessing
344
+ data = np.nan_to_num(data)[self.feature_idx, :]
335
345
 
336
346
  for processor in self.preprocessors:
337
347
  data = processor.process(data)
@@ -341,8 +351,13 @@ class DataProcessor:
341
351
 
342
352
  # normalize features
343
353
  if self.settings["postprocessing"]["feature_normalization"]:
344
- normed_features = self.feature_normalizer.process(np.fromiter(features_dict.values(), dtype="float"))
345
- features_dict = {key: normed_features[idx] for idx, key in enumerate(features_dict.keys())}
354
+ normed_features = self.feature_normalizer.process(
355
+ np.fromiter(features_dict.values(), dtype="float")
356
+ )
357
+ features_dict = {
358
+ key: normed_features[idx]
359
+ for idx, key in enumerate(features_dict.keys())
360
+ }
346
361
 
347
362
  features_current = pd.Series(
348
363
  data=list(features_dict.values()),
@@ -364,7 +379,7 @@ class DataProcessor:
364
379
  ] = np.nan
365
380
 
366
381
  if self.verbose is True:
367
- print(
382
+ logger.info(
368
383
  "Last batch took: "
369
384
  + str(np.round(time() - start_time, 2))
370
385
  + " seconds"
@@ -390,14 +405,14 @@ class DataProcessor:
390
405
  sidecar["coords"] = self.projection.coords
391
406
  if self.settings["postprocessing"]["project_cortex"]:
392
407
  sidecar["grid_cortex"] = self.projection.grid_cortex
393
- sidecar[
394
- "proj_matrix_cortex"
395
- ] = self.projection.proj_matrix_cortex
408
+ sidecar["proj_matrix_cortex"] = (
409
+ self.projection.proj_matrix_cortex
410
+ )
396
411
  if self.settings["postprocessing"]["project_subcortex"]:
397
412
  sidecar["grid_subcortex"] = self.projection.grid_subcortex
398
- sidecar[
399
- "proj_matrix_subcortex"
400
- ] = self.projection.proj_matrix_subcortex
413
+ sidecar["proj_matrix_subcortex"] = (
414
+ self.projection.proj_matrix_subcortex
415
+ )
401
416
  if additional_args is not None:
402
417
  sidecar = sidecar | additional_args
403
418
 
@@ -105,23 +105,12 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
105
105
  peak_right_val (np.ndarray): value of righ peak
106
106
  """
107
107
 
108
- ind_greater = np.where(arr_ind_peaks > trough_ind)[0]
109
- if ind_greater.shape[0] == 0:
110
- raise NoValidTroughException("No valid trough")
111
- val_ind_greater = arr_ind_peaks[ind_greater]
112
- peak_right_idx = arr_ind_peaks[
113
- ind_greater[np.argsort(val_ind_greater)[0]]
114
- ]
115
-
116
- ind_smaller = np.where(arr_ind_peaks < trough_ind)[0]
117
- if ind_smaller.shape[0] == 0:
118
- raise NoValidTroughException("No valid trough")
119
-
120
- val_ind_smaller = arr_ind_peaks[ind_smaller]
121
- peak_left_idx = arr_ind_peaks[
122
- ind_smaller[np.argsort(val_ind_smaller)[-1]]
123
- ]
108
+ try: peak_right_idx = arr_ind_peaks[arr_ind_peaks > trough_ind][0]
109
+ except IndexError: raise NoValidTroughException("No valid trough")
124
110
 
111
+ try: peak_left_idx = arr_ind_peaks[arr_ind_peaks < trough_ind][-1]
112
+ except IndexError: raise NoValidTroughException("No valid trough")
113
+
125
114
  return (
126
115
  peak_left_idx,
127
116
  peak_right_idx,
@@ -150,12 +139,10 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
150
139
  """
151
140
  for ch_idx, ch_name in enumerate(self.ch_names):
152
141
  for filter_name, filter in self.list_filter:
153
- if filter_name == "no_filter":
154
- self.data_process_sw = data[ch_idx, :]
155
- else:
156
- self.data_process_sw = signal.convolve(
157
- data[ch_idx, :], filter, mode="same"
158
- )
142
+ self.data_process_sw = (data[ch_idx, :]
143
+ if filter_name == "no_filter"
144
+ else signal.fftconvolve(data[ch_idx, :], filter, mode="same")
145
+ )
159
146
 
160
147
  # check settings if troughs and peaks are analyzed
161
148
 
@@ -267,123 +254,103 @@ class SharpwaveAnalyzer(nm_features_abc.Feature):
267
254
  distance=self.sw_settings["detect_troughs"]["distance_troughs_ms"],
268
255
  )[0]
269
256
 
270
- for trough_idx in troughs:
271
- try:
272
- (
273
- peak_idx_left,
274
- peak_idx_right,
275
- peak_left,
276
- peak_right,
277
- ) = self._get_peaks_around(
278
- trough_idx, peaks, self.data_process_sw
279
- )
280
- except NoValidTroughException:
281
- # in this case there are no adjacent two peaks around this trough
282
- # str(e) could print the exception error message
283
- # print(str(e))
257
+ """ Find left and right peak indexes for each trough """
258
+ peak_pointer = 0
259
+ peak_idx_left = []
260
+ peak_idx_right = []
261
+ first_valid = last_valid = 0
262
+
263
+ for i, trough_idx in enumerate(troughs):
264
+
265
+ # Locate peak right of current trough
266
+ while peak_pointer < peaks.size and peaks[peak_pointer] < trough_idx:
267
+ peak_pointer += 1
268
+
269
+ if peak_pointer - 1 < 0:
270
+ # If trough has no peak to it's left, it's not valid
271
+ first_valid = i + 1 # Try with next one
284
272
  continue
285
273
 
286
- trough = self.data_process_sw[trough_idx]
287
- self.trough.append(trough)
288
- self.troughs_idx.append(trough_idx)
289
-
290
- if self.sw_settings["sharpwave_features"]["interval"] is True:
291
- if len(self.troughs_idx) > 1:
292
- # take the last identified trough idx
293
- # corresponds here to second last trough_idx
294
-
295
- interval = (trough_idx - self.troughs_idx[-2]) * (
296
- 1000 / self.sfreq
297
- )
298
- else:
299
- # set first interval to zero
300
- interval = 0
301
- self.interval.append(interval)
302
-
303
- if self.sw_settings["sharpwave_features"]["peak_left"] is True:
304
- self.peak_left.append(peak_left)
305
-
306
- if self.sw_settings["sharpwave_features"]["peak_right"] is True:
307
- self.peak_right.append(peak_right)
308
-
309
- if self.sw_settings["sharpwave_features"]["sharpness"] is True:
310
- # check if sharpness can be calculated
311
- # trough_idx 5 ms need to be consistent
312
- if (trough_idx - int(5 * (1000 / self.sfreq)) <= 0) or (
313
- trough_idx + int(5 * (1000 / self.sfreq))
314
- >= self.data_process_sw.shape[0]
315
- ):
316
- continue
317
-
318
- sharpness = (
319
- (
320
- self.data_process_sw[trough_idx]
321
- - self.data_process_sw[
322
- trough_idx - int(5 * (1000 / self.sfreq))
323
- ]
324
- )
325
- + (
326
- self.data_process_sw[trough_idx]
327
- - self.data_process_sw[
328
- trough_idx + int(5 * (1000 / self.sfreq))
329
- ]
330
- )
331
- ) / 2
332
-
333
- self.sharpness.append(sharpness)
334
-
335
- if self.sw_settings["sharpwave_features"]["rise_steepness"] is True:
336
- # steepness is calculated as the first derivative
337
- # from peak/trough to trough/peak
338
- # here + 1 due to python syntax, s.t. the last element is included
339
- rise_steepness = np.max(
340
- np.diff(
341
- self.data_process_sw[peak_idx_left : trough_idx + 1]
342
- )
343
- )
344
- self.rise_steepness.append(rise_steepness)
345
-
346
- if (
347
- self.sw_settings["sharpwave_features"]["decay_steepness"]
348
- is True
349
- ):
350
- decay_steepness = np.max(
351
- np.diff(
352
- self.data_process_sw[trough_idx : peak_idx_right + 1]
353
- )
354
- )
355
- self.decay_steepness.append(decay_steepness)
356
-
357
- if (
358
- self.sw_settings["sharpwave_features"]["rise_steepness"] is True
359
- and self.sw_settings["sharpwave_features"]["decay_steepness"]
360
- is True
361
- and self.sw_settings["sharpwave_features"]["slope_ratio"]
362
- is True
363
- ):
364
- self.slope_ratio.append(rise_steepness - decay_steepness)
365
-
366
- if self.sw_settings["sharpwave_features"]["prominence"] is True:
367
- self.prominence.append(
368
- np.abs(
369
- (peak_right + peak_left) / 2
370
- - self.data_process_sw[trough_idx]
371
- )
372
- )
373
-
374
- if self.sw_settings["sharpwave_features"]["decay_time"] is True:
375
- self.decay_time.append(
376
- (peak_idx_left - trough_idx) * (1000 / self.sfreq)
377
- ) # ms
378
-
379
- if self.sw_settings["sharpwave_features"]["rise_time"] is True:
380
- self.rise_time.append(
381
- (peak_idx_right - trough_idx) * (1000 / self.sfreq)
382
- ) # ms
383
-
384
- if self.sw_settings["sharpwave_features"]["width"] is True:
385
- self.width.append(peak_idx_right - peak_idx_left) # ms
274
+ if peak_pointer == peaks.size:
275
+ # If we went past the end of the peaks list, trough had no peak to its right
276
+ continue
386
277
 
278
+ last_valid = i
279
+ peak_idx_left.append(peaks[peak_pointer - 1])
280
+ peak_idx_right.append(peaks[peak_pointer])
281
+
282
+ troughs = troughs[first_valid:last_valid + 1] # Remove non valid troughs
283
+
284
+ peak_idx_left = np.array(peak_idx_left, dtype=np.integer)
285
+ peak_idx_right = np.array(peak_idx_right, dtype=np.integer)
286
+
287
+ peak_left = self.data_process_sw[peak_idx_left]
288
+ peak_right = self.data_process_sw[peak_idx_right]
289
+ trough_values = self.data_process_sw[troughs]
290
+
291
+ # No need to store trough data as it is not used anywhere else in the program
292
+ # self.trough.append(trough)
293
+ # self.troughs_idx.append(trough_idx)
294
+
295
+ """ Calculate features (vectorized) """
296
+
297
+ if self.sw_settings["sharpwave_features"]["interval"]:
298
+ self.interval = np.concatenate(([0], np.diff(troughs))) * (1000 / self.sfreq)
299
+
300
+ if self.sw_settings["sharpwave_features"]["peak_left"]:
301
+ self.peak_left = peak_left
302
+
303
+ if self.sw_settings["sharpwave_features"]["peak_right"]:
304
+ self.peak_right = peak_right
305
+
306
+ if self.sw_settings["sharpwave_features"]["sharpness"]:
307
+ # sharpess is calculated on a +- 5 ms window
308
+ # valid troughs need 5 ms of margin on both siddes
309
+ troughs_valid = troughs[np.logical_and(
310
+ troughs - int(5 * (1000 / self.sfreq)) > 0,
311
+ troughs + int(5 * (1000 / self.sfreq)) < self.data_process_sw.shape[0])]
312
+
313
+ self.sharpness = (
314
+ (self.data_process_sw[troughs_valid] - self.data_process_sw[troughs_valid - int(5 * (1000 / self.sfreq))]) +
315
+ (self.data_process_sw[troughs_valid] - self.data_process_sw[troughs_valid + int(5 * (1000 / self.sfreq))])
316
+ ) / 2
317
+
318
+ if (self.sw_settings["sharpwave_features"]["rise_steepness"] or
319
+ self.sw_settings["sharpwave_features"]["decay_steepness"]):
320
+
321
+ # steepness is calculated as the first derivative
322
+ steepness = np.concatenate(([0],np.diff(self.data_process_sw)))
323
+
324
+ if self.sw_settings["sharpwave_features"]["rise_steepness"]: # left peak -> trough
325
+ # + 1 due to python syntax, s.t. the last element is included
326
+ self.rise_steepness = np.array([
327
+ np.max(np.abs(steepness[peak_idx_left[i] : troughs[i] + 1]))
328
+ for i in range(trough_idx.size)
329
+ ])
330
+
331
+ if self.sw_settings["sharpwave_features"]["decay_steepness"]: # trough -> right peak
332
+ self.decay_steepness = np.array([
333
+ np.max(np.abs(steepness[troughs[i] : peak_idx_right[i] + 1]))
334
+ for i in range(trough_idx.size)
335
+ ])
336
+
337
+ if (self.sw_settings["sharpwave_features"]["rise_steepness"] and
338
+ self.sw_settings["sharpwave_features"]["decay_steepness"] and
339
+ self.sw_settings["sharpwave_features"]["slope_ratio"]):
340
+ self.slope_ratio = self.rise_steepness - self.decay_steepness
341
+
342
+ if self.sw_settings["sharpwave_features"]["prominence"]:
343
+ self.prominence = np.abs((peak_right + peak_left) / 2 - trough_values)
344
+
345
+ if self.sw_settings["sharpwave_features"]["decay_time"]:
346
+ self.decay_time = (peak_idx_left - troughs) * (1000 / self.sfreq) # ms
347
+
348
+ if self.sw_settings["sharpwave_features"]["rise_time"]:
349
+ self.rise_time = (peak_idx_right - troughs) * (1000 / self.sfreq) # ms
350
+
351
+ if self.sw_settings["sharpwave_features"]["width"]:
352
+ self.width = peak_idx_right - peak_idx_left # ms
353
+
387
354
  @staticmethod
388
355
  def test_settings(
389
356
  s: dict,