paradigma 1.0.4__py3-none-any.whl → 1.1.0__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.
paradigma/__init__.py CHANGED
@@ -1,6 +1,15 @@
1
+ """
2
+ ParaDigMa: Parkinson Digital Biomarker Analysis Toolbox
3
+ """
4
+
1
5
  # read version from installed package
2
6
  from importlib.metadata import version
3
7
 
4
8
  __version__ = version("paradigma")
5
9
 
6
- __all__ = []
10
+ # Import main pipeline functions for easy access
11
+ from .orchestrator import run_paradigma
12
+
13
+ __all__ = [
14
+ "run_paradigma",
15
+ ]
@@ -1,6 +1,6 @@
1
1
  import pickle
2
2
  from pathlib import Path
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  import numpy as np
6
6
  from sklearn.base import BaseEstimator
@@ -10,9 +10,9 @@ from sklearn.preprocessing import StandardScaler
10
10
  class ClassifierPackage:
11
11
  def __init__(
12
12
  self,
13
- classifier: Optional[BaseEstimator] = None,
14
- threshold: Optional[float] = None,
15
- scaler: Optional[Any] = None,
13
+ classifier: BaseEstimator | None = None,
14
+ threshold: float | None = None,
15
+ scaler: Any | None = None,
16
16
  ):
17
17
  """
18
18
  Initialize the ClassifierPackage with a classifier, threshold, and scaler.
@@ -30,13 +30,13 @@ class ClassifierPackage:
30
30
  self.threshold = threshold
31
31
  self.scaler = scaler
32
32
 
33
- def transform_features(self, X) -> np.ndarray:
33
+ def transform_features(self, x) -> np.ndarray:
34
34
  """
35
35
  Transform the input features using the scaler.
36
36
 
37
37
  Parameters
38
38
  ----------
39
- X : np.ndarray
39
+ x : np.ndarray
40
40
  The input features.
41
41
 
42
42
  Return
@@ -45,8 +45,8 @@ class ClassifierPackage:
45
45
  The transformed features.
46
46
  """
47
47
  if not self.scaler:
48
- return X
49
- return self.scaler.transform(X)
48
+ return x
49
+ return self.scaler.transform(x)
50
50
 
51
51
  def update_scaler(self, x_train: np.ndarray) -> None:
52
52
  """
@@ -60,13 +60,13 @@ class ClassifierPackage:
60
60
  scaler = StandardScaler()
61
61
  self.scaler = scaler.fit(x_train)
62
62
 
63
- def predict_proba(self, X) -> float:
63
+ def predict_proba(self, x) -> float:
64
64
  """
65
65
  Make predictions using the classifier and apply the threshold.
66
66
 
67
67
  Parameters
68
68
  ----------
69
- X : np.ndarray
69
+ x : np.ndarray
70
70
  The input features.
71
71
 
72
72
  Return
@@ -77,15 +77,15 @@ class ClassifierPackage:
77
77
  """
78
78
  if not self.classifier:
79
79
  raise ValueError("Classifier is not loaded.")
80
- return self.classifier.predict_proba(X)[:, 1]
80
+ return self.classifier.predict_proba(x)[:, 1]
81
81
 
82
- def predict(self, X) -> int:
82
+ def predict(self, x) -> int:
83
83
  """
84
84
  Make predictions using the classifier and apply the threshold.
85
85
 
86
86
  Parameters
87
87
  ----------
88
- X : np.ndarray
88
+ x : np.ndarray
89
89
  The input features.
90
90
 
91
91
  Return
@@ -96,7 +96,7 @@ class ClassifierPackage:
96
96
  """
97
97
  if not self.classifier:
98
98
  raise ValueError("Classifier is not loaded.")
99
- return int(self.predict_proba(X) >= self.threshold)
99
+ return int(self.predict_proba(x) >= self.threshold)
100
100
 
101
101
  def save(self, filepath: str | Path) -> None:
102
102
  """
paradigma/config.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import warnings
2
2
  from dataclasses import asdict
3
- from typing import Dict, List
4
3
 
5
4
  import numpy as np
6
5
 
@@ -19,7 +18,9 @@ class BaseConfig:
19
18
  self.set_filenames(sensor)
20
19
 
21
20
  def set_filenames(self, prefix: str) -> None:
22
- """Sets the filenames based on the prefix. This method is duplicated from `gaits_analysis_config.py`.
21
+ """Sets the filenames based on the prefix.
22
+
23
+ This method is duplicated from `gaits_analysis_config.py`.
23
24
 
24
25
  Parameters
25
26
  ----------
@@ -51,12 +52,12 @@ class IMUConfig(BaseConfig):
51
52
 
52
53
  self.time_colname = self.column_mapping["TIME"]
53
54
 
54
- self.accelerometer_colnames: List[str] = []
55
- self.gyroscope_colnames: List[str] = []
56
- self.gravity_colnames: List[str] = []
55
+ self.accelerometer_colnames: list[str] = []
56
+ self.gyroscope_colnames: list[str] = []
57
+ self.gravity_colnames: list[str] = []
57
58
 
58
- self.d_channels_accelerometer: Dict[str, str] = {}
59
- self.d_channels_gyroscope: Dict[str, str] = {}
59
+ self.d_channels_accelerometer: dict[str, str] = {}
60
+ self.d_channels_gyroscope: dict[str, str] = {}
60
61
 
61
62
  accel_keys = ["ACCELEROMETER_X", "ACCELEROMETER_Y", "ACCELEROMETER_Z"]
62
63
  grav_keys = [
@@ -83,7 +84,7 @@ class IMUConfig(BaseConfig):
83
84
  c: self.rotation_units for c in self.gyroscope_colnames
84
85
  }
85
86
 
86
- self.d_channels_imu: Dict[str, str] = {
87
+ self.d_channels_imu: dict[str, str] = {
87
88
  **self.d_channels_accelerometer,
88
89
  **self.d_channels_gyroscope,
89
90
  }
@@ -95,6 +96,10 @@ class IMUConfig(BaseConfig):
95
96
  self.upper_cutoff_frequency = 3.5
96
97
  self.filter_order = 4
97
98
 
99
+ # Segmentation parameters for handling non-contiguous data
100
+ self.max_segment_gap_s = 1.5
101
+ self.min_segment_length_s = 1.5
102
+
98
103
 
99
104
  class PPGConfig(BaseConfig):
100
105
 
@@ -150,7 +155,7 @@ class GaitConfig(IMUConfig):
150
155
  self.spectrum_high_frequency: int = int(self.sampling_frequency / 2)
151
156
 
152
157
  # Power in specified frequency bands
153
- self.d_frequency_bandwidths: Dict[str, List[float]] = {
158
+ self.d_frequency_bandwidths: dict[str, list[float]] = {
154
159
  "power_below_gait": [0.2, 0.7],
155
160
  "power_gait": [0.7, 3.5],
156
161
  "power_tremor": [3.5, 8],
@@ -170,7 +175,7 @@ class GaitConfig(IMUConfig):
170
175
  # -----------------
171
176
  # TSDF data storage
172
177
  # -----------------
173
- self.d_channels_values: Dict[str, str] = {
178
+ self.d_channels_values: dict[str, str] = {
174
179
  "accelerometer_std_norm": DataUnits.GRAVITY,
175
180
  "accelerometer_x_grav_mean": DataUnits.GRAVITY,
176
181
  "accelerometer_y_grav_mean": DataUnits.GRAVITY,
@@ -178,18 +183,18 @@ class GaitConfig(IMUConfig):
178
183
  "accelerometer_x_grav_std": DataUnits.GRAVITY,
179
184
  "accelerometer_y_grav_std": DataUnits.GRAVITY,
180
185
  "accelerometer_z_grav_std": DataUnits.GRAVITY,
181
- "accelerometer_x_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
182
- "accelerometer_y_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
183
- "accelerometer_z_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY,
184
- "accelerometer_x_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
185
- "accelerometer_y_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
186
- "accelerometer_z_power_gait": DataUnits.POWER_SPECTRAL_DENSITY,
187
- "accelerometer_x_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
188
- "accelerometer_y_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
189
- "accelerometer_z_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
190
- "accelerometer_x_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
191
- "accelerometer_y_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
192
- "accelerometer_z_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY,
186
+ "accelerometer_x_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
187
+ "accelerometer_y_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
188
+ "accelerometer_z_power_below_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
189
+ "accelerometer_x_power_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
190
+ "accelerometer_y_power_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
191
+ "accelerometer_z_power_gait": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
192
+ "accelerometer_x_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
193
+ "accelerometer_y_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
194
+ "accelerometer_z_power_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
195
+ "accelerometer_x_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
196
+ "accelerometer_y_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
197
+ "accelerometer_z_power_above_tremor": DataUnits.POWER_SPECTRAL_DENSITY_ACC,
193
198
  "accelerometer_x_dominant_frequency": DataUnits.FREQUENCY,
194
199
  "accelerometer_y_dominant_frequency": DataUnits.FREQUENCY,
195
200
  "accelerometer_z_dominant_frequency": DataUnits.FREQUENCY,
@@ -257,20 +262,23 @@ class TremorConfig(IMUConfig):
257
262
  # -----------
258
263
  # Aggregation
259
264
  # -----------
260
- self.aggregates_tremor_power: List[str] = ["mode_binned", "median", "90p"]
265
+ self.aggregates_tremor_power: list[str] = ["mode_binned", "median", "90p"]
261
266
  self.evaluation_points_tremor_power: np.ndarray = np.linspace(0, 6, 301)
262
267
 
263
268
  # -----------------
264
269
  # TSDF data storage
265
270
  # -----------------
266
271
  if step == "features":
267
- self.d_channels_values: Dict[str, str] = {}
272
+ self.d_channels_values: dict[str, str] = {}
268
273
  for mfcc_coef in range(1, self.n_coefficients_mfcc + 1):
269
- self.d_channels_values[f"mfcc_{mfcc_coef}"] = "unitless"
274
+ self.d_channels_values[f"mfcc_{mfcc_coef}"] = DataUnits.NONE
275
+
276
+ self.d_channels_values[DataColumns.FREQ_PEAK] = DataUnits.FREQUENCY
277
+ self.d_channels_values[DataColumns.BELOW_TREMOR_POWER] = (
278
+ DataUnits.POWER_ROTATION
279
+ )
280
+ self.d_channels_values[DataColumns.TREMOR_POWER] = DataUnits.POWER_ROTATION
270
281
 
271
- self.d_channels_values["freq_peak"] = "Hz"
272
- self.d_channels_values["below_tremor_power"] = "(deg/s)^2"
273
- self.d_channels_values["tremor_power"] = "(deg/s)^2"
274
282
  elif step == "classification":
275
283
  self.d_channels_values = {
276
284
  DataColumns.PRED_TREMOR_PROBA: "probability",
@@ -299,7 +307,8 @@ class PulseRateConfig(PPGConfig):
299
307
  else:
300
308
  self.imu_sampling_frequency = IMUConfig().sampling_frequency
301
309
  warnings.warn(
302
- f"imu_sampling_frequency not provided, using default of {self.imu_sampling_frequency} Hz"
310
+ f"imu_sampling_frequency not provided, using default "
311
+ f"of {self.imu_sampling_frequency} Hz"
303
312
  )
304
313
 
305
314
  # Windowing parameters
paradigma/constants.py CHANGED
@@ -15,7 +15,9 @@ class DataColumns:
15
15
  GYROSCOPE_Z: str = "gyroscope_z"
16
16
  PPG: str = "green"
17
17
  TIME: str = "time"
18
- SEGMENT_NR: str = "segment_nr"
18
+ GAIT_SEGMENT_NR: str = "gait_segment_nr"
19
+ SEGMENT_NR: str = "gait_segment_nr" # Deprecated: Use GAIT_SEGMENT_NR instead
20
+ DATA_SEGMENT_NR: str = "data_segment_nr"
19
21
  SEGMENT_CAT: str = "segment_category"
20
22
 
21
23
  # Gait
@@ -37,6 +39,9 @@ class DataColumns:
37
39
  PRED_TREMOR_LOGREG: str = "pred_tremor_logreg"
38
40
  PRED_TREMOR_CHECKED: str = "pred_tremor_checked"
39
41
  PRED_ARM_AT_REST: str = "pred_arm_at_rest"
42
+ TREMOR_POWER: str = "tremor_power"
43
+ BELOW_TREMOR_POWER: str = "below_tremor_power"
44
+ FREQ_PEAK: str = "freq_peak"
40
45
 
41
46
  # Constants for PPG features
42
47
  VARIANCE: str = "variance"
@@ -78,9 +83,12 @@ class DataUnits:
78
83
  GRAVITY: str = "g"
79
84
  """ The acceleration due to gravity is in g. """
80
85
 
81
- POWER_SPECTRAL_DENSITY: str = "g^2/Hz"
86
+ POWER_SPECTRAL_DENSITY_ACC: str = "g^2/Hz"
82
87
  """ The power spectral density is in g^2/Hz. """
83
88
 
89
+ POWER_ROTATION: str = "(deg/s)^2"
90
+ """ The power is in "(deg/s)^2. """
91
+
84
92
  FREQUENCY: str = "Hz"
85
93
  """ The frequency is in Hz. """
86
94