py-neuromodulation 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. docs/build/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
  2. docs/build/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +233 -0
  3. docs/build/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  4. docs/build/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
  5. docs/build/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  6. docs/build/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
  7. docs/build/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  8. docs/build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +68 -0
  9. docs/build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +239 -0
  10. docs/build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  11. docs/build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +97 -0
  12. docs/build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  13. docs/build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +192 -0
  14. docs/build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  15. docs/source/_build/html/_downloads/09df217f95985497f45d69e2d4bdc5b1/plot_2_example_add_feature.py +76 -0
  16. docs/source/_build/html/_downloads/0d0d0a76e8f648d5d3cbc47da6351932/plot_real_time_demo.py +97 -0
  17. docs/source/_build/html/_downloads/3b4900a2b2818ff30362215b76f7d5eb/plot_1_example_BIDS.py +240 -0
  18. docs/source/_build/html/_downloads/5d73cadc59a8805c47e3b84063afc157/plot_example_BIDS.py +233 -0
  19. docs/source/_build/html/_downloads/7660317fa5a6bfbd12fcca9961457fc4/plot_example_rmap_computing.py +63 -0
  20. docs/source/_build/html/_downloads/7e92dd2e6cc86b239d14cafad972ae4f/plot_3_example_sharpwave_analysis.py +219 -0
  21. docs/source/_build/html/_downloads/839e5b319379f7fd9e867deb00fd797f/plot_example_gridPointProjection.py +210 -0
  22. docs/source/_build/html/_downloads/ae8be19afe5e559f011fc9b138968ba0/plot_first_demo.py +192 -0
  23. docs/source/_build/html/_downloads/b8b06cacc17969d3725a0b6f1d7741c5/plot_example_sharpwave_analysis.py +219 -0
  24. docs/source/_build/html/_downloads/c2db0bf2b334d541b00662b991682256/plot_6_real_time_demo.py +121 -0
  25. docs/source/_build/html/_downloads/c31a86c0b68cb4167d968091ace8080d/plot_example_add_feature.py +68 -0
  26. docs/source/_build/html/_downloads/ce3914826f782cbd1ea8fd024eaf0ac3/plot_5_example_rmap_computing.py +64 -0
  27. docs/source/_build/html/_downloads/da36848a41e6a3235d91fb7cfb6d59b4/plot_0_first_demo.py +189 -0
  28. docs/source/_build/html/_downloads/eaa4305c75b19a1e2eea941f742a6331/plot_4_example_gridPointProjection.py +210 -0
  29. docs/source/auto_examples/plot_0_first_demo.py +189 -0
  30. docs/source/auto_examples/plot_1_example_BIDS.py +240 -0
  31. docs/source/auto_examples/plot_2_example_add_feature.py +76 -0
  32. docs/source/auto_examples/plot_3_example_sharpwave_analysis.py +219 -0
  33. docs/source/auto_examples/plot_4_example_gridPointProjection.py +210 -0
  34. docs/source/auto_examples/plot_5_example_rmap_computing.py +64 -0
  35. docs/source/auto_examples/plot_6_real_time_demo.py +121 -0
  36. docs/source/conf.py +105 -0
  37. examples/plot_0_first_demo.py +189 -0
  38. examples/plot_1_example_BIDS.py +240 -0
  39. examples/plot_2_example_add_feature.py +76 -0
  40. examples/plot_3_example_sharpwave_analysis.py +219 -0
  41. examples/plot_4_example_gridPointProjection.py +210 -0
  42. examples/plot_5_example_rmap_computing.py +64 -0
  43. examples/plot_6_real_time_demo.py +121 -0
  44. packages/realtime_decoding/build/lib/realtime_decoding/__init__.py +4 -0
  45. packages/realtime_decoding/build/lib/realtime_decoding/decoder.py +104 -0
  46. packages/realtime_decoding/build/lib/realtime_decoding/features.py +163 -0
  47. packages/realtime_decoding/build/lib/realtime_decoding/helpers.py +15 -0
  48. packages/realtime_decoding/build/lib/realtime_decoding/run_decoding.py +345 -0
  49. packages/realtime_decoding/build/lib/realtime_decoding/trainer.py +54 -0
  50. packages/tmsi/build/lib/TMSiFileFormats/__init__.py +37 -0
  51. packages/tmsi/build/lib/TMSiFileFormats/file_formats/__init__.py +36 -0
  52. packages/tmsi/build/lib/TMSiFileFormats/file_formats/lsl_stream_writer.py +200 -0
  53. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_file_writer.py +496 -0
  54. packages/tmsi/build/lib/TMSiFileFormats/file_formats/poly5_to_edf_converter.py +236 -0
  55. packages/tmsi/build/lib/TMSiFileFormats/file_formats/xdf_file_writer.py +977 -0
  56. packages/tmsi/build/lib/TMSiFileFormats/file_readers/__init__.py +35 -0
  57. packages/tmsi/build/lib/TMSiFileFormats/file_readers/edf_reader.py +116 -0
  58. packages/tmsi/build/lib/TMSiFileFormats/file_readers/poly5reader.py +294 -0
  59. packages/tmsi/build/lib/TMSiFileFormats/file_readers/xdf_reader.py +229 -0
  60. packages/tmsi/build/lib/TMSiFileFormats/file_writer.py +102 -0
  61. packages/tmsi/build/lib/TMSiPlotters/__init__.py +2 -0
  62. packages/tmsi/build/lib/TMSiPlotters/gui/__init__.py +39 -0
  63. packages/tmsi/build/lib/TMSiPlotters/gui/_plotter_gui.py +234 -0
  64. packages/tmsi/build/lib/TMSiPlotters/gui/plotting_gui.py +440 -0
  65. packages/tmsi/build/lib/TMSiPlotters/plotters/__init__.py +44 -0
  66. packages/tmsi/build/lib/TMSiPlotters/plotters/hd_emg_plotter.py +446 -0
  67. packages/tmsi/build/lib/TMSiPlotters/plotters/impedance_plotter.py +589 -0
  68. packages/tmsi/build/lib/TMSiPlotters/plotters/signal_plotter.py +1326 -0
  69. packages/tmsi/build/lib/TMSiSDK/__init__.py +54 -0
  70. packages/tmsi/build/lib/TMSiSDK/device.py +588 -0
  71. packages/tmsi/build/lib/TMSiSDK/devices/__init__.py +34 -0
  72. packages/tmsi/build/lib/TMSiSDK/devices/saga/TMSi_Device_API.py +1764 -0
  73. packages/tmsi/build/lib/TMSiSDK/devices/saga/__init__.py +34 -0
  74. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_device.py +1366 -0
  75. packages/tmsi/build/lib/TMSiSDK/devices/saga/saga_types.py +520 -0
  76. packages/tmsi/build/lib/TMSiSDK/devices/saga/xml_saga_config.py +165 -0
  77. packages/tmsi/build/lib/TMSiSDK/error.py +95 -0
  78. packages/tmsi/build/lib/TMSiSDK/sample_data.py +63 -0
  79. packages/tmsi/build/lib/TMSiSDK/sample_data_server.py +99 -0
  80. packages/tmsi/build/lib/TMSiSDK/settings.py +45 -0
  81. packages/tmsi/build/lib/TMSiSDK/tmsi_device.py +111 -0
  82. packages/tmsi/build/lib/__init__.py +4 -0
  83. packages/tmsi/build/lib/apex_sdk/__init__.py +34 -0
  84. packages/tmsi/build/lib/apex_sdk/device/__init__.py +41 -0
  85. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API.py +1009 -0
  86. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_enums.py +239 -0
  87. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_API_structures.py +668 -0
  88. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_device.py +1611 -0
  89. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_dongle.py +38 -0
  90. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_event_reader.py +57 -0
  91. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_channel.py +44 -0
  92. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_config.py +150 -0
  93. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_const.py +36 -0
  94. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_impedance_channel.py +48 -0
  95. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/apex_info.py +108 -0
  96. packages/tmsi/build/lib/apex_sdk/device/devices/apex/apex_structures/dongle_info.py +39 -0
  97. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/download_measurement.py +77 -0
  98. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/eeg_measurement.py +150 -0
  99. packages/tmsi/build/lib/apex_sdk/device/devices/apex/measurements/impedance_measurement.py +129 -0
  100. packages/tmsi/build/lib/apex_sdk/device/threads/conversion_thread.py +59 -0
  101. packages/tmsi/build/lib/apex_sdk/device/threads/sampling_thread.py +57 -0
  102. packages/tmsi/build/lib/apex_sdk/device/tmsi_channel.py +83 -0
  103. packages/tmsi/build/lib/apex_sdk/device/tmsi_device.py +201 -0
  104. packages/tmsi/build/lib/apex_sdk/device/tmsi_device_enums.py +103 -0
  105. packages/tmsi/build/lib/apex_sdk/device/tmsi_dongle.py +43 -0
  106. packages/tmsi/build/lib/apex_sdk/device/tmsi_event_reader.py +50 -0
  107. packages/tmsi/build/lib/apex_sdk/device/tmsi_measurement.py +118 -0
  108. packages/tmsi/build/lib/apex_sdk/sample_data_server/__init__.py +33 -0
  109. packages/tmsi/build/lib/apex_sdk/sample_data_server/event_data.py +44 -0
  110. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data.py +50 -0
  111. packages/tmsi/build/lib/apex_sdk/sample_data_server/sample_data_server.py +136 -0
  112. packages/tmsi/build/lib/apex_sdk/tmsi_errors/error.py +126 -0
  113. packages/tmsi/build/lib/apex_sdk/tmsi_sdk.py +113 -0
  114. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/apex/apex_structure_generator.py +134 -0
  115. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/decorators.py +60 -0
  116. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/logger_filter.py +42 -0
  117. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/singleton.py +42 -0
  118. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/support_functions.py +72 -0
  119. packages/tmsi/build/lib/apex_sdk/tmsi_utilities/tmsi_logger.py +98 -0
  120. py_neuromodulation/{helper.py → _write_example_dataset_helper.py} +1 -1
  121. py_neuromodulation/nm_EpochStream.py +2 -3
  122. py_neuromodulation/nm_IO.py +43 -70
  123. py_neuromodulation/nm_RMAP.py +308 -11
  124. py_neuromodulation/nm_analysis.py +1 -1
  125. py_neuromodulation/nm_artifacts.py +25 -0
  126. py_neuromodulation/nm_bispectra.py +64 -29
  127. py_neuromodulation/nm_bursts.py +44 -30
  128. py_neuromodulation/nm_coherence.py +2 -1
  129. py_neuromodulation/nm_features.py +4 -2
  130. py_neuromodulation/nm_filter.py +63 -32
  131. py_neuromodulation/nm_filter_preprocessing.py +91 -0
  132. py_neuromodulation/nm_fooof.py +47 -29
  133. py_neuromodulation/nm_mne_connectivity.py +1 -1
  134. py_neuromodulation/nm_normalization.py +50 -74
  135. py_neuromodulation/nm_oscillatory.py +151 -31
  136. py_neuromodulation/nm_plots.py +13 -10
  137. py_neuromodulation/nm_rereference.py +10 -8
  138. py_neuromodulation/nm_run_analysis.py +28 -13
  139. py_neuromodulation/nm_sharpwaves.py +103 -136
  140. py_neuromodulation/nm_stats.py +44 -30
  141. py_neuromodulation/nm_stream_abc.py +18 -10
  142. py_neuromodulation/nm_stream_offline.py +181 -40
  143. py_neuromodulation/utils/_logging.py +24 -0
  144. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/METADATA +182 -142
  145. py_neuromodulation-0.0.3.dist-info/RECORD +188 -0
  146. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/WHEEL +2 -1
  147. py_neuromodulation-0.0.3.dist-info/top_level.txt +5 -0
  148. tests/__init__.py +0 -0
  149. tests/conftest.py +117 -0
  150. tests/test_all_examples.py +10 -0
  151. tests/test_all_features.py +63 -0
  152. tests/test_bispectra.py +70 -0
  153. tests/test_bursts.py +105 -0
  154. tests/test_feature_sampling_rates.py +143 -0
  155. tests/test_fooof.py +16 -0
  156. tests/test_initalization_offline_stream.py +41 -0
  157. tests/test_multiprocessing.py +58 -0
  158. tests/test_nan_values.py +29 -0
  159. tests/test_nm_filter.py +95 -0
  160. tests/test_nm_resample.py +63 -0
  161. tests/test_normalization_settings.py +146 -0
  162. tests/test_notch_filter.py +31 -0
  163. tests/test_osc_features.py +424 -0
  164. tests/test_preprocessing_filter.py +151 -0
  165. tests/test_rereference.py +171 -0
  166. tests/test_sampling.py +57 -0
  167. tests/test_settings_change_after_init.py +76 -0
  168. tests/test_sharpwave.py +165 -0
  169. tests/test_target_channel_add.py +100 -0
  170. tests/test_timing.py +80 -0
  171. py_neuromodulation/data/README +0 -6
  172. py_neuromodulation/data/dataset_description.json +0 -8
  173. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/MOV_aligned_features_ch_ECOG_RIGHT_0_all.png +0 -0
  174. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/all_feature_plt.pdf +0 -0
  175. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_FEATURES.csv +0 -182
  176. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_LM_ML_RES.p +0 -0
  177. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SETTINGS.json +0 -273
  178. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_SIDECAR.json +0 -6
  179. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_decoding_performance.png +0 -0
  180. py_neuromodulation/data/derivatives/sub-testsub_ses-EphysMedOff_task-gripforce_run-0/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_nm_channels.csv +0 -11
  181. py_neuromodulation/data/participants.json +0 -32
  182. py_neuromodulation/data/participants.tsv +0 -2
  183. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_coordsystem.json +0 -5
  184. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_space-mni_electrodes.tsv +0 -11
  185. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_channels.tsv +0 -11
  186. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.eeg +0 -0
  187. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.json +0 -18
  188. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr +0 -35
  189. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/ieeg/sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vmrk +0 -13
  190. py_neuromodulation/data/sub-testsub/ses-EphysMedOff/sub-testsub_ses-EphysMedOff_scans.tsv +0 -2
  191. py_neuromodulation/grid_cortex.tsv +0 -40
  192. py_neuromodulation/grid_subcortex.tsv +0 -1429
  193. py_neuromodulation/nm_settings.json +0 -290
  194. py_neuromodulation/plots/STN_surf.mat +0 -0
  195. py_neuromodulation/plots/Vertices.mat +0 -0
  196. py_neuromodulation/plots/faces.mat +0 -0
  197. py_neuromodulation/plots/grid.mat +0 -0
  198. py_neuromodulation/py_neuromodulation.egg-info/PKG-INFO +0 -104
  199. py_neuromodulation/py_neuromodulation.egg-info/dependency_links.txt +0 -1
  200. py_neuromodulation/py_neuromodulation.egg-info/requires.txt +0 -26
  201. py_neuromodulation/py_neuromodulation.egg-info/top_level.txt +0 -1
  202. py_neuromodulation-0.0.2.dist-info/RECORD +0 -73
  203. /py_neuromodulation/{py_neuromodulation.egg-info/SOURCES.txt → utils/__init__.py} +0 -0
  204. {py_neuromodulation-0.0.2.dist-info → py_neuromodulation-0.0.3.dist-info}/LICENSE +0 -0
@@ -4,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,
@@ -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: