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
@@ -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,46 +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
149
  sfreq=self.sfreq,
89
150
  )
90
- features = []
151
+
91
152
  sample_add = self.sfreq / self.run_analysis.sfreq_features
92
153
 
93
154
  offset_time = self.settings["segment_length_features_ms"]
94
- #offset_start = np.ceil(offset_time / 1000 * self.sfreq).astype(int)
155
+ # offset_start = np.ceil(offset_time / 1000 * self.sfreq).astype(int)
95
156
  offset_start = offset_time / 1000 * self.sfreq
96
157
 
97
- cnt_samples = offset_start
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
+ )
98
165
 
99
- while True:
100
- data_batch = next(generator, None)
101
- if data_batch is None:
102
- break
103
- feature_series = self.run_analysis.process(data_batch.astype(np.float64))
104
- feature_series = self._add_timestamp(feature_series, cnt_samples)
105
- 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
+ )
106
179
 
107
- if self.model is not None:
108
- prediction = self.model.predict(feature_series)
180
+ feature_series = self._add_target(
181
+ feature_series=feature_series, data=data_batch
182
+ )
109
183
 
110
- cnt_samples += sample_add
184
+ l_features.append(feature_series)
111
185
 
112
- feature_df = pd.DataFrame(features)
113
- feature_df = self._add_labels(features=feature_df, data=data)
186
+ cnt_samples += sample_add
187
+ feature_df = pd.DataFrame(l_features)
114
188
 
115
189
  self.save_after_stream(out_path_root, folder_name, feature_df)
116
190
 
117
191
  return feature_df
118
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
+
119
251
 
120
252
  class Stream(_OfflineStream):
121
253
  def __init__(
@@ -129,7 +261,7 @@ class Stream(_OfflineStream):
129
261
  path_grids: _PathLike | None = None,
130
262
  coord_names: list | None = None,
131
263
  coord_list: list | None = None,
132
- verbose: bool = True,
264
+ verbose: bool = True,
133
265
  ) -> None:
134
266
  """Stream initialization
135
267
 
@@ -154,11 +286,13 @@ class Stream(_OfflineStream):
154
286
  coord_list : list | None, optional
155
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
156
288
  verbose : bool, optional
157
- print out stream computation time information, by default True
289
+ log stream computation time information, by default True
158
290
  """
159
291
 
160
292
  if nm_channels is None and data is not None:
161
- 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
+ )
162
296
 
163
297
  if nm_channels is None and data is None:
164
298
  raise ValueError(
@@ -179,11 +313,15 @@ class Stream(_OfflineStream):
179
313
 
180
314
  self.data = data
181
315
 
316
+ self.target_idx_initialized = False
317
+
182
318
  def run(
183
319
  self,
184
320
  data: np.ndarray | pd.DataFrame = None,
185
321
  out_path_root: _PathLike | None = None,
186
322
  folder_name: str = "sub",
323
+ parallel: bool = False,
324
+ n_jobs: int = -2,
187
325
  ) -> pd.DataFrame:
188
326
  """Call run function for offline stream.
189
327
 
@@ -210,8 +348,11 @@ class Stream(_OfflineStream):
210
348
  elif self.data is not None:
211
349
  data = self._handle_data(self.data)
212
350
  elif self.data is None and data is None:
213
- raise ValueError(
214
- "No data passed to run function."
215
- )
351
+ raise ValueError("No data passed to run function.")
352
+
353
+ if parallel is True:
354
+ self._check_settings_for_parallel()
216
355
 
217
- 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)
@@ -1,142 +1,182 @@
1
- Metadata-Version: 2.1
2
- Name: py_neuromodulation
3
- Version: 0.0.2
4
- Summary: Real-time analysis of intracranial neurophysiology recordings.
5
- Keywords: real-time,eeg,ieeg,dbs,ecog,electrocorticography,deep-brain-stimulation,machine-learning
6
- Author-email: Timon Merk <timon.merk@charite.de>
7
- Maintainer: Timon Merk
8
- Requires-Python: >=3.10
9
- Description-Content-Type: text/x-rst
10
- Classifier: Development Status :: 2 - Pre-Alpha
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Programming Language :: Python
13
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
- Requires-Dist: mne
15
- Requires-Dist: filterpy >= 1.4.5
16
- Requires-Dist: fooof
17
- Requires-Dist: imbalanced-learn
18
- Requires-Dist: matplotlib >= 3.3.4
19
- Requires-Dist: mne-bids >= 0.8
20
- Requires-Dist: mne-connectivity
21
- Requires-Dist: mrmr-selection
22
- Requires-Dist: nolds
23
- Requires-Dist: numpy >= 1.21.2
24
- Requires-Dist: pandas >= 1.2.2
25
- Requires-Dist: pip
26
- Requires-Dist: pynput
27
- Requires-Dist: pybids
28
- Requires-Dist: scikit-image
29
- Requires-Dist: scikit-learn >= 0.24.2
30
- Requires-Dist: scikit-optimize
31
- Requires-Dist: scipy >= 1.7.1
32
- Requires-Dist: seaborn >= 0.11
33
- Requires-Dist: notebook
34
- Requires-Dist: ipython
35
- Requires-Dist: pybispectra
36
- Requires-Dist: black ; extra == "dev"
37
- Requires-Dist: pytest ; extra == "dev"
38
- Requires-Dist: pytest-cov ; extra == "dev"
39
- Project-URL: bugtracker, https://github.com/neuromodulation/py_neuromodulation/issues
40
- Project-URL: repository, https://github.com/neuromodulation/py_neuromodulation
41
- Provides-Extra: dev
42
-
43
- py_neuromodulation
44
- ==================
45
-
46
- Analyzing neural data can be a troublesome, trial and error prone,
47
- and beginner unfriendly process. *py_neuromodulation* allows using a simple
48
- interface for extraction of established neurophysiological features and includes commonly applied pre -and postprocessing methods.
49
-
50
- Only **time series data** with a corresponding **sampling frequency** are required for feature extraction.
51
-
52
- The output will be a `pandas.DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html>`_ including different time-resolved computed features. Internally a **stream** get's initialized,
53
- which resembles an *online* data-stream that can in theory also be be used with a hardware acquisition system.
54
-
55
- The following features are currently included:
56
-
57
- * oscillatory: fft, stft or bandpass filtered band power
58
- * `temporal waveform shape <https://www.sciencedirect.com/science/article/pii/S1364661316302182>`_
59
- * `fooof <https://fooof-tools.github.io/fooof/>`_
60
- * `mne_connectivity estimates <https://mne.tools/mne-connectivity/stable/index.html>`_
61
- * `Hjorth parameter <https://en.wikipedia.org/wiki/Hjorth_parameters>`_
62
- * `non-linear dynamical estimates <https://nolds.readthedocs.io/en/latest/>`_
63
- * various burst features
64
- * line length
65
- * and more...
66
-
67
-
68
- Find here the preprint of **py_neuromodulation** called *"Invasive neurophysiology and whole brain connectomics for neural decoding in patients with brain implants"* [1]_.
69
-
70
- The original intention for writing this toolbox was movement decoding from invasive brain signals [2]_.
71
- The application however could be any neural decoding problem.
72
- *py_neuromodulation* offers wrappers around common practice machine learning methods for efficient analysis.
73
-
74
- Find the documentation here http://py-neuromodulation.readthedocs.io for example usage and parametrization.
75
-
76
- Installation
77
- ============
78
-
79
- py_neuromodulation requires at least python 3.10. For installation you can use pip:
80
-
81
- .. code-block::
82
-
83
- pip install py-neuromodulation
84
-
85
- We recommend however installing the package in a new new conda environment:
86
-
87
- .. code-block::
88
-
89
- git clone https://github.com/neuromodulation/py_neuromodulation.git
90
- conda create -n pynm-test python=3.10
91
- conda activate pynm-test
92
-
93
- Then install the packages listed in the `pyproject.toml`:
94
-
95
- .. code-block::
96
-
97
- pip install .
98
-
99
-
100
- Optionally the ipython kernel can be specified for the installed pynm-test conda environment:
101
-
102
- .. code-block::
103
-
104
- ipython kernel install --user --name=pynm-test
105
-
106
- Then *py_neuromodulation* can be imported via:
107
-
108
- .. code-block::
109
-
110
- import py_neuromodulation as py_nm
111
-
112
- Basic Usage
113
- ===========
114
-
115
- .. code-block:: python
116
-
117
- import py_neuromodulation as pn
118
- import numpy as np
119
-
120
- NUM_CHANNELS = 5
121
- NUM_DATA = 10000
122
- sfreq = 1000 # Hz
123
- feature_freq = 3 # Hz
124
-
125
- data = np.random.random([NUM_CHANNELS, NUM_DATA])
126
-
127
- stream = pn.Stream(sfreq=sfreq, data=data, sampling_rate_features_hz=sampling_rate_features_hz)
128
- features = stream.run()
129
-
130
- Check the `Usage <https://py-neuromodulation.readthedocs.io/en/latest/usage.html>`_ and `First examples <https://py-neuromodulation.readthedocs.io/en/latest/auto_examples/plot_first_demo.html>`_ for further introduction.
131
-
132
- Contact information
133
- -------------------
134
- For any question or suggestion please find my contact
135
- information at `my GitHub profile <https://github.com/timonmerk>`_.
136
-
137
- References
138
- ----------
139
-
140
- .. [1] Merk, T. et al. *Invasive neurophysiology and whole brain connectomics for neural decoding in patients with brain implants*, `https://doi.org/10.21203/rs.3.rs-3212709/v1` (2023).
141
- .. [2] Merk, T. et al. *Electrocorticography is superior to subthalamic local field potentials for movement decoding in Parkinson’s disease*. Elife 11, e75126, `https://doi.org/10.7554/eLife.75126` (2022).
142
-
1
+ Metadata-Version: 2.1
2
+ Name: py_neuromodulation
3
+ Version: 0.0.3
4
+ Summary: Real-time analysis of intracranial neurophysiology recordings.
5
+ Author-email: Timon Merk <timon.merk@charite.de>
6
+ Maintainer: Timon Merk
7
+ License: MIT License
8
+
9
+ Copyright (c) 2021 Interventional Cognitive Neuromodulation - Neumann Lab Berlin
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: bugtracker, https://github.com/neuromodulation/py_neuromodulation/issues
30
+ Project-URL: repository, https://github.com/neuromodulation/py_neuromodulation
31
+ Keywords: real-time,eeg,ieeg,dbs,ecog,electrocorticography,deep-brain-stimulation,machine-learning
32
+ Classifier: Development Status :: 2 - Pre-Alpha
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python
35
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
36
+ Requires-Python: >=3.10
37
+ Description-Content-Type: text/x-rst
38
+ License-File: LICENSE
39
+ Requires-Dist: mne
40
+ Requires-Dist: filterpy >=1.4.5
41
+ Requires-Dist: fooof
42
+ Requires-Dist: imbalanced-learn
43
+ Requires-Dist: matplotlib >=3.3.4
44
+ Requires-Dist: mne-bids >=0.8
45
+ Requires-Dist: mne-connectivity
46
+ Requires-Dist: mrmr-selection
47
+ Requires-Dist: nolds
48
+ Requires-Dist: numpy >=1.21.2
49
+ Requires-Dist: pandas >=1.2.2
50
+ Requires-Dist: pip
51
+ Requires-Dist: pynput
52
+ Requires-Dist: pybids
53
+ Requires-Dist: scikit-image
54
+ Requires-Dist: scikit-learn >=0.24.2
55
+ Requires-Dist: scikit-optimize
56
+ Requires-Dist: scipy >=1.7.1
57
+ Requires-Dist: seaborn >=0.11
58
+ Requires-Dist: notebook
59
+ Requires-Dist: ipython
60
+ Requires-Dist: pybispectra >=1.0.0
61
+ Requires-Dist: pyparrm
62
+ Requires-Dist: pyarrow >=14.0.2
63
+ Requires-Dist: joblib >=1.3.2
64
+ Requires-Dist: black >=24.2.0
65
+ Requires-Dist: pytest >=8.0.2
66
+ Requires-Dist: wget
67
+ Provides-Extra: dev
68
+ Requires-Dist: black ; extra == 'dev'
69
+ Requires-Dist: pytest ; extra == 'dev'
70
+ Requires-Dist: pytest-cov ; extra == 'dev'
71
+
72
+ py_neuromodulation
73
+ ==================
74
+
75
+ Analyzing neural data can be a troublesome, trial and error prone,
76
+ and beginner unfriendly process. *py_neuromodulation* allows using a simple
77
+ interface for extraction of established neurophysiological features and includes commonly applied pre -and postprocessing methods.
78
+
79
+ Only **time series data** with a corresponding **sampling frequency** are required for feature extraction.
80
+
81
+ The output will be a `pandas.DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html>`_ including different time-resolved computed features. Internally a **stream** get's initialized,
82
+ which resembles an *online* data-stream that can in theory also be be used with a hardware acquisition system.
83
+
84
+ The following features are currently included:
85
+
86
+ * oscillatory: fft, stft or bandpass filtered band power
87
+ * `temporal waveform shape <https://www.sciencedirect.com/science/article/pii/S1364661316302182>`_
88
+ * `fooof <https://fooof-tools.github.io/fooof/>`_
89
+ * `mne_connectivity estimates <https://mne.tools/mne-connectivity/stable/index.html>`_
90
+ * `Hjorth parameter <https://en.wikipedia.org/wiki/Hjorth_parameters>`_
91
+ * `non-linear dynamical estimates <https://nolds.readthedocs.io/en/latest/>`_
92
+ * various burst features
93
+ * line length
94
+ * and more...
95
+
96
+
97
+ Find here the preprint of **py_neuromodulation** called *"Invasive neurophysiology and whole brain connectomics for neural decoding in patients with brain implants"* [1]_.
98
+
99
+ The original intention for writing this toolbox was movement decoding from invasive brain signals [2]_.
100
+ The application however could be any neural decoding problem.
101
+ *py_neuromodulation* offers wrappers around common practice machine learning methods for efficient analysis.
102
+
103
+ Find the documentation here http://py-neuromodulation.readthedocs.io for example usage and parametrization.
104
+
105
+ Installation
106
+ ============
107
+
108
+ py_neuromodulation requires at least python 3.10. For installation you can use pip:
109
+
110
+ .. code-block::
111
+
112
+ pip install py-neuromodulation
113
+
114
+
115
+ We recommend however installing the package using `rye <https://rye-up.com/guide/installation/>`_:
116
+
117
+ .. code-block::
118
+
119
+ git clone https://github.com/neuromodulation/py_neuromodulation.git
120
+ rye pin 3.11
121
+ rye sync
122
+
123
+ And then activating the virtual environment e.g. in Windows using:
124
+
125
+ .. code-block::
126
+
127
+ .\.venv\Scripts\activate
128
+
129
+ Alternatively you can also install the package in a conda environment:
130
+
131
+ conda create -n pynm-test python=3.11
132
+ conda activate pynm-test
133
+
134
+ Then install the packages listed in the `pyproject.toml`:
135
+
136
+ .. code-block::
137
+
138
+ pip install .
139
+
140
+
141
+ Optionally the ipython kernel can be specified for the installed pynm-test conda environment:
142
+
143
+ .. code-block::
144
+
145
+ ipython kernel install --user --name=pynm-test
146
+
147
+ Then *py_neuromodulation* can be imported via:
148
+
149
+ .. code-block::
150
+
151
+ import py_neuromodulation as nm
152
+
153
+ Basic Usage
154
+ ===========
155
+
156
+ .. code-block:: python
157
+
158
+ import py_neuromodulation as nm
159
+ import numpy as np
160
+
161
+ NUM_CHANNELS = 5
162
+ NUM_DATA = 10000
163
+ sfreq = 1000 # Hz
164
+ sampling_rate_features_hz = 3 # Hz
165
+
166
+ data = np.random.random([NUM_CHANNELS, NUM_DATA])
167
+
168
+ stream = nm.Stream(sfreq=sfreq, data=data, sampling_rate_features_hz=sampling_rate_features_hz)
169
+ features = stream.run()
170
+
171
+ Check the `Usage <https://py-neuromodulation.readthedocs.io/en/latest/usage.html>`_ and `First examples <https://py-neuromodulation.readthedocs.io/en/latest/auto_examples/plot_0_first_demo.html>`_ for further introduction.
172
+
173
+ Contact information
174
+ -------------------
175
+ For any question or suggestion please find my contact
176
+ information at `my GitHub profile <https://github.com/timonmerk>`_.
177
+
178
+ References
179
+ ----------
180
+
181
+ .. [1] Merk, T. et al. *Invasive neurophysiology and whole brain connectomics for neural decoding in patients with brain implants*, `https://doi.org/10.21203/rs.3.rs-3212709/v1` (2023).
182
+ .. [2] Merk, T. et al. *Electrocorticography is superior to subthalamic local field potentials for movement decoding in Parkinson’s disease*. Elife 11, e75126, `https://doi.org/10.7554/eLife.75126` (2022).