py-neuromodulation 0.0.5__py3-none-any.whl → 0.0.7__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 (57) hide show
  1. py_neuromodulation/__init__.py +16 -10
  2. py_neuromodulation/{nm_RMAP.py → analysis/RMAP.py} +2 -2
  3. py_neuromodulation/analysis/__init__.py +4 -0
  4. py_neuromodulation/{nm_decode.py → analysis/decode.py} +4 -4
  5. py_neuromodulation/{nm_analysis.py → analysis/feature_reader.py} +21 -20
  6. py_neuromodulation/{nm_plots.py → analysis/plots.py} +54 -12
  7. py_neuromodulation/{nm_stats.py → analysis/stats.py} +2 -8
  8. py_neuromodulation/{nm_settings.yaml → default_settings.yaml} +7 -9
  9. py_neuromodulation/features/__init__.py +31 -0
  10. py_neuromodulation/features/bandpower.py +165 -0
  11. py_neuromodulation/{nm_bispectra.py → features/bispectra.py} +11 -12
  12. py_neuromodulation/{nm_bursts.py → features/bursts.py} +14 -9
  13. py_neuromodulation/{nm_coherence.py → features/coherence.py} +28 -19
  14. py_neuromodulation/{nm_features.py → features/feature_processor.py} +30 -53
  15. py_neuromodulation/{nm_fooof.py → features/fooof.py} +11 -8
  16. py_neuromodulation/{nm_hjorth_raw.py → features/hjorth_raw.py} +10 -5
  17. py_neuromodulation/{nm_linelength.py → features/linelength.py} +1 -1
  18. py_neuromodulation/{nm_mne_connectivity.py → features/mne_connectivity.py} +5 -6
  19. py_neuromodulation/{nm_nolds.py → features/nolds.py} +5 -7
  20. py_neuromodulation/{nm_oscillatory.py → features/oscillatory.py} +7 -181
  21. py_neuromodulation/{nm_sharpwaves.py → features/sharpwaves.py} +13 -4
  22. py_neuromodulation/filter/__init__.py +3 -0
  23. py_neuromodulation/{nm_kalmanfilter.py → filter/kalman_filter.py} +67 -71
  24. py_neuromodulation/filter/kalman_filter_external.py +1890 -0
  25. py_neuromodulation/{nm_filter.py → filter/mne_filter.py} +128 -219
  26. py_neuromodulation/filter/notch_filter.py +93 -0
  27. py_neuromodulation/processing/__init__.py +10 -0
  28. py_neuromodulation/{nm_artifacts.py → processing/artifacts.py} +2 -3
  29. py_neuromodulation/{nm_preprocessing.py → processing/data_preprocessor.py} +19 -25
  30. py_neuromodulation/{nm_filter_preprocessing.py → processing/filter_preprocessing.py} +3 -4
  31. py_neuromodulation/{nm_normalization.py → processing/normalization.py} +9 -7
  32. py_neuromodulation/{nm_projection.py → processing/projection.py} +14 -14
  33. py_neuromodulation/{nm_rereference.py → processing/rereference.py} +13 -13
  34. py_neuromodulation/{nm_resample.py → processing/resample.py} +1 -4
  35. py_neuromodulation/stream/__init__.py +3 -0
  36. py_neuromodulation/{nm_run_analysis.py → stream/data_processor.py} +42 -42
  37. py_neuromodulation/stream/generator.py +53 -0
  38. py_neuromodulation/{nm_mnelsl_generator.py → stream/mnelsl_player.py} +10 -6
  39. py_neuromodulation/{nm_mnelsl_stream.py → stream/mnelsl_stream.py} +13 -9
  40. py_neuromodulation/{nm_settings.py → stream/settings.py} +27 -24
  41. py_neuromodulation/{nm_stream.py → stream/stream.py} +217 -188
  42. py_neuromodulation/utils/__init__.py +2 -0
  43. py_neuromodulation/{nm_define_nmchannels.py → utils/channels.py} +14 -9
  44. py_neuromodulation/{nm_database.py → utils/database.py} +2 -2
  45. py_neuromodulation/{nm_IO.py → utils/io.py} +42 -77
  46. py_neuromodulation/utils/keyboard.py +52 -0
  47. py_neuromodulation/{nm_logger.py → utils/logging.py} +3 -3
  48. py_neuromodulation/{nm_types.py → utils/types.py} +72 -14
  49. {py_neuromodulation-0.0.5.dist-info → py_neuromodulation-0.0.7.dist-info}/METADATA +12 -29
  50. py_neuromodulation-0.0.7.dist-info/RECORD +89 -0
  51. py_neuromodulation/FieldTrip.py +0 -589
  52. py_neuromodulation/_write_example_dataset_helper.py +0 -83
  53. py_neuromodulation/nm_generator.py +0 -45
  54. py_neuromodulation/nm_stream_abc.py +0 -166
  55. py_neuromodulation-0.0.5.dist-info/RECORD +0 -83
  56. {py_neuromodulation-0.0.5.dist-info → py_neuromodulation-0.0.7.dist-info}/WHEEL +0 -0
  57. {py_neuromodulation-0.0.5.dist-info → py_neuromodulation-0.0.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,18 @@
1
1
  from collections.abc import Iterable
2
2
  from pydantic import field_validator
3
- from py_neuromodulation.nm_types import NMBaseModel
4
3
  from typing import TYPE_CHECKING, Callable
5
4
 
6
5
  import numpy as np
7
6
 
8
- from py_neuromodulation.nm_features import NMFeature
9
- from py_neuromodulation.nm_types import BoolSelector, FrequencyRange
7
+ from py_neuromodulation.utils.types import (
8
+ NMBaseModel,
9
+ NMFeature,
10
+ BoolSelector,
11
+ FrequencyRange,
12
+ )
10
13
 
11
14
  if TYPE_CHECKING:
12
- from py_neuromodulation.nm_settings import NMSettings
15
+ from py_neuromodulation import NMSettings
13
16
 
14
17
 
15
18
  class BispectraComponents(BoolSelector):
@@ -67,7 +70,7 @@ class Bispectra(NMFeature):
67
70
  self.sfreq = sfreq
68
71
  self.ch_names = ch_names
69
72
  self.frequency_ranges_hz = settings.frequency_ranges_hz
70
- self.settings: BispectraSettings = settings.bispectrum
73
+ self.settings: BispectraSettings = settings.bispectrum_settings
71
74
 
72
75
  assert all(
73
76
  f_band_bispectrum in settings.frequency_ranges_hz
@@ -93,11 +96,8 @@ class Bispectra(NMFeature):
93
96
  def calc_feature(self, data: np.ndarray) -> dict:
94
97
  from pybispectra import compute_fft, WaveShape
95
98
 
96
- # PyBispectra's compute_fft uses PQDM to parallelize the calculation per channel
97
- # Is this necessary? Maybe the overhead of parallelization is not worth it
98
- # considering that we incur in it once per batch of data
99
99
  fft_coeffs, freqs = compute_fft(
100
- data=np.expand_dims(data, axis=(0)),
100
+ data=np.expand_dims(data, axis=0),
101
101
  sampling_freq=self.sfreq,
102
102
  n_points=data.shape[1],
103
103
  verbose=False,
@@ -124,12 +124,11 @@ class Bispectra(NMFeature):
124
124
  f1s=tuple(self.settings.f1s), # type: ignore
125
125
  f2s=tuple(self.settings.f2s), # type: ignore
126
126
  )
127
+ waveshape = waveshape.results.get_results(copy=False) # can overwrite obj with array
127
128
 
128
129
  feature_results = {}
129
130
  for ch_idx, ch_name in enumerate(self.ch_names):
130
- bispectrum = waveshape._bicoherence[
131
- ch_idx
132
- ] # Same as waveshape.results._data, skips a copy
131
+ bispectrum = waveshape[ch_idx]
133
132
 
134
133
  for component in self.settings.components.get_enabled():
135
134
  spectrum_ch = COMPONENT_DICT[component](bispectrum)
@@ -8,13 +8,13 @@ from collections.abc import Sequence
8
8
  from itertools import product
9
9
 
10
10
  from pydantic import Field, field_validator
11
- from py_neuromodulation.nm_types import BoolSelector, NMBaseModel
12
- from py_neuromodulation.nm_features import NMFeature
11
+ from py_neuromodulation.utils.types import BoolSelector, NMBaseModel, NMFeature
13
12
 
14
13
  from typing import TYPE_CHECKING, Callable
14
+ from py_neuromodulation.utils.types import create_validation_error
15
15
 
16
16
  if TYPE_CHECKING:
17
- from py_neuromodulation.nm_settings import NMSettings
17
+ from py_neuromodulation import NMSettings
18
18
 
19
19
 
20
20
  LARGE_NUM = 2**24
@@ -45,7 +45,7 @@ class BurstFeatures(BoolSelector):
45
45
  in_burst: bool = True
46
46
 
47
47
 
48
- class BurstSettings(NMBaseModel):
48
+ class BurstsSettings(NMBaseModel):
49
49
  threshold: float = Field(default=75, ge=0, le=100)
50
50
  time_duration_s: float = Field(default=30, ge=0)
51
51
  frequency_bands: list[str] = ["low_beta", "high_beta", "low_gamma"]
@@ -56,17 +56,22 @@ class BurstSettings(NMBaseModel):
56
56
  return [f.replace(" ", "_") for f in frequency_bands]
57
57
 
58
58
 
59
- class Burst(NMFeature):
59
+ class Bursts(NMFeature):
60
60
  def __init__(
61
61
  self, settings: "NMSettings", ch_names: Sequence[str], sfreq: float
62
62
  ) -> None:
63
63
  # Test settings
64
+ settings.validate()
65
+
66
+ # Validate that all frequency bands are defined in the settings
64
67
  for fband_burst in settings.burst_settings.frequency_bands:
65
- assert (
66
- fband_burst in list(settings.frequency_ranges_hz.keys())
67
- ), f"bursting {fband_burst} needs to be defined in settings['frequency_ranges_hz']"
68
+ if fband_burst not in list(settings.frequency_ranges_hz.keys()):
69
+ raise create_validation_error(
70
+ f"bursting {fband_burst} needs to be defined in settings['frequency_ranges_hz']",
71
+ loc=["burst_settings", "frequency_bands"],
72
+ )
68
73
 
69
- from py_neuromodulation.nm_filter import MNEFilter
74
+ from py_neuromodulation.filter import MNEFilter
70
75
 
71
76
  self.settings = settings.burst_settings
72
77
  self.sfreq = sfreq
@@ -5,12 +5,16 @@ from collections.abc import Iterable
5
5
  from typing import TYPE_CHECKING, Annotated
6
6
  from pydantic import Field, field_validator
7
7
 
8
- from py_neuromodulation.nm_features import NMFeature
9
- from py_neuromodulation.nm_types import BoolSelector, FrequencyRange, NMBaseModel
8
+ from py_neuromodulation.utils.types import (
9
+ NMFeature,
10
+ BoolSelector,
11
+ FrequencyRange,
12
+ NMBaseModel,
13
+ )
10
14
  from py_neuromodulation import logger
11
15
 
12
16
  if TYPE_CHECKING:
13
- from py_neuromodulation.nm_settings import NMSettings
17
+ from py_neuromodulation import NMSettings
14
18
 
15
19
 
16
20
  class CoherenceMethods(BoolSelector):
@@ -22,7 +26,7 @@ class CoherenceFeatures(BoolSelector):
22
26
  mean_fband: bool = True
23
27
  max_fband: bool = True
24
28
  max_allfbands: bool = True
25
-
29
+
26
30
 
27
31
  ListOfTwoStr = Annotated[list[str], Field(min_length=2, max_length=2)]
28
32
 
@@ -31,6 +35,7 @@ class CoherenceSettings(NMBaseModel):
31
35
  features: CoherenceFeatures = CoherenceFeatures()
32
36
  method: CoherenceMethods = CoherenceMethods()
33
37
  channels: list[ListOfTwoStr] = []
38
+ nperseg: int = Field(default=128, ge=0)
34
39
  frequency_bands: list[str] = Field(default=["high_beta"], min_length=1)
35
40
 
36
41
  @field_validator("frequency_bands")
@@ -45,6 +50,7 @@ class CoherenceObject:
45
50
  window: str,
46
51
  fbands: list[FrequencyRange],
47
52
  fband_names: list[str],
53
+ nperseg: int,
48
54
  ch_1_name: str,
49
55
  ch_2_name: str,
50
56
  ch_1_idx: int,
@@ -61,6 +67,7 @@ class CoherenceObject:
61
67
  self.ch_2 = ch_2_name
62
68
  self.ch_1_idx = ch_1_idx
63
69
  self.ch_2_idx = ch_2_idx
70
+ self.nperseg = nperseg
64
71
  self.coh = coh
65
72
  self.icoh = icoh
66
73
  self.features_coh = features_coh
@@ -75,14 +82,15 @@ class CoherenceObject:
75
82
  def get_coh(self, feature_results, x, y):
76
83
  from scipy.signal import welch, csd
77
84
 
78
- self.f, self.Pxx = welch(x, self.sfreq, self.window, nperseg=128)
79
- self.Pyy = welch(y, self.sfreq, self.window, nperseg=128)[1]
80
- self.Pxy = csd(x, y, self.sfreq, self.window, nperseg=128)[1]
85
+ self.f, self.Pxx = welch(x, self.sfreq, self.window, nperseg=self.nperseg)
86
+ self.Pyy = welch(y, self.sfreq, self.window, nperseg=self.nperseg)[1]
87
+ self.Pxy = csd(x, y, self.sfreq, self.window, nperseg=self.nperseg)[1]
81
88
 
82
89
  if self.coh:
83
- self.coh_val = np.abs(self.Pxy**2) / (self.Pxx * self.Pyy)
90
+ # XXX: gives different output to abs(Sxy) / sqrt(Sxx * Syy)
91
+ self.coh_val = np.abs(self.Pxy) ** 2 / (self.Pxx * self.Pyy)
84
92
  if self.icoh:
85
- self.icoh_val = np.array(self.Pxy / (self.Pxx * self.Pyy)).imag
93
+ self.icoh_val = self.Pxy.imag / np.sqrt(self.Pxx * self.Pyy)
86
94
 
87
95
  for coh_idx, coh_type in enumerate([self.coh, self.icoh]):
88
96
  if coh_type:
@@ -140,11 +148,11 @@ class CoherenceObject:
140
148
  return feature_results
141
149
 
142
150
 
143
- class NMCoherence(NMFeature):
151
+ class Coherence(NMFeature):
144
152
  def __init__(
145
153
  self, settings: "NMSettings", ch_names: list[str], sfreq: float
146
154
  ) -> None:
147
- self.settings = settings.coherence
155
+ self.settings = settings.coherence_settings
148
156
  self.frequency_ranges_hz = settings.frequency_ranges_hz
149
157
  self.sfreq = sfreq
150
158
  self.ch_names = ch_names
@@ -176,6 +184,7 @@ class NMCoherence(NMFeature):
176
184
  "hann",
177
185
  fband_specs,
178
186
  fband_names,
187
+ self.settings.nperseg,
179
188
  ch_1_name,
180
189
  ch_2_name,
181
190
  ch_1_idx,
@@ -193,7 +202,7 @@ class NMCoherence(NMFeature):
193
202
  sfreq: float,
194
203
  ):
195
204
  flat_channels = [
196
- ch for ch_pair in settings.coherence.channels for ch in ch_pair
205
+ ch for ch_pair in settings.coherence_settings.channels for ch in ch_pair
197
206
  ]
198
207
 
199
208
  valid_coh_channel = [
@@ -203,37 +212,37 @@ class NMCoherence(NMFeature):
203
212
  if valid_coh_channel[ch_idx] == 0:
204
213
  raise RuntimeError(
205
214
  f"Coherence selected channel {ch_coh} does not match any channel name: \n"
206
- f" - settings.coherence.channels: {settings.coherence.channels}\n"
215
+ f" - settings.coherence_settings.channels: {settings.coherence_settings.channels}\n"
207
216
  f" - ch_names: {ch_names} \n"
208
217
  )
209
218
 
210
219
  if valid_coh_channel[ch_idx] > 1:
211
220
  raise RuntimeError(
212
221
  f"Coherence selected channel {ch_coh} is ambigous and matches more than one channel name: \n"
213
- f" - settings.coherence.channels: {settings.coherence.channels}\n"
222
+ f" - settings.coherence_settings.channels: {settings.coherence_settings.channels}\n"
214
223
  f" - ch_names: {ch_names} \n"
215
224
  )
216
225
 
217
226
  assert all(
218
227
  f_band_coh in settings.frequency_ranges_hz
219
- for f_band_coh in settings.coherence.frequency_bands
228
+ for f_band_coh in settings.coherence_settings.frequency_bands
220
229
  ), (
221
230
  "coherence selected frequency bands don't match the ones"
222
231
  "specified in s['frequency_ranges_hz']"
223
- f"coherence frequency bands: {settings.coherence.frequency_bands}"
232
+ f"coherence frequency bands: {settings.coherence_settings.frequency_bands}"
224
233
  f"specified frequency_ranges_hz: {settings.frequency_ranges_hz}"
225
234
  )
226
235
 
227
236
  assert all(
228
237
  settings.frequency_ranges_hz[fb][0] < sfreq / 2
229
238
  and settings.frequency_ranges_hz[fb][1] < sfreq / 2
230
- for fb in settings.coherence.frequency_bands
239
+ for fb in settings.coherence_settings.frequency_bands
231
240
  ), (
232
241
  "the coherence frequency band ranges need to be smaller than the Nyquist frequency"
233
- f"got sfreq = {sfreq} and fband ranges {settings.coherence.frequency_bands}"
242
+ f"got sfreq = {sfreq} and fband ranges {settings.coherence_settings.frequency_bands}"
234
243
  )
235
244
 
236
- if not settings.coherence.method.get_enabled():
245
+ if not settings.coherence_settings.method.get_enabled():
237
246
  logger.warn(
238
247
  "feature coherence enabled, but no coherence['method'] selected"
239
248
  )
@@ -1,51 +1,27 @@
1
- from typing import Protocol, Type, runtime_checkable, TYPE_CHECKING
2
- from collections.abc import Sequence
3
- import numpy as np
1
+ from typing import Type, TYPE_CHECKING
4
2
 
5
- if TYPE_CHECKING:
6
- from nm_settings import NMSettings
7
-
8
- from py_neuromodulation.nm_types import ImportDetails, get_class, FeatureName
9
-
10
-
11
- @runtime_checkable
12
- class NMFeature(Protocol):
13
- def __init__(
14
- self, settings: "NMSettings", ch_names: Sequence[str], sfreq: int | float
15
- ) -> None: ...
3
+ from py_neuromodulation.utils.types import NMFeature, FeatureName
16
4
 
17
- def calc_feature(self, data: np.ndarray) -> dict:
18
- """
19
- Feature calculation method. Each method needs to loop through all channels
20
-
21
- Parameters
22
- ----------
23
- data : 'np.ndarray'
24
- (channels, time)
25
- feature_results : dict
26
-
27
- Returns
28
- -------
29
- dict
30
- """
31
- ...
32
-
33
-
34
- FEATURE_DICT: dict[FeatureName | str, ImportDetails] = {
35
- "raw_hjorth": ImportDetails("nm_hjorth_raw", "Hjorth"),
36
- "return_raw": ImportDetails("nm_hjorth_raw", "Raw"),
37
- "bandpass_filter": ImportDetails("nm_oscillatory", "BandPower"),
38
- "stft": ImportDetails("nm_oscillatory", "STFT"),
39
- "fft": ImportDetails("nm_oscillatory", "FFT"),
40
- "welch": ImportDetails("nm_oscillatory", "Welch"),
41
- "sharpwave_analysis": ImportDetails("nm_sharpwaves", "SharpwaveAnalyzer"),
42
- "fooof": ImportDetails("nm_fooof", "FooofAnalyzer"),
43
- "nolds": ImportDetails("nm_nolds", "Nolds"),
44
- "coherence": ImportDetails("nm_coherence", "NMCoherence"),
45
- "bursts": ImportDetails("nm_bursts", "Burst"),
46
- "linelength": ImportDetails("nm_linelength", "LineLength"),
47
- "mne_connectivity": ImportDetails("nm_mne_connectivity", "MNEConnectivity"),
48
- "bispectrum": ImportDetails("nm_bispectra", "Bispectra"),
5
+ if TYPE_CHECKING:
6
+ import numpy as np
7
+ from py_neuromodulation import NMSettings
8
+
9
+
10
+ FEATURE_DICT: dict[FeatureName | str, str] = {
11
+ "raw_hjorth": "Hjorth",
12
+ "return_raw": "Raw",
13
+ "bandpass_filter": "BandPower",
14
+ "stft": "STFT",
15
+ "fft": "FFT",
16
+ "welch": "Welch",
17
+ "sharpwave_analysis": "SharpwaveAnalyzer",
18
+ "fooof": "FooofAnalyzer",
19
+ "nolds": "Nolds",
20
+ "coherence": "Coherence",
21
+ "bursts": "Bursts",
22
+ "linelength": "LineLength",
23
+ "mne_connectivity": "MNEConnectivity",
24
+ "bispectrum": "Bispectra",
49
25
  }
50
26
 
51
27
 
@@ -63,12 +39,13 @@ class FeatureProcessors:
63
39
  sfreq (float): sampling frequency in Hz
64
40
  """
65
41
  from py_neuromodulation import user_features
42
+ from importlib import import_module
66
43
 
67
44
  # Accept 'str' for custom features
68
45
  self.features: dict[FeatureName | str, NMFeature] = {
69
- feature_name: get_class(FEATURE_DICT[feature_name])(
70
- settings, ch_names, sfreq
71
- )
46
+ feature_name: getattr(
47
+ import_module("py_neuromodulation.features"), FEATURE_DICT[feature_name]
48
+ )(settings, ch_names, sfreq)
72
49
  for feature_name in settings.features.get_enabled()
73
50
  }
74
51
 
@@ -80,12 +57,12 @@ class FeatureProcessors:
80
57
 
81
58
  Parameters
82
59
  ----------
83
- feature : nm_features_abc.Feature
60
+ feature : features_abc.Feature
84
61
  New feature to add to feature list
85
62
  """
86
63
  self.features[feature_name] = feature # type: ignore
87
64
 
88
- def estimate_features(self, data: np.ndarray) -> dict:
65
+ def estimate_features(self, data: "np.ndarray") -> dict:
89
66
  """Calculate features, as defined in settings.json
90
67
  Features are based on bandpower, raw Hjorth parameters and sharp wave
91
68
  characteristics.
@@ -125,7 +102,7 @@ def add_custom_feature(feature_name: str, new_feature: Type[NMFeature]):
125
102
  in this file).
126
103
  """
127
104
  from py_neuromodulation import user_features
128
- from py_neuromodulation.nm_settings import NMSettings
105
+ from py_neuromodulation import NMSettings
129
106
 
130
107
  user_features[feature_name] = new_feature
131
108
  NMSettings._add_feature(feature_name)
@@ -138,7 +115,7 @@ def remove_custom_feature(feature_name: str):
138
115
  feature_name (str): Name of the feature to remove
139
116
  """
140
117
  from py_neuromodulation import user_features
141
- from py_neuromodulation.nm_settings import NMSettings
118
+ from py_neuromodulation import NMSettings
142
119
 
143
120
  user_features.pop(feature_name)
144
121
  NMSettings._remove_feature(feature_name)
@@ -2,13 +2,16 @@ from collections.abc import Iterable
2
2
  import numpy as np
3
3
 
4
4
  from typing import TYPE_CHECKING
5
- from py_neuromodulation.nm_types import NMBaseModel
6
5
 
7
- from py_neuromodulation.nm_features import NMFeature
8
- from py_neuromodulation.nm_types import BoolSelector, FrequencyRange
6
+ from py_neuromodulation.utils.types import (
7
+ NMBaseModel,
8
+ NMFeature,
9
+ BoolSelector,
10
+ FrequencyRange,
11
+ )
9
12
 
10
13
  if TYPE_CHECKING:
11
- from py_neuromodulation.nm_settings import NMSettings
14
+ from py_neuromodulation import NMSettings
12
15
 
13
16
 
14
17
  class FooofAperiodicSettings(BoolSelector):
@@ -48,7 +51,7 @@ class FooofAnalyzer(NMFeature):
48
51
  def __init__(
49
52
  self, settings: "NMSettings", ch_names: Iterable[str], sfreq: float
50
53
  ) -> None:
51
- self.settings = settings.fooof
54
+ self.settings = settings.fooof_settings
52
55
  self.sfreq = sfreq
53
56
  self.ch_names = ch_names
54
57
 
@@ -59,12 +62,12 @@ class FooofAnalyzer(NMFeature):
59
62
  self.f_vec = np.arange(0, int(self.num_samples / 2) + 1, 1)
60
63
 
61
64
  assert (
62
- settings.fooof.windowlength_ms <= settings.segment_length_features_ms
65
+ self.settings.windowlength_ms <= settings.segment_length_features_ms
63
66
  ), f"fooof windowlength_ms ({settings.fooof.windowlength_ms}) needs to be smaller equal than segment_length_features_ms ({settings.segment_length_features_ms})."
64
67
 
65
68
  assert (
66
- settings.fooof.freq_range_hz[0] < sfreq
67
- and settings.fooof.freq_range_hz[1] < sfreq
69
+ self.settings.freq_range_hz[0] < sfreq
70
+ and self.settings.freq_range_hz[1] < sfreq
68
71
  ), f"fooof frequency range needs to be below sfreq, got {settings.fooof.freq_range_hz}"
69
72
 
70
73
  from fooof import FOOOFGroup
@@ -5,16 +5,19 @@ Reference: B Hjorth
5
5
  DOI: 10.1016/0013-4694(70)90143-4
6
6
  """
7
7
 
8
+ from typing import TYPE_CHECKING
8
9
  import numpy as np
9
- from collections.abc import Iterable
10
+ from collections.abc import Sequence
10
11
 
11
- from py_neuromodulation.nm_features import NMFeature
12
- from py_neuromodulation.nm_settings import NMSettings
12
+ from py_neuromodulation.utils.types import NMFeature
13
+
14
+ if TYPE_CHECKING:
15
+ from py_neuromodulation import NMSettings
13
16
 
14
17
 
15
18
  class Hjorth(NMFeature):
16
19
  def __init__(
17
- self, settings: NMSettings, ch_names: Iterable[str], sfreq: float
20
+ self, settings: "NMSettings", ch_names: Sequence[str], sfreq: float
18
21
  ) -> None:
19
22
  self.ch_names = ch_names
20
23
 
@@ -40,7 +43,9 @@ class Hjorth(NMFeature):
40
43
 
41
44
 
42
45
  class Raw(NMFeature):
43
- def __init__(self, settings: dict, ch_names: Iterable[str], sfreq: float) -> None:
46
+ def __init__(
47
+ self, settings: "NMSettings", ch_names: Sequence[str], sfreq: float
48
+ ) -> None:
44
49
  self.ch_names = ch_names
45
50
 
46
51
  def calc_feature(self, data: np.ndarray) -> dict:
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  from collections.abc import Sequence
3
3
 
4
- from py_neuromodulation.nm_features import NMFeature
4
+ from py_neuromodulation.features.feature_processor import NMFeature
5
5
 
6
6
 
7
7
  class LineLength(NMFeature):
@@ -1,13 +1,11 @@
1
1
  from collections.abc import Iterable
2
2
  import numpy as np
3
- import pandas as pd
4
3
  from typing import TYPE_CHECKING
5
4
 
6
- from py_neuromodulation.nm_features import NMFeature
7
- from py_neuromodulation.nm_types import NMBaseModel
5
+ from py_neuromodulation.utils.types import NMFeature, NMBaseModel
8
6
 
9
7
  if TYPE_CHECKING:
10
- from py_neuromodulation.nm_settings import NMSettings
8
+ from py_neuromodulation import NMSettings
11
9
  from mne.io import RawArray
12
10
  from mne import Epochs
13
11
 
@@ -32,8 +30,8 @@ class MNEConnectivity(NMFeature):
32
30
  self.sfreq = sfreq
33
31
 
34
32
  # Params used by spectral_connectivity_epochs
35
- self.mode = settings.mne_connectivity.mode
36
- self.method = settings.mne_connectivity.method
33
+ self.mode = settings.mne_connectivity_settings.mode
34
+ self.method = settings.mne_connectivity_settings.method
37
35
 
38
36
  self.fbands = settings.frequency_ranges_hz
39
37
  self.fband_ranges: list = []
@@ -48,6 +46,7 @@ class MNEConnectivity(NMFeature):
48
46
  from mne.io import RawArray
49
47
  from mne import Epochs
50
48
  from mne_connectivity import spectral_connectivity_epochs
49
+ import pandas as pd
51
50
 
52
51
  time_samples_s = data.shape[1] / self.sfreq
53
52
  epoch_length: float = 1 # TODO: Make this a parameter?
@@ -1,16 +1,14 @@
1
1
  import numpy as np
2
2
  from collections.abc import Iterable
3
3
 
4
- from py_neuromodulation.nm_types import NMBaseModel
5
4
  from typing import TYPE_CHECKING
6
5
 
7
- from py_neuromodulation.nm_features import NMFeature
8
- from py_neuromodulation.nm_types import BoolSelector
6
+ from py_neuromodulation.utils.types import NMFeature, BoolSelector, NMBaseModel
9
7
 
10
8
  from pydantic import field_validator
11
9
 
12
10
  if TYPE_CHECKING:
13
- from py_neuromodulation.nm_settings import NMSettings
11
+ from py_neuromodulation import NMSettings
14
12
 
15
13
 
16
14
  class NoldsFeatures(BoolSelector):
@@ -35,16 +33,16 @@ class Nolds(NMFeature):
35
33
  def __init__(
36
34
  self, settings: "NMSettings", ch_names: Iterable[str], sfreq: float
37
35
  ) -> None:
38
- self.settings = settings.nolds_features
36
+ self.settings = settings.nolds_settings
39
37
  self.ch_names = ch_names
40
38
 
41
39
  if len(self.settings.frequency_bands) > 0:
42
- from py_neuromodulation.nm_oscillatory import BandPower
40
+ from py_neuromodulation.features.bandpower import BandPower
43
41
 
44
42
  self.bp_filter = BandPower(settings, ch_names, sfreq, use_kf=False)
45
43
 
46
44
  # Check if the selected frequency bands are defined in the global settings
47
- for fb in settings.nolds_features.frequency_bands:
45
+ for fb in settings.nolds_settings.frequency_bands:
48
46
  assert (
49
47
  fb in settings.frequency_ranges_hz
50
48
  ), f"{fb} selected in nolds_features, but not defined in s['frequency_ranges_hz']"