paradigma 1.0.0__py3-none-any.whl → 1.0.2__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/config.py CHANGED
@@ -244,7 +244,7 @@ class TremorConfig(IMUConfig):
244
244
  }
245
245
 
246
246
 
247
- class HeartRateConfig(PPGConfig):
247
+ class PulseRateConfig(PPGConfig):
248
248
  def __init__(self, sensor: str = 'ppg', min_window_length_s: int = 30) -> None:
249
249
  super().__init__()
250
250
 
@@ -265,14 +265,14 @@ class HeartRateConfig(PPGConfig):
265
265
  self.freq_bin_resolution = 0.05 # Hz
266
266
 
267
267
  # ---------------------
268
- # Heart rate estimation
268
+ # Pulse rate estimation
269
269
  # ---------------------
270
270
  self.set_tfd_length(min_window_length_s) # Set tfd length to default of 30 seconds
271
271
  self.threshold_sqa = 0.5
272
- self.threshold_sqa_accelerometer = 0.13
272
+ self.threshold_sqa_accelerometer = 0.10
273
273
 
274
- hr_est_length = 2
275
- self.hr_est_samples = hr_est_length * self.sampling_frequency
274
+ pr_est_length = 2 # pulse rate estimation length in seconds
275
+ self.pr_est_samples = pr_est_length * self.sampling_frequency
276
276
 
277
277
  # Time-frequency distribution parameters
278
278
  self.kern_type = 'sep'
@@ -297,7 +297,7 @@ class HeartRateConfig(PPGConfig):
297
297
 
298
298
  def set_tfd_length(self, tfd_length: int):
299
299
  self.tfd_length = tfd_length
300
- self.min_hr_samples = int(round(self.tfd_length * self.sampling_frequency))
300
+ self.min_pr_samples = int(round(self.tfd_length * self.sampling_frequency))
301
301
 
302
302
  def set_sensor(self, sensor):
303
303
  self.sensor = sensor
paradigma/constants.py CHANGED
@@ -58,8 +58,8 @@ class DataColumns():
58
58
  PRED_SQA_ACC_LABEL: str = "pred_sqa_acc_label"
59
59
  PRED_SQA: str = "pred_sqa"
60
60
 
61
- # Constants for heart rate
62
- HEART_RATE: str = "heart_rate"
61
+ # Constants for pulse rate
62
+ PULSE_RATE: str = "pulse_rate"
63
63
 
64
64
  @dataclass(frozen=True)
65
65
  class DataUnits():
@@ -7,7 +7,7 @@ from scipy.signal import find_peaks, windows
7
7
  from scipy.stats import kurtosis, skew
8
8
  from sklearn.decomposition import PCA
9
9
 
10
- from paradigma.config import HeartRateConfig
10
+ from paradigma.config import PulseRateConfig
11
11
 
12
12
 
13
13
  def compute_statistics(data: np.ndarray, statistic: str, abs_stats: bool=False) -> np.ndarray:
@@ -353,7 +353,7 @@ def extract_frequency_peak(
353
353
  def compute_relative_power(
354
354
  freqs: np.ndarray,
355
355
  psd: np.ndarray,
356
- config: HeartRateConfig
356
+ config: PulseRateConfig
357
357
  ) -> list:
358
358
  """
359
359
  Calculate relative power within the dominant frequency band in the physiological range (0.75 - 3 Hz).
@@ -364,11 +364,11 @@ def compute_relative_power(
364
364
  The frequency bins of the power spectral density.
365
365
  psd: np.ndarray
366
366
  The power spectral density of the signal.
367
- config: HeartRateConfig
367
+ config: PulseRateConfig
368
368
  The configuration object containing the parameters for the feature extraction. The following
369
369
  attributes are used:
370
370
  - freq_band_physio: tuple
371
- The frequency band for physiological heart rate (default: (0.75, 3)).
371
+ The frequency band for physiological pulse rate (default: (0.75, 3)).
372
372
  - bandwidth: float
373
373
  The bandwidth around the peak frequency to consider for relative power calculation (default: 0.5).
374
374
 
@@ -10,14 +10,14 @@ from typing import List
10
10
 
11
11
  from paradigma.classification import ClassifierPackage
12
12
  from paradigma.constants import DataColumns
13
- from paradigma.config import HeartRateConfig
13
+ from paradigma.config import PulseRateConfig
14
14
  from paradigma.feature_extraction import compute_statistics, compute_signal_to_noise_ratio, compute_auto_correlation, \
15
15
  compute_dominant_frequency, compute_relative_power, compute_spectral_entropy
16
- from paradigma.pipelines.heart_rate_utils import assign_sqa_label, extract_hr_segments, extract_hr_from_segment
16
+ from paradigma.pipelines.pulse_rate_utils import assign_sqa_label, extract_pr_segments, extract_pr_from_segment
17
17
  from paradigma.segmenting import tabulate_windows, WindowedDataExtractor
18
- from paradigma.util import read_metadata, aggregate_parameter
18
+ from paradigma.util import aggregate_parameter
19
19
 
20
- def extract_signal_quality_features(df_ppg: pd.DataFrame, df_acc: pd.DataFrame, ppg_config: HeartRateConfig, acc_config: HeartRateConfig) -> pd.DataFrame:
20
+ def extract_signal_quality_features(df_ppg: pd.DataFrame, df_acc: pd.DataFrame, ppg_config: PulseRateConfig, acc_config: PulseRateConfig) -> pd.DataFrame:
21
21
  """
22
22
  Extract signal quality features from the PPG signal.
23
23
  The features are extracted from the temporal and spectral domain of the PPG signal.
@@ -30,9 +30,9 @@ def extract_signal_quality_features(df_ppg: pd.DataFrame, df_acc: pd.DataFrame,
30
30
  The DataFrame containing the PPG signal.
31
31
  df_acc : pd.DataFrame
32
32
  The DataFrame containing the accelerometer signal.
33
- ppg_config: HeartRateConfig
33
+ ppg_config: PulseRateConfig
34
34
  The configuration for the signal quality feature extraction of the PPG signal.
35
- acc_config: HeartRateConfig
35
+ acc_config: PulseRateConfig
36
36
  The configuration for the signal quality feature extraction of the accelerometer signal.
37
37
 
38
38
  Returns
@@ -94,7 +94,7 @@ def extract_signal_quality_features(df_ppg: pd.DataFrame, df_acc: pd.DataFrame,
94
94
  return df_features
95
95
 
96
96
 
97
- def signal_quality_classification(df: pd.DataFrame, config: HeartRateConfig, full_path_to_classifier_package: str | Path) -> pd.DataFrame:
97
+ def signal_quality_classification(df: pd.DataFrame, config: PulseRateConfig, full_path_to_classifier_package: str | Path) -> pd.DataFrame:
98
98
  """
99
99
  Classify the signal quality of the PPG signal using a logistic regression classifier. A probability close to 1 indicates a high-quality signal, while a probability close to 0 indicates a low-quality signal.
100
100
  The classifier is trained on features extracted from the PPG signal. The features are extracted using the extract_signal_quality_features function.
@@ -105,7 +105,7 @@ def signal_quality_classification(df: pd.DataFrame, config: HeartRateConfig, ful
105
105
  ----------
106
106
  df : pd.DataFrame
107
107
  The DataFrame containing the PPG features and the accelerometer feature for signal quality classification.
108
- config : HeartRateConfig
108
+ config : PulseRateConfig
109
109
  The configuration for the signal quality classification.
110
110
  full_path_to_classifier_package : str | Path
111
111
  The path to the directory containing the classifier.
@@ -128,9 +128,9 @@ def signal_quality_classification(df: pd.DataFrame, config: HeartRateConfig, ful
128
128
  return df[[DataColumns.TIME, DataColumns.PRED_SQA_PROBA, DataColumns.PRED_SQA_ACC_LABEL]] # Return only the relevant columns, namely the predicted probabilities for the PPG signal quality and the accelerometer label
129
129
 
130
130
 
131
- def estimate_heart_rate(df_sqa: pd.DataFrame, df_ppg_preprocessed: pd.DataFrame, config: HeartRateConfig) -> pd.DataFrame:
131
+ def estimate_pulse_rate(df_sqa: pd.DataFrame, df_ppg_preprocessed: pd.DataFrame, config: PulseRateConfig) -> pd.DataFrame:
132
132
  """
133
- Estimate the heart rate from the PPG signal using the time-frequency domain method.
133
+ Estimate the pulse rate from the PPG signal using the time-frequency domain method.
134
134
 
135
135
  Parameters
136
136
  ----------
@@ -138,13 +138,13 @@ def estimate_heart_rate(df_sqa: pd.DataFrame, df_ppg_preprocessed: pd.DataFrame,
138
138
  The DataFrame containing the signal quality assessment predictions.
139
139
  df_ppg_preprocessed : pd.DataFrame
140
140
  The DataFrame containing the preprocessed PPG signal.
141
- config : HeartRateConfig
142
- The configuration for the heart rate estimation.
141
+ config : PulseRateConfig
142
+ The configuration for the pulse rate estimation.
143
143
 
144
144
  Returns
145
145
  -------
146
- df_hr : pd.DataFrame
147
- The DataFrame containing the heart rate estimations.
146
+ df_pr : pd.DataFrame
147
+ The DataFrame containing the pulse rate estimations.
148
148
  """
149
149
 
150
150
  # Extract NumPy arrays for faster operations
@@ -156,13 +156,13 @@ def estimate_heart_rate(df_sqa: pd.DataFrame, df_ppg_preprocessed: pd.DataFrame,
156
156
 
157
157
  # Assign window-level probabilities to individual samples
158
158
  sqa_label = assign_sqa_label(ppg_post_prob, config, acc_label) # assigns a signal quality label to every individual data point
159
- v_start_idx, v_end_idx = extract_hr_segments(sqa_label, config.min_hr_samples) # extracts heart rate segments based on the SQA label
159
+ v_start_idx, v_end_idx = extract_pr_segments(sqa_label, config.min_pr_samples) # extracts pulse rate segments based on the SQA label
160
160
 
161
- v_hr_rel = np.array([])
162
- t_hr_rel = np.array([])
161
+ v_pr_rel = np.array([])
162
+ t_pr_rel = np.array([])
163
163
 
164
- edge_add = 2 * config.sampling_frequency # Add 2s on both sides of the segment for HR estimation
165
- step_size = config.hr_est_samples # Step size for HR estimation
164
+ edge_add = 2 * config.sampling_frequency # Add 2s on both sides of the segment for PR estimation
165
+ step_size = config.pr_est_samples # Step size for PR estimation
166
166
 
167
167
  # Estimate the maximum size for preallocation
168
168
  valid_segments = (v_start_idx >= edge_add) & (v_end_idx <= len(ppg_preprocessed) - edge_add) # check if the segments are valid, e.g. not too close to the edges (2s)
@@ -171,55 +171,55 @@ def estimate_heart_rate(df_sqa: pd.DataFrame, df_ppg_preprocessed: pd.DataFrame,
171
171
  max_size = np.sum((valid_end_idx - valid_start_idx) // step_size) # maximum size for preallocation
172
172
 
173
173
  # Preallocate arrays
174
- v_hr_rel = np.empty(max_size, dtype=float)
175
- t_hr_rel = np.empty(max_size, dtype=float)
174
+ v_pr_rel = np.empty(max_size, dtype=float)
175
+ t_pr_rel = np.empty(max_size, dtype=float)
176
176
 
177
177
  # Track current position
178
- hr_pos = 0
178
+ pr_pos = 0
179
179
 
180
180
  for start_idx, end_idx in zip(valid_start_idx, valid_end_idx):
181
181
  # Extract extended PPG segment
182
182
  extended_ppg_segment = ppg_preprocessed[start_idx - edge_add : end_idx + edge_add, ppg_idx]
183
183
 
184
- # Estimate heart rate
185
- hr_est = extract_hr_from_segment(
184
+ # Estimate pulse rate
185
+ pr_est = extract_pr_from_segment(
186
186
  extended_ppg_segment,
187
187
  config.tfd_length,
188
188
  config.sampling_frequency,
189
189
  config.kern_type,
190
190
  config.kern_params,
191
191
  )
192
- n_hr = len(hr_est) # Number of heart rate estimates
193
- end_idx_time = n_hr * step_size + start_idx # Calculate end index for time, different from end_idx since it is always a multiple of step_size, while end_idx is not
192
+ n_pr = len(pr_est) # Number of pulse rate estimates
193
+ end_idx_time = n_pr * step_size + start_idx # Calculate end index for time, different from end_idx since it is always a multiple of step_size, while end_idx is not
194
194
 
195
- # Extract relative time for HR estimates
196
- hr_time = ppg_preprocessed[start_idx : end_idx_time : step_size, time_idx]
195
+ # Extract relative time for PR estimates
196
+ pr_time = ppg_preprocessed[start_idx : end_idx_time : step_size, time_idx]
197
197
 
198
198
  # Insert into preallocated arrays
199
- v_hr_rel[hr_pos:hr_pos + n_hr] = hr_est
200
- t_hr_rel[hr_pos:hr_pos + n_hr] = hr_time
201
- hr_pos += n_hr
199
+ v_pr_rel[pr_pos:pr_pos + n_pr] = pr_est
200
+ t_pr_rel[pr_pos:pr_pos + n_pr] = pr_time
201
+ pr_pos += n_pr
202
202
 
203
- df_hr = pd.DataFrame({"time": t_hr_rel, "heart_rate": v_hr_rel})
203
+ df_pr = pd.DataFrame({"time": t_pr_rel, "pulse_rate": v_pr_rel})
204
204
 
205
- return df_hr
205
+ return df_pr
206
206
 
207
207
 
208
- def aggregate_heart_rate(hr_values: np.ndarray, aggregates: List[str] = ['mode', '99p']) -> dict:
208
+ def aggregate_pulse_rate(pr_values: np.ndarray, aggregates: List[str] = ['mode', '99p']) -> dict:
209
209
  """
210
- Aggregate the heart rate estimates using the specified aggregation methods.
210
+ Aggregate the pulse rate estimates using the specified aggregation methods.
211
211
 
212
212
  Parameters
213
213
  ----------
214
- hr_values : np.ndarray
215
- The array containing the heart rate estimates
214
+ pr_values : np.ndarray
215
+ The array containing the pulse rate estimates
216
216
  aggregates : List[str]
217
- The list of aggregation methods to be used for the heart rate estimates. The default is ['mode', '99p'].
217
+ The list of aggregation methods to be used for the pulse rate estimates. The default is ['mode', '99p'].
218
218
 
219
219
  Returns
220
220
  -------
221
221
  aggregated_results : dict
222
- The dictionary containing the aggregated results of the heart rate estimates.
222
+ The dictionary containing the aggregated results of the pulse rate estimates.
223
223
  """
224
224
  # Initialize the dictionary for the aggregated results
225
225
  aggregated_results = {}
@@ -227,19 +227,19 @@ def aggregate_heart_rate(hr_values: np.ndarray, aggregates: List[str] = ['mode',
227
227
  # Initialize the dictionary for the aggregated results with the metadata
228
228
  aggregated_results = {
229
229
  'metadata': {
230
- 'nr_hr_est': len(hr_values)
230
+ 'nr_pr_est': len(pr_values)
231
231
  },
232
- 'hr_aggregates': {}
232
+ 'pr_aggregates': {}
233
233
  }
234
234
  for aggregate in aggregates:
235
- aggregated_results['hr_aggregates'][f'{aggregate}_{DataColumns.HEART_RATE}'] = aggregate_parameter(hr_values, aggregate)
235
+ aggregated_results['pr_aggregates'][f'{aggregate}_{DataColumns.PULSE_RATE}'] = aggregate_parameter(pr_values, aggregate)
236
236
 
237
237
  return aggregated_results
238
238
 
239
239
 
240
240
  def extract_temporal_domain_features(
241
241
  ppg_windowed: np.ndarray,
242
- config: HeartRateConfig,
242
+ config: PulseRateConfig,
243
243
  quality_stats: List[str] = ['mean', 'std']
244
244
  ) -> pd.DataFrame:
245
245
  """
@@ -250,7 +250,7 @@ def extract_temporal_domain_features(
250
250
  ppg_windowed: np.ndarray
251
251
  The dataframe containing the windowed accelerometer signal
252
252
 
253
- config: HeartRateConfig
253
+ config: PulseRateConfig
254
254
  The configuration object containing the parameters for the feature extraction
255
255
 
256
256
  quality_stats: list, optional
@@ -273,7 +273,7 @@ def extract_temporal_domain_features(
273
273
 
274
274
  def extract_spectral_domain_features(
275
275
  ppg_windowed: np.ndarray,
276
- config: HeartRateConfig,
276
+ config: PulseRateConfig,
277
277
  ) -> pd.DataFrame:
278
278
  """
279
279
  Calculate the spectral features (dominant frequency, relative power, and spectral entropy)
@@ -285,7 +285,7 @@ def extract_spectral_domain_features(
285
285
  ppg_windowed: np.ndarray
286
286
  The dataframe containing the windowed ppg signal
287
287
 
288
- config: HeartRateConfig
288
+ config: PulseRateConfig
289
289
  The configuration object containing the parameters for the feature extraction
290
290
 
291
291
  Returns
@@ -371,7 +371,7 @@ def extract_acc_power_feature(
371
371
  def extract_accelerometer_feature(
372
372
  acc_windowed: np.ndarray,
373
373
  ppg_windowed: np.ndarray,
374
- config: HeartRateConfig
374
+ config: PulseRateConfig
375
375
  ) -> pd.DataFrame:
376
376
  """
377
377
  Extract accelerometer features from the accelerometer signal in the PPG frequency range.
@@ -384,7 +384,7 @@ def extract_accelerometer_feature(
384
384
  ppg_windowed: np.ndarray
385
385
  The dataframe containing the corresponding windowed ppg signal
386
386
 
387
- config: HeartRateConfig
387
+ config: PulseRateConfig
388
388
  The configuration object containing the parameters for the feature extraction
389
389
 
390
390
  Returns
@@ -2,12 +2,12 @@ import numpy as np
2
2
  from scipy import signal
3
3
  from typing import Tuple
4
4
 
5
- from paradigma.config import HeartRateConfig
5
+ from paradigma.config import PulseRateConfig
6
6
 
7
7
 
8
8
  def assign_sqa_label(
9
9
  ppg_prob: np.ndarray,
10
- config: HeartRateConfig,
10
+ config: PulseRateConfig,
11
11
  acc_label=None
12
12
  ) -> np.ndarray:
13
13
  """
@@ -17,7 +17,7 @@ def assign_sqa_label(
17
17
  ----------
18
18
  ppg_prob : np.ndarray
19
19
  The probabilities for PPG.
20
- config : HeartRateConfig
20
+ config : PulseRateConfig
21
21
  The configuration parameters.
22
22
  acc_label : np.ndarray, optional
23
23
  The labels for the accelerometer.
@@ -61,23 +61,23 @@ def assign_sqa_label(
61
61
  return sqa_label
62
62
 
63
63
 
64
- def extract_hr_segments(sqa_label: np.ndarray, min_hr_samples: int) -> Tuple[np.ndarray, np.ndarray]:
64
+ def extract_pr_segments(sqa_label: np.ndarray, min_pr_samples: int) -> Tuple[np.ndarray, np.ndarray]:
65
65
  """
66
- Extracts heart rate segments based on the SQA label.
66
+ Extracts pulse rate segments based on the SQA label.
67
67
 
68
68
  Parameters
69
69
  ----------
70
70
  sqa_label : np.ndarray
71
71
  The signal quality assessment label.
72
- min_hr_samples : int
73
- The minimum number of samples required for a heart rate segment.
72
+ min_pr_samples : int
73
+ The minimum number of samples required for a pulse rate segment.
74
74
 
75
75
  Returns
76
76
  -------
77
77
  Tuple[v_start_idx_long, v_end_idx_long]
78
- The start and end indices of the heart rate segments.
78
+ The start and end indices of the pulse rate segments.
79
79
  """
80
- # Find the start and end indices of the heart rate segments
80
+ # Find the start and end indices of the pulse rate segments
81
81
  v_start_idx = np.where(np.diff(sqa_label.astype(int)) == 1)[0] + 1
82
82
  v_end_idx = np.where(np.diff(sqa_label.astype(int)) == -1)[0] + 1
83
83
 
@@ -88,13 +88,13 @@ def extract_hr_segments(sqa_label: np.ndarray, min_hr_samples: int) -> Tuple[np.
88
88
  v_end_idx = np.append(v_end_idx, len(sqa_label))
89
89
 
90
90
  # Check if the segments are long enough
91
- v_start_idx_long = v_start_idx[(v_end_idx - v_start_idx) >= min_hr_samples]
92
- v_end_idx_long = v_end_idx[(v_end_idx - v_start_idx) >= min_hr_samples]
91
+ v_start_idx_long = v_start_idx[(v_end_idx - v_start_idx) >= min_pr_samples]
92
+ v_end_idx_long = v_end_idx[(v_end_idx - v_start_idx) >= min_pr_samples]
93
93
 
94
94
  return v_start_idx_long, v_end_idx_long
95
95
 
96
96
 
97
- def extract_hr_from_segment(
97
+ def extract_pr_from_segment(
98
98
  ppg: np.ndarray,
99
99
  tfd_length: int,
100
100
  fs: int,
@@ -102,7 +102,7 @@ def extract_hr_from_segment(
102
102
  kern_params: dict
103
103
  ) -> np.ndarray:
104
104
  """
105
- Extracts heart rate from the time-frequency distribution of the PPG signal.
105
+ Extracts pulse rate from the time-frequency distribution of the PPG signal.
106
106
 
107
107
  Parameters
108
108
  ----------
@@ -121,7 +121,7 @@ def extract_hr_from_segment(
121
121
  Returns
122
122
  -------
123
123
  np.ndarray
124
- The estimated heart rate.
124
+ The estimated pulse rate.
125
125
  """
126
126
 
127
127
  # Constants to handle boundary effects
@@ -145,23 +145,23 @@ def extract_hr_from_segment(
145
145
  end_idx = len(ppg)
146
146
  ppg_segments.append(ppg[start_idx:end_idx])
147
147
 
148
- hr_est_from_ppg = np.array([])
148
+ pr_est_from_ppg = np.array([])
149
149
  for segment in ppg_segments:
150
150
  # Calculate the time-frequency distribution
151
- hr_tfd = extract_hr_with_tfd(segment, fs, kern_type, kern_params)
152
- hr_est_from_ppg = np.concatenate((hr_est_from_ppg, hr_tfd))
151
+ pr_tfd = extract_pr_with_tfd(segment, fs, kern_type, kern_params)
152
+ pr_est_from_ppg = np.concatenate((pr_est_from_ppg, pr_tfd))
153
153
 
154
- return hr_est_from_ppg
154
+ return pr_est_from_ppg
155
155
 
156
156
 
157
- def extract_hr_with_tfd(
157
+ def extract_pr_with_tfd(
158
158
  ppg: np.ndarray,
159
159
  fs: int,
160
160
  kern_type: str,
161
161
  kern_params: dict
162
162
  ) -> np.ndarray:
163
163
  """
164
- Estimate heart rate (HR) from a PPG segment using a TFD method with optional
164
+ Estimate pulse rate (PR) from a PPG segment using a TFD method with optional
165
165
  moving average filtering.
166
166
 
167
167
  Parameters
@@ -177,8 +177,8 @@ def extract_hr_with_tfd(
177
177
 
178
178
  Returns
179
179
  -------
180
- hr_smooth_tfd : np.ndarray
181
- Estimated HR values (in beats per minute) for each 2-second segment of the PPG signal.
180
+ pr_smooth_tfd : np.ndarray
181
+ Estimated pr values (in beats per minute) for each 2-second segment of the PPG signal.
182
182
  """
183
183
  # Generate the TFD matrix using the specified kernel
184
184
  tfd_obj = TimeFreqDistr()
@@ -189,16 +189,16 @@ def extract_hr_with_tfd(
189
189
  time_axis = np.arange(num_time_samples) / fs
190
190
  freq_axis = np.linspace(0, 0.5, num_freq_bins) * fs
191
191
 
192
- # Estimate HR by identifying the max frequency in the TFD
192
+ # Estimate pulse rate by identifying the max frequency in the TFD
193
193
  max_freq_indices = np.argmax(tfd, axis=0)
194
194
 
195
- hr_smooth_tfd = np.array([])
195
+ pr_smooth_tfd = np.array([])
196
196
  for i in range(2, int(len(ppg) / fs) - 4 + 1, 2): # Skip the first and last 2 seconds, add 1 to include the last segment
197
197
  relevant_indices = (time_axis >= i) & (time_axis < i + 2)
198
198
  avg_frequency = np.mean(freq_axis[max_freq_indices[relevant_indices]])
199
- hr_smooth_tfd = np.concatenate((hr_smooth_tfd, [60 * avg_frequency])) # Convert frequency to BPM
199
+ pr_smooth_tfd = np.concatenate((pr_smooth_tfd, [60 * avg_frequency])) # Convert frequency to BPM
200
200
 
201
- return hr_smooth_tfd
201
+ return pr_smooth_tfd
202
202
 
203
203
 
204
204
  class TimeFreqDistr:
@@ -143,7 +143,6 @@ def detect_tremor(df: pd.DataFrame, config: TremorConfig, full_path_to_classifie
143
143
 
144
144
  return df
145
145
 
146
-
147
146
  def aggregate_tremor(df: pd.DataFrame, config: TremorConfig):
148
147
  """
149
148
  Quantifies the amount of tremor time and tremor power, aggregated over all windows in the input dataframe.
@@ -154,8 +153,8 @@ def aggregate_tremor(df: pd.DataFrame, config: TremorConfig):
154
153
  Parameters
155
154
  ----------
156
155
  df : pd.DataFrame
157
- The input DataFrame containing extracted tremor features. The DataFrame must include
158
- the necessary columns as specified in the classifier's feature names.
156
+ The input DataFrame containing the tremor predictions and computed tremor power.
157
+ The DataFrame must also contain a datatime column ('time_dt').
159
158
 
160
159
  config : TremorConfig
161
160
  Configuration object containing the percentile for aggregating tremor power.
@@ -251,6 +250,7 @@ def extract_spectral_domain_features(data: np.ndarray, config) -> pd.DataFrame:
251
250
  pd.DataFrame
252
251
  The feature dataframe containing the extracted spectral features, including
253
252
  MFCCs, the frequency of the peak, the tremor power and below tremor power for each window.
253
+
254
254
  """
255
255
 
256
256
  # Initialize a dictionary to hold the results
paradigma/testing.py CHANGED
@@ -7,14 +7,14 @@ import tsdf
7
7
  from typing import List
8
8
 
9
9
  from paradigma.classification import ClassifierPackage
10
- from paradigma.config import IMUConfig, PPGConfig, GaitConfig, TremorConfig, HeartRateConfig
10
+ from paradigma.config import IMUConfig, PPGConfig, GaitConfig, TremorConfig, PulseRateConfig
11
11
  from paradigma.constants import DataColumns, TimeUnit
12
12
  from paradigma.pipelines.gait_pipeline import extract_gait_features, detect_gait, \
13
13
  extract_arm_activity_features, filter_gait
14
14
  from paradigma.pipelines.tremor_pipeline import extract_tremor_features, detect_tremor, \
15
15
  aggregate_tremor
16
- from paradigma.pipelines.heart_rate_pipeline import extract_signal_quality_features, signal_quality_classification, \
17
- aggregate_heart_rate
16
+ from paradigma.pipelines.pulse_rate_pipeline import extract_signal_quality_features, signal_quality_classification, \
17
+ aggregate_pulse_rate
18
18
  from paradigma.preprocessing import preprocess_imu_data, preprocess_ppg_data
19
19
  from paradigma.util import read_metadata, write_df_data, get_end_iso8601, merge_predictions_with_timestamps
20
20
 
@@ -353,7 +353,7 @@ def aggregate_tremor_io(path_to_feature_input: str | Path, path_to_prediction_in
353
353
  json.dump(d_aggregates, json_file, indent=4)
354
354
 
355
355
 
356
- def extract_signal_quality_features_io(input_path: str | Path, output_path: str | Path, ppg_config: HeartRateConfig, acc_config: HeartRateConfig) -> pd.DataFrame:
356
+ def extract_signal_quality_features_io(input_path: str | Path, output_path: str | Path, ppg_config: PulseRateConfig, acc_config: PulseRateConfig) -> pd.DataFrame:
357
357
  """
358
358
  Extract signal quality features from the PPG signal and save them to a file.
359
359
 
@@ -363,9 +363,9 @@ def extract_signal_quality_features_io(input_path: str | Path, output_path: str
363
363
  The path to the directory containing the preprocessed PPG and accelerometer data.
364
364
  output_path : str | Path
365
365
  The path to the directory where the extracted features will be saved.
366
- ppg_config: HeartRateConfig
366
+ ppg_config: PulseRateConfig
367
367
  The configuration for the signal quality feature extraction of the ppg signal.
368
- acc_config: HeartRateConfig
368
+ acc_config: PulseRateConfig
369
369
  The configuration for the signal quality feature extraction of the accelerometer signal.
370
370
 
371
371
  Returns
@@ -390,7 +390,7 @@ def extract_signal_quality_features_io(input_path: str | Path, output_path: str
390
390
  return df_windowed
391
391
 
392
392
 
393
- def signal_quality_classification_io(input_path: str | Path, output_path: str | Path, path_to_classifier_input: str | Path, config: HeartRateConfig) -> None:
393
+ def signal_quality_classification_io(input_path: str | Path, output_path: str | Path, path_to_classifier_input: str | Path, config: PulseRateConfig) -> None:
394
394
 
395
395
  # Load the data
396
396
  metadata_time, metadata_values = read_metadata(input_path, config.meta_filename, config.time_filename, config.values_filename)
@@ -399,32 +399,32 @@ def signal_quality_classification_io(input_path: str | Path, output_path: str |
399
399
  df_sqa = signal_quality_classification(df_windowed, config, path_to_classifier_input)
400
400
 
401
401
 
402
- def aggregate_heart_rate_io(
402
+ def aggregate_pulse_rate_io(
403
403
  full_path_to_input: str | Path,
404
404
  full_path_to_output: str | Path,
405
405
  aggregates: List[str] = ['mode', '99p']
406
406
  ) -> None:
407
407
  """
408
- Extract heart rate from the PPG signal and save the aggregated heart rate estimates to a file.
408
+ Extract pulse rate from the PPG signal and save the aggregated pulse rate estimates to a file.
409
409
 
410
410
  Parameters
411
411
  ----------
412
412
  input_path : str | Path
413
- The path to the directory containing the heart rate estimates.
413
+ The path to the directory containing the pulse rate estimates.
414
414
  output_path : str | Path
415
- The path to the directory where the aggregated heart rate estimates will be saved.
415
+ The path to the directory where the aggregated pulse rate estimates will be saved.
416
416
  aggregates : List[str]
417
- The list of aggregation methods to be used for the heart rate estimates. The default is ['mode', '99p'].
417
+ The list of aggregation methods to be used for the pulse rate estimates. The default is ['mode', '99p'].
418
418
  """
419
419
 
420
- # Load the heart rate estimates
420
+ # Load the pulse rate estimates
421
421
  with open(full_path_to_input, 'r') as f:
422
- df_hr = json.load(f)
422
+ df_pr = json.load(f)
423
423
 
424
- # Aggregate the heart rate estimates
425
- hr_values = df_hr['heart_rate'].values
426
- df_hr_aggregates = aggregate_heart_rate(hr_values, aggregates)
424
+ # Aggregate the pulse rate estimates
425
+ pr_values = df_pr['pulse_rate'].values
426
+ df_pr_aggregates = aggregate_pulse_rate(pr_values, aggregates)
427
427
 
428
- # Save the aggregated heart rate estimates
428
+ # Save the aggregated pulse rate estimates
429
429
  with open(full_path_to_output, 'w') as json_file:
430
- json.dump(df_hr_aggregates, json_file, indent=4)
430
+ json.dump(df_pr_aggregates, json_file, indent=4)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: paradigma
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: ParaDigMa - A toolbox for deriving Parkinson's disease Digital Markers from real-life wrist sensor data
5
5
  License: Apache-2.0
6
6
  Author: Erik Post
@@ -26,7 +26,7 @@ Description-Content-Type: text/markdown
26
26
  |:----:|----|
27
27
  | **Packages and Releases** | [![Latest release](https://img.shields.io/github/release/biomarkersparkinson/paradigma.svg)](https://github.com/biomarkersparkinson/paradigma/releases/latest) [![PyPI](https://img.shields.io/pypi/v/paradigma.svg)](https://pypi.python.org/pypi/paradigma/) [![Static Badge](https://img.shields.io/badge/RSD-paradigma-lib)](https://research-software-directory.org/software/paradigma) |
28
28
  | **DOI** | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.13838392.svg)](https://doi.org/10.5281/zenodo.13838392) |
29
- | **Build Status** | [![](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) [![Build and test](https://github.com/biomarkersParkinson/paradigma/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/biomarkersParkinson/paradigma/actions/workflows/build-and-test.yml) [![pages-build-deployment](https://github.com/biomarkersParkinson/paradigma/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/biomarkersParkinson/paradigma/actions/workflows/pages/pages-build-deployment) |
29
+ | **Build Status** | [![](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [![Build and test](https://github.com/biomarkersParkinson/paradigma/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/biomarkersParkinson/paradigma/actions/workflows/build-and-test.yml) [![pages-build-deployment](https://github.com/biomarkersParkinson/paradigma/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/biomarkersParkinson/paradigma/actions/workflows/pages/pages-build-deployment) |
30
30
  | **License** | [![GitHub license](https://img.shields.io/github/license/biomarkersParkinson/paradigma)](https://github.com/biomarkersparkinson/paradigma/blob/main/LICENSE) |
31
31
  <!-- | **Fairness** | [![fair-software.eu](https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F-green)](https://fair-software.eu) [![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/8083/badge)](https://www.bestpractices.dev/projects/8083) | -->
32
32
 
@@ -4,19 +4,19 @@ paradigma/assets/gait_filtering_clf_package.pkl,sha256=lAaLyhmXdV4X_drmYt0EM6wGw
4
4
  paradigma/assets/ppg_quality_clf_package.pkl,sha256=vUcM4v8gZwWAmDVK7E4UcHhVnhlEg27RSB71oPGloSc,1292
5
5
  paradigma/assets/tremor_detection_clf_package.pkl,sha256=S-KsK1EcUBJX6oGGBo8GqU0AhNZThA6Qe-cs0QPcWw4,1475
6
6
  paradigma/classification.py,sha256=sBJSePvwHZNPUQuLdx-pncfnDzMq-1naomsCxSJneWY,2921
7
- paradigma/config.py,sha256=72KkIEVV1v5dD9ZJDPI-mFNvorA8nBADEcA0A-jviHU,11163
8
- paradigma/constants.py,sha256=JlrD4Zx66g7myQALYAc4Gw_y6yW5EipZuvwj9_fjjpI,3543
9
- paradigma/feature_extraction.py,sha256=PAl9DgjTljxtifjPLpM1L_7xRs2fI6eUcybkaJ9kxAA,35323
7
+ paradigma/config.py,sha256=hGmWpK1sjwjlmCn43bBa6DEWBTDZjRXlGANIp6X42mY,11206
8
+ paradigma/constants.py,sha256=gR--OzxaZqS5nJnYlWLqnJ9xN05_GMNtd6ec3upsfms,3543
9
+ paradigma/feature_extraction.py,sha256=zgu_fW1zpPvHxpgsPVpJILUiyWH44b9n1bGG7lV2HwE,35323
10
10
  paradigma/pipelines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  paradigma/pipelines/gait_pipeline.py,sha256=ZhAc2RZbBX52SJ8hvSRjb5THM47WCfY50iEdImlszJM,26231
12
- paradigma/pipelines/heart_rate_pipeline.py,sha256=0-D9KcW9nwE5sgXsWHONkeKrsX6qZ5BYqjDttoffwL8,17726
13
- paradigma/pipelines/heart_rate_utils.py,sha256=aV2mTMWrFWHZD0KpHqy3IIC1onZykbppyp7_OUWxFTU,26764
14
- paradigma/pipelines/tremor_pipeline.py,sha256=ihe6QeTk4MT_tXBE8PUN5rWNh2r5n5HCC1k4xyJjtUw,14761
12
+ paradigma/pipelines/pulse_rate_pipeline.py,sha256=aBDopwWvfabLCQM6De9PHNKKzL03xD_29jWcsElnjCw,17711
13
+ paradigma/pipelines/pulse_rate_utils.py,sha256=rlXze04meLFlyPaxMBYhvz3_vu3SM77RF-7mLPegTm0,26772
14
+ paradigma/pipelines/tremor_pipeline.py,sha256=qsKEV3QFPQ4bsTGdEX0nXHVMjVlBUpcEHbWEDcGsmVw,14758
15
15
  paradigma/preprocessing.py,sha256=OcrwiyNjZpw41IKCf9QRY75A-532kU4gSSSXjqWuTeE,14556
16
16
  paradigma/segmenting.py,sha256=hgT4dtg23eyvjUraEXCzX8u0kSRx4vArjQgF10r61P8,13909
17
- paradigma/testing.py,sha256=Ni68clfyHz_mQWBD8cDmls_uadd0rpKPq3-IKzolDZc,18547
17
+ paradigma/testing.py,sha256=zWPBj7Q1Td6rgeMGoAWi6rIVLB8M6_FNUxlZSbpWqEM,18547
18
18
  paradigma/util.py,sha256=E1keTX7vMDowSUG1AGx3juUMIXD7znaGwuqWDfQTpXo,16424
19
- paradigma-1.0.0.dist-info/LICENSE,sha256=Lda8kIVC2kbmlSeYaUWwUwV75Q-q31idYvo18HUTfiw,9807
20
- paradigma-1.0.0.dist-info/METADATA,sha256=rnOYOPZkoDVOMF0z9dDLfEulaCspPPjmGWjDtBgUDPI,11654
21
- paradigma-1.0.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
22
- paradigma-1.0.0.dist-info/RECORD,,
19
+ paradigma-1.0.2.dist-info/LICENSE,sha256=Lda8kIVC2kbmlSeYaUWwUwV75Q-q31idYvo18HUTfiw,9807
20
+ paradigma-1.0.2.dist-info/METADATA,sha256=XkUFT1LRgM2taKEqno1cZQl3c-KuCxJJd4kIUOckfE8,11654
21
+ paradigma-1.0.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
22
+ paradigma-1.0.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.2
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any