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
@@ -2,6 +2,7 @@ import random
2
2
  import copy
3
3
 
4
4
  import matplotlib.pyplot as plt
5
+
5
6
  # from numba import njit
6
7
  import numpy as np
7
8
  import pandas as pd
@@ -33,7 +34,7 @@ def fitlm_kfold(x, y, kfold_splits=5):
33
34
  scores.append(score)
34
35
  coeffs = np.vstack((coeffs, model.coef_))
35
36
  coeffs = list(np.delete(coeffs, 0))
36
- return scores, coeffs, model, ['scores', 'coeffs', 'model']
37
+ return scores, coeffs, model, ["scores", "coeffs", "model"]
37
38
 
38
39
 
39
40
  def zscore(data):
@@ -76,14 +77,14 @@ def permutationTestSpearmansRho(x, y, plot_distr=True, x_unit=None, p=5000):
76
77
 
77
78
  # calculate p value
78
79
  if gT < 0:
79
- p_val = len(np.where(pD <= gT)[0])/p
80
+ p_val = len(np.where(pD <= gT)[0]) / p
80
81
  else:
81
- p_val = len(np.where(pD >= gT)[0])/p
82
+ p_val = len(np.where(pD >= gT)[0]) / p
82
83
 
83
84
  if plot_distr is True:
84
85
  plt.hist(pD, bins=30, label="permutation results")
85
86
  plt.axvline(gT, color="orange", label="ground truth")
86
- plt.title("ground truth " + x_unit + "="+str(gT) + " p=" + str(p_val))
87
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
87
88
  plt.xlabel(x_unit)
88
89
  plt.legend()
89
90
  plt.show()
@@ -120,19 +121,23 @@ def permutationTest(x, y, plot_distr=True, x_unit=None, p=5000):
120
121
  random.shuffle(pS)
121
122
  # Compute permuted absolute difference of your two sampled
122
123
  # distributions and store it in pD:
123
- pD.append(np.abs(np.average(pS[0:int(len(pS)/2)]) - np.average(
124
- pS[int(len(pS)/2):])))
124
+ pD.append(
125
+ np.abs(
126
+ np.average(pS[0 : int(len(pS) / 2)])
127
+ - np.average(pS[int(len(pS) / 2) :])
128
+ )
129
+ )
125
130
 
126
131
  # Calculate p-value
127
132
  if gT < 0:
128
- p_val = len(np.where(pD <= gT)[0])/p
133
+ p_val = len(np.where(pD <= gT)[0]) / p
129
134
  else:
130
- p_val = len(np.where(pD >= gT)[0])/p
135
+ p_val = len(np.where(pD >= gT)[0]) / p
131
136
 
132
137
  if plot_distr is True:
133
138
  plt.hist(pD, bins=30, label="permutation results")
134
139
  plt.axvline(gT, color="orange", label="ground truth")
135
- plt.title("ground truth "+x_unit+"="+str(gT)+" p="+str(p_val))
140
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
136
141
  plt.xlabel(x_unit)
137
142
  plt.legend()
138
143
  plt.show()
@@ -165,17 +170,20 @@ def permutationTest_relative(x, y, plot_distr=True, x_unit=None, p=5000):
165
170
  l_.append((x[i], y[i]))
166
171
  else:
167
172
  l_.append((y[i], x[i]))
168
- pD.append(np.abs(np.average(np.array(l_)[:, 0]) - np.average(
169
- np.array(l_)[:, 1])))
173
+ pD.append(
174
+ np.abs(
175
+ np.average(np.array(l_)[:, 0]) - np.average(np.array(l_)[:, 1])
176
+ )
177
+ )
170
178
  if gT < 0:
171
- p_val = len(np.where(pD <= gT)[0])/p
179
+ p_val = len(np.where(pD <= gT)[0]) / p
172
180
  else:
173
- p_val = len(np.where(pD >= gT)[0])/p
181
+ p_val = len(np.where(pD >= gT)[0]) / p
174
182
 
175
183
  if plot_distr is True:
176
184
  plt.hist(pD, bins=30, label="permutation results")
177
185
  plt.axvline(gT, color="orange", label="ground truth")
178
- plt.title("ground truth "+x_unit+"="+str(gT)+" p="+str(p_val))
186
+ plt.title("ground truth " + x_unit + "=" + str(gT) + " p=" + str(p_val))
179
187
  plt.xlabel(x_unit)
180
188
  plt.legend()
181
189
  plt.show()
@@ -211,13 +219,13 @@ def permutation_numba_onesample(x, y, n_perm, two_tailed=True):
211
219
  """
212
220
  if two_tailed is True:
213
221
  zeroed = x - y
214
- print(zeroed)
215
222
  z = np.abs(np.mean(zeroed))
216
223
  p = np.empty(n_perm)
217
224
  # Run the simulation n_perm times
218
225
  for i in np.arange(n_perm):
219
226
  sign = np.random.choice(
220
- a=np.array([-1., 1.]), size=len(x), replace=True)
227
+ a=np.array([-1.0, 1.0]), size=len(x), replace=True
228
+ )
221
229
  p[i] = np.abs(np.mean(zeroed * sign))
222
230
  else:
223
231
  zeroed = x - y
@@ -226,7 +234,8 @@ def permutation_numba_onesample(x, y, n_perm, two_tailed=True):
226
234
  # Run the simulation n_perm times
227
235
  for i in np.arange(n_perm):
228
236
  sign = np.random.choice(
229
- a=np.array([-1., 1.]), size=len(x), replace=True)
237
+ a=np.array([-1.0, 1.0]), size=len(x), replace=True
238
+ )
230
239
  p[i] = np.mean(zeroed * sign)
231
240
  # Return p-value
232
241
  return z, (np.sum(p >= z)) / n_perm
@@ -311,7 +320,8 @@ def cluster_wise_p_val_correction(p_arr, p_sig=0.05, num_permutations=10000):
311
320
  # first cluster is assigned to be 1 from measure.label
312
321
  index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[0]
313
322
  p_cluster_sum[cluster_i] = np.sum(
314
- np.array(1 - p_arr)[index_cluster[cluster_i]])
323
+ np.array(1 - p_arr)[index_cluster[cluster_i]]
324
+ )
315
325
  # p_min corresponds to the most unlikely cluster
316
326
  p_min = np.max(p_cluster_sum)
317
327
 
@@ -320,11 +330,13 @@ def cluster_wise_p_val_correction(p_arr, p_sig=0.05, num_permutations=10000):
320
330
  # loop through random permutation cycles
321
331
  r_per_arr = np.zeros(num_permutations)
322
332
  for r in range(num_permutations):
323
- r_per = np.random.randint(low=0, high=p_arr.shape[0],
324
- size=p_arr.shape[0])
333
+ r_per = np.random.randint(
334
+ low=0, high=p_arr.shape[0], size=p_arr.shape[0]
335
+ )
325
336
 
326
- labels, num_clusters = measure.label(p_arr[r_per] <= p_sig,
327
- return_num=True)
337
+ labels, num_clusters = measure.label(
338
+ p_arr[r_per] <= p_sig, return_num=True
339
+ )
328
340
 
329
341
  index_cluster = {}
330
342
  if num_clusters == 0:
@@ -332,11 +344,12 @@ def cluster_wise_p_val_correction(p_arr, p_sig=0.05, num_permutations=10000):
332
344
  else:
333
345
  p_cluster_sum = np.zeros(num_clusters)
334
346
  for cluster_i in np.arange(num_clusters):
335
- index_cluster[cluster_i] = np.where(
336
- labels == cluster_i + 1)[
337
- 0] # first cluster is assigned to be 1 from measure.label
347
+ index_cluster[cluster_i] = np.where(labels == cluster_i + 1)[
348
+ 0
349
+ ] # first cluster is assigned to be 1 from measure.label
338
350
  p_cluster_sum[cluster_i] = np.sum(
339
- np.array(1 - p_arr[r_per])[index_cluster[cluster_i]])
351
+ np.array(1 - p_arr[r_per])[index_cluster[cluster_i]]
352
+ )
340
353
  # corresponds to the most unlikely cluster
341
354
  r_per_arr[r] = np.max(p_cluster_sum)
342
355
 
@@ -432,7 +445,8 @@ def cluster_wise_p_val_correction_numba(p_arr, p_sig, n_perm):
432
445
  r_per_arr = np.zeros(n_perm_)
433
446
  for r in range(n_perm_):
434
447
  r_per = np.random.randint(
435
- low=0, high=p_arr_.shape[0], size=p_arr_.shape[0])
448
+ low=0, high=p_arr_.shape[0], size=p_arr_.shape[0]
449
+ )
436
450
  labels_, n_clusters = cluster(p_arr_[r_per] <= p_sig_)
437
451
 
438
452
  cluster_ind = {}
@@ -441,10 +455,10 @@ def cluster_wise_p_val_correction_numba(p_arr, p_sig, n_perm):
441
455
  else:
442
456
  p_sum = np.zeros(n_clusters)
443
457
  for ind in range(n_clusters):
444
- cluster_ind[ind] = \
445
- np.where(labels_ == ind + 1)[0]
458
+ cluster_ind[ind] = np.where(labels_ == ind + 1)[0]
446
459
  p_sum[ind] = np.sum(
447
- np.asarray(1 - p_arr_[r_per])[cluster_ind[ind]])
460
+ np.asarray(1 - p_arr_[r_per])[cluster_ind[ind]]
461
+ )
448
462
  r_per_arr[r] = np.max(p_sum)
449
463
  return r_per_arr
450
464
 
@@ -1,9 +1,15 @@
1
1
  """Module that contains PNStream ABC."""
2
+
2
3
  from abc import ABC, abstractmethod
3
4
  import os
4
5
  import pathlib
5
6
  import _pickle as cPickle
6
7
 
8
+ from .utils import _logging # logger initialization
9
+
10
+ # Logger use in different modules: logger = logging.getLogger("PynmLogger")
11
+
12
+
7
13
  import pandas as pd
8
14
  from sklearn import base
9
15
 
@@ -12,7 +18,7 @@ from py_neuromodulation import (
12
18
  nm_IO,
13
19
  nm_plots,
14
20
  nm_run_analysis,
15
- nm_settings
21
+ nm_settings,
16
22
  )
17
23
 
18
24
  _PathLike = str | os.PathLike
@@ -72,7 +78,9 @@ class PNStream(ABC):
72
78
  self.settings = self._load_settings(settings)
73
79
 
74
80
  if sampling_rate_features_hz is not None:
75
- self.settings["sampling_rate_features_hz"] = sampling_rate_features_hz
81
+ self.settings["sampling_rate_features_hz"] = (
82
+ sampling_rate_features_hz
83
+ )
76
84
 
77
85
  self.nm_channels = self._load_nm_channels(nm_channels)
78
86
  if path_grids is None:
@@ -138,10 +146,12 @@ class PNStream(ABC):
138
146
  nm_channels: pd.DataFrame | _PathLike,
139
147
  ) -> pd.DataFrame:
140
148
  if not isinstance(nm_channels, pd.DataFrame):
141
- nm_channels = nm_IO.load_nm_channels(nm_channels)
142
-
143
- if nm_channels.query("used == 1 and target == 0").shape[0] == 0:
144
- raise ValueError("No channels selected for analysis that have column 'used' = 1 and 'target' = 0. Please check your nm_channels")
149
+ nm_channels = nm_IO.load_nm_channels(nm_channels)
150
+
151
+ if nm_channels.query("used == 1 and target == 0").shape[0] == 0:
152
+ raise ValueError(
153
+ "No channels selected for analysis that have column 'used' = 1 and 'target' = 0. Please check your nm_channels"
154
+ )
145
155
 
146
156
  return nm_channels
147
157
 
@@ -149,7 +159,7 @@ class PNStream(ABC):
149
159
  def _load_settings(settings: dict | _PathLike | None) -> dict:
150
160
  if isinstance(settings, dict):
151
161
  return settings
152
- if settings is None:
162
+ if settings is None:
153
163
  return nm_settings.get_default_settings()
154
164
  return nm_IO.read_settings(str(settings))
155
165
 
@@ -196,9 +206,7 @@ class PNStream(ABC):
196
206
  ) -> None:
197
207
  self.run_analysis.save_nm_channels(out_path_root, folder_name)
198
208
 
199
- def save_settings(
200
- self, out_path_root: _PathLike, folder_name: str
201
- ) -> None:
209
+ def save_settings(self, out_path_root: _PathLike, folder_name: str) -> None:
202
210
  self.run_analysis.save_settings(out_path_root, folder_name)
203
211
 
204
212
  def save_sidecar(self, out_path_root: _PathLike, folder_name: str) -> None:
@@ -1,17 +1,28 @@
1
1
  """Module for offline data streams."""
2
- import math
3
- import os
4
2
 
3
+ import os
4
+ from joblib import Parallel, delayed
5
5
  import numpy as np
6
6
  import pandas as pd
7
+ from itertools import count
8
+ import logging
9
+
10
+ logger = logging.getLogger("PynmLogger")
11
+
12
+ import mne
7
13
 
8
- from py_neuromodulation import nm_generator, nm_IO, nm_stream_abc, nm_define_nmchannels
14
+ from py_neuromodulation import (
15
+ nm_generator,
16
+ nm_IO,
17
+ nm_stream_abc,
18
+ nm_define_nmchannels,
19
+ )
9
20
 
10
21
  _PathLike = str | os.PathLike
11
22
 
12
23
 
13
24
  class _OfflineStream(nm_stream_abc.PNStream):
14
- """Offline stream base class.
25
+ """Offline stream base class.
15
26
  This class can be inhereted for different types of offline streams, e.g. epoch-based or continuous.
16
27
 
17
28
  Parameters
@@ -19,19 +30,38 @@ class _OfflineStream(nm_stream_abc.PNStream):
19
30
  nm_stream_abc : nm_stream_abc.PNStream
20
31
  """
21
32
 
22
- def _add_labels(
23
- self, features: pd.DataFrame, data: np.ndarray
24
- ) -> pd.DataFrame:
25
- """Add resampled labels to features if there are target channels."""
33
+ def _add_target(
34
+ self, feature_series: pd.Series, data: np.ndarray
35
+ ) -> pd.Series:
36
+ """Add target channels to feature series.
37
+
38
+ Parameters
39
+ ----------
40
+ feature_series : pd.Series
41
+ data : np.ndarray
42
+ Raw data with shape (n_channels, n_samples). Channels not for feature computation are also included
43
+
44
+ Returns
45
+ -------
46
+ pd.Series
47
+ feature series with target channels added
48
+ """
49
+
26
50
  if self.nm_channels["target"].sum() > 0:
27
- features = nm_IO.add_labels(
28
- features=features,
29
- settings=self.settings,
30
- nm_channels=self.nm_channels,
31
- raw_arr_data=data,
32
- fs=self.sfreq,
33
- )
34
- return features
51
+ if not self.target_idx_initialized:
52
+ self.target_indexes = self.nm_channels[
53
+ self.nm_channels["target"] == 1
54
+ ].index
55
+ self.target_names = self.nm_channels.loc[
56
+ self.target_indexes, "name"
57
+ ].to_list()
58
+ self.target_idx_initialized = True
59
+
60
+ for target_idx, target_name in zip(
61
+ self.target_indexes, self.target_names
62
+ ):
63
+ feature_series[target_name] = data[target_idx, -1]
64
+ return feature_series
35
65
 
36
66
  def _add_timestamp(
37
67
  self, feature_series: pd.Series, cnt_samples: int
@@ -41,11 +71,10 @@ class _OfflineStream(nm_stream_abc.PNStream):
41
71
  Due to normalization run_analysis needs to keep track of the counted
42
72
  samples. These are accessed here for time conversion.
43
73
  """
44
- timestamp = cnt_samples * 1000 / self.sfreq
45
74
  feature_series["time"] = cnt_samples * 1000 / self.sfreq
46
75
 
47
76
  if self.verbose:
48
- print(
77
+ logging.info(
49
78
  str(np.round(feature_series["time"] / 1000, 2))
50
79
  + " seconds of data processed"
51
80
  )
@@ -76,45 +105,149 @@ class _OfflineStream(nm_stream_abc.PNStream):
76
105
  )
77
106
  return data.to_numpy()
78
107
 
108
+ def _check_settings_for_parallel(self):
109
+ """Check specified settings and raise error if parallel processing is not possible.
110
+
111
+ Raises:
112
+ ValueError: depending on the settings, parallel processing is not possible
113
+ """
114
+
115
+ if "raw_normalization" in self.settings["preprocessing"]:
116
+ raise ValueError(
117
+ "Parallel processing is not possible with raw_normalization normalization."
118
+ )
119
+ if self.settings["postprocessing"]["feature_normalization"] is True:
120
+ raise ValueError(
121
+ "Parallel processing is not possible with feature normalization."
122
+ )
123
+ if self.settings["features"]["bursts"] is True:
124
+ raise ValueError(
125
+ "Parallel processing is not possible with burst estimation."
126
+ )
127
+
128
+ def _process_batch(self, data_batch, cnt_samples):
129
+ feature_series = self.run_analysis.process(
130
+ data_batch.astype(np.float64)
131
+ )
132
+ feature_series = self._add_timestamp(feature_series, cnt_samples)
133
+ feature_series = self._add_target(
134
+ feature_series=feature_series, data=data_batch
135
+ )
136
+ return feature_series
137
+
79
138
  def _run_offline(
80
139
  self,
81
140
  data: np.ndarray,
82
141
  out_path_root: _PathLike | None = None,
83
142
  folder_name: str = "sub",
143
+ parallel: bool = False,
144
+ n_jobs: int = -2,
84
145
  ) -> pd.DataFrame:
85
146
  generator = nm_generator.raw_data_generator(
86
147
  data=data,
87
148
  settings=self.settings,
88
- sfreq=math.floor(self.sfreq),
149
+ sfreq=self.sfreq,
89
150
  )
90
- features = []
91
- sample_add = int(self.sfreq / self.run_analysis.sfreq_features)
92
151
 
93
- offset_time = self.settings["segment_length_features_ms"]
94
- offset_start = np.ceil(offset_time / 1000 * self.sfreq).astype(int)
152
+ sample_add = self.sfreq / self.run_analysis.sfreq_features
95
153
 
96
- cnt_samples = offset_start
154
+ offset_time = self.settings["segment_length_features_ms"]
155
+ # offset_start = np.ceil(offset_time / 1000 * self.sfreq).astype(int)
156
+ offset_start = offset_time / 1000 * self.sfreq
157
+
158
+ if parallel:
159
+ l_features = Parallel(n_jobs=n_jobs, verbose=10)(
160
+ delayed(self._process_batch)(data_batch, cnt_samples)
161
+ for data_batch, cnt_samples in zip(
162
+ generator, count(offset_start, sample_add)
163
+ )
164
+ )
97
165
 
98
- while True:
99
- data_batch = next(generator, None)
100
- if data_batch is None:
101
- break
102
- feature_series = self.run_analysis.process(data_batch.astype(np.float64))
103
- feature_series = self._add_timestamp(feature_series, cnt_samples)
104
- features.append(feature_series)
166
+ else:
167
+ l_features = []
168
+ cnt_samples = offset_start
169
+ while True:
170
+ data_batch = next(generator, None)
171
+ if data_batch is None:
172
+ break
173
+ feature_series = self.run_analysis.process(
174
+ data_batch.astype(np.float64)
175
+ )
176
+ feature_series = self._add_timestamp(
177
+ feature_series, cnt_samples
178
+ )
105
179
 
106
- if self.model is not None:
107
- prediction = self.model.predict(feature_series)
180
+ feature_series = self._add_target(
181
+ feature_series=feature_series, data=data_batch
182
+ )
108
183
 
109
- cnt_samples += sample_add
184
+ l_features.append(feature_series)
110
185
 
111
- feature_df = pd.DataFrame(features)
112
- feature_df = self._add_labels(features=feature_df, data=data)
186
+ cnt_samples += sample_add
187
+ feature_df = pd.DataFrame(l_features)
113
188
 
114
189
  self.save_after_stream(out_path_root, folder_name, feature_df)
115
190
 
116
191
  return feature_df
117
192
 
193
+ def plot_raw_signal(
194
+ self,
195
+ sfreq: float = None,
196
+ data: np.array = None,
197
+ lowpass: float = None,
198
+ highpass: float = None,
199
+ picks: list = None,
200
+ plot_time: bool = True,
201
+ plot_psd: bool = False,
202
+ ) -> None:
203
+ """Use MNE-RawArray Plot to investigate PSD or raw_signal plot.
204
+
205
+ Parameters
206
+ ----------
207
+ sfreq : float
208
+ sampling frequency [Hz]
209
+ data : np.array, optional
210
+ data (n_channels, n_times), by default None
211
+ plot_time : bool, optional
212
+ mne.io.RawArray.plot(), by default True
213
+ plot_psd : bool, optional
214
+ mne.io.RawArray.plot(), by default True
215
+
216
+ Raises
217
+ ------
218
+ ValueError
219
+ raise Exception when no data is passed
220
+ """
221
+ if self.data is None and data is None:
222
+ raise ValueError("No data passed to plot_raw_signal function.")
223
+
224
+ if data is None and self.data is not None:
225
+ data = self.data
226
+
227
+ if sfreq is None:
228
+ sfreq = self.sfreq
229
+
230
+ if self.nm_channels is not None:
231
+ ch_names = self.nm_channels["name"].to_list()
232
+ ch_types = self.nm_channels["type"].to_list()
233
+ else:
234
+ ch_names = [f"ch_{i}" for i in range(data.shape[0])]
235
+ ch_types = ["ecog" for i in range(data.shape[0])]
236
+
237
+ # create mne.RawArray
238
+ info = mne.create_info(
239
+ ch_names=ch_names, sfreq=sfreq, ch_types=ch_types
240
+ )
241
+ raw = mne.io.RawArray(data, info)
242
+
243
+ if picks is not None:
244
+ raw = raw.pick(picks)
245
+ self.raw = raw
246
+ if plot_time:
247
+ raw.plot(highpass=highpass, lowpass=lowpass)
248
+ if plot_psd:
249
+ raw.compute_psd().plot()
250
+
118
251
 
119
252
  class Stream(_OfflineStream):
120
253
  def __init__(
@@ -128,7 +261,7 @@ class Stream(_OfflineStream):
128
261
  path_grids: _PathLike | None = None,
129
262
  coord_names: list | None = None,
130
263
  coord_list: list | None = None,
131
- verbose: bool = True,
264
+ verbose: bool = True,
132
265
  ) -> None:
133
266
  """Stream initialization
134
267
 
@@ -153,11 +286,13 @@ class Stream(_OfflineStream):
153
286
  coord_list : list | None, optional
154
287
  coordinates in the form [[coord_1_x, coord_1_y, coord_1_z], [coord_2_x, coord_2_y, coord_2_z],], by default None
155
288
  verbose : bool, optional
156
- print out stream computation time information, by default True
289
+ log stream computation time information, by default True
157
290
  """
158
291
 
159
292
  if nm_channels is None and data is not None:
160
- nm_channels = nm_define_nmchannels.get_default_channels_from_data(data)
293
+ nm_channels = nm_define_nmchannels.get_default_channels_from_data(
294
+ data
295
+ )
161
296
 
162
297
  if nm_channels is None and data is None:
163
298
  raise ValueError(
@@ -178,11 +313,15 @@ class Stream(_OfflineStream):
178
313
 
179
314
  self.data = data
180
315
 
316
+ self.target_idx_initialized = False
317
+
181
318
  def run(
182
319
  self,
183
320
  data: np.ndarray | pd.DataFrame = None,
184
321
  out_path_root: _PathLike | None = None,
185
322
  folder_name: str = "sub",
323
+ parallel: bool = False,
324
+ n_jobs: int = -2,
186
325
  ) -> pd.DataFrame:
187
326
  """Call run function for offline stream.
188
327
 
@@ -209,8 +348,11 @@ class Stream(_OfflineStream):
209
348
  elif self.data is not None:
210
349
  data = self._handle_data(self.data)
211
350
  elif self.data is None and data is None:
212
- raise ValueError(
213
- "No data passed to run function."
214
- )
351
+ raise ValueError("No data passed to run function.")
352
+
353
+ if parallel is True:
354
+ self._check_settings_for_parallel()
215
355
 
216
- return self._run_offline(data, out_path_root, folder_name)
356
+ return self._run_offline(
357
+ data, out_path_root, folder_name, parallel=parallel, n_jobs=n_jobs
358
+ )
@@ -0,0 +1,24 @@
1
+ import logging
2
+
3
+ # include the filename in the log output
4
+ # Configure the logger
5
+ logger = logging.getLogger("PynmLogger")
6
+ logger.setLevel(logging.INFO)
7
+
8
+ # Create a file handler and set its level to DEBUG
9
+ file_handler = logging.FileHandler("logfile_pynm.log")
10
+ file_handler.setLevel(logging.INFO)
11
+
12
+ # console_handler = logging.StreamHandler()
13
+ # console_handler.setLevel(logging.DEBUG)
14
+
15
+ # Create a formatter and add it to the handler
16
+ formatter = logging.Formatter(
17
+ "%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(message)s"
18
+ )
19
+ file_handler.setFormatter(formatter)
20
+ # console_handler.setFormatter(formatter)
21
+
22
+ # Add the file handler to the logger
23
+ logger.addHandler(file_handler)
24
+ # logger.addHandler(console_handler)