diff --git a/README.md b/README.md index d81ca5a..ee0f632 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,7 @@ ecg_aligned_80hz = normalizer.transform(ecg) * `template`: A template ECG created with `create_template()` method. This template is used as a reference for aligning R-peaks in the ECG signals. -* `select_lead`: Specifies the lead (e.g., 'Lead II', 'Lead V1') for R-peak - and QRST point detection. Different leads can provide varying levels of +* `select_lead`: Specifies the lead (e.g., 'Lead II', 'Lead V1') for R-peak detection. Different leads can provide varying levels of clarity for these features. Selection via channel numbers 0,1,... . * `num_workers`: Determines the number of CPU cores to be utilized for @@ -74,6 +73,8 @@ ecg_aligned_80hz = normalizer.transform(ecg) * `median_beat`: Calculates the median from a set of aligned beats and returns a single, representative beat. +* `silent`: Disable all warnings. + ## Citation Please use the following citation: diff --git a/pyproject.toml b/pyproject.toml index 7568e9d..9b8efc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ requires = [ name='rlign' version='1.0' description='Beat normalization for ecg data.' +readme = "README.md" requires-python = ">=3.11" authors=[ {name="Lucas Bickmann"}, @@ -35,4 +36,4 @@ classifiers=[ ] [project.optional-dependencies] -docs = ["sphinx"] \ No newline at end of file +docs = ["sphinx"] diff --git a/rlign/rlign.py b/rlign/rlign.py index 72cee70..055786f 100644 --- a/rlign/rlign.py +++ b/rlign/rlign.py @@ -145,9 +145,6 @@ def _normalize_interval( # switch to defined resample method match scale_method: case 'linear': - # limit number of peaks - #source_rpeaks = source_rpeaks[:-1] - medians = [] # iterate over all peaks for idx, (source_st, source_fs) in enumerate(zip(source_rpeaks, source_rpeaks_intervals)): @@ -166,7 +163,6 @@ def _normalize_interval( target_fs ).transpose(1, 0) - if self.median_beat: if (template_starts[idx] - int(self.template.intervals[0] / 3) < 0 or template_starts[idx] + self.template.intervals[0] - int(self.template.intervals[0] / 3) > len( @@ -304,9 +300,7 @@ def _template_transform( return None, 1 try: - #hr = ecg_process(ecg_lead, sampling_rate=self.sampling_rate)[0]["ECG_Rate"].mean() - - # just some basic approximation + # just some basic heart rate approximation min_factor = 60 / (ecg.shape[1] / self.sampling_rate) hr = int(len(rpeaks) * min_factor) except: @@ -319,8 +313,7 @@ def _template_transform( return ecg, 1 - - # limit number of rpeaks to the targets + # limit number of R-peaks to the targets rpeaks = rpeaks[: len(self.template.rpeaks+1)] # get the interval lengths of found rpeaks diff --git a/rlign/utils.py b/rlign/utils.py index 7f426db..8e53dc8 100644 --- a/rlign/utils.py +++ b/rlign/utils.py @@ -7,7 +7,7 @@ import numpy as np import pandas as pd -from typing import Optional, Tuple +from typing import Tuple from scipy.signal import resample from sklearn.utils import check_array from neurokit2 import ecg_clean, ecg_peaks @@ -66,53 +66,52 @@ def find_rpeaks( ecg_lead: np.ndarray, sampling_rate: int, neurokit_method: str = "neurokit", - correct_artifacts: bool = True - ) -> Tuple[np.ndarray, dict]: - ''' - Internal function which calls neurokit for rpeak and qrs-complex computation. + correct_artifacts: bool = True) -> Tuple[np.ndarray, dict]: + """ + Internal function which calls neurokit for rpeak and qrs-complex computation. - Parameters: - ecg_lead: An array representing a single-lead ECG signal. The input is - expected to be a one-dimensional array of voltage values over time, representing - the electrical activity of the heart as captured by a specific ECG lead. - - sampling_rate: Defines the sampling rate for all ECG recordings. - - neurokit_method: Chooses the algorithm for R-peak detection from the - NeuroKit package. Different algorithms may offer varying performance - based on the ECG signal characteristics. - - correct_artifacts: If set to True, artifact correction is applied - exclusively for R-peak detections, enhancing the accuracy of peak - identification in noisy signals. - - Returns: - (rpeaks, qrs_epochs): A pair of elements (rpeaks, qrs_epochs) representing - the outcomes of the R-peak detection and QRS complex computation processes, - respectively. If an error occurs during the processing, the function - returns (None, None), indicating a failure in signal analysis. - ''' - try: - # clean the ecg as recommended by neurokit - data_ = ecg_clean( - ecg_lead, - sampling_rate=sampling_rate - ) - - # caluclate rpeaks - _, r_peaks = ecg_peaks( - data_, - sampling_rate=sampling_rate, - method=neurokit_method, - correct_artifacts=correct_artifacts - ) - rpeaks = r_peaks['ECG_R_Peaks'].astype(np.int32) - - except Exception as e: - logging.warning(f'Failure in neurokit: {e}\n') - return None, None - - return rpeaks + Parameters: + ecg_lead: An array representing a single-lead ECG signal. The input is + expected to be a one-dimensional array of voltage values over time, representing + the electrical activity of the heart as captured by a specific ECG lead. + + sampling_rate: Defines the sampling rate for all ECG recordings. + + neurokit_method: Chooses the algorithm for R-peak detection from the + NeuroKit package. Different algorithms may offer varying performance + based on the ECG signal characteristics. + + correct_artifacts: If set to True, artifact correction is applied + exclusively for R-peak detections, enhancing the accuracy of peak + identification in noisy signals. + + Returns: + (rpeaks, qrs_epochs): A pair of elements (rpeaks, qrs_epochs) representing + the outcomes of the R-peak detection and QRS complex computation processes, + respectively. If an error occurs during the processing, the function + returns (None, None), indicating a failure in signal analysis. + """ + try: + # clean the ecg as recommended by neurokit + data_ = ecg_clean( + ecg_lead, + sampling_rate=sampling_rate + ) + + # caluclate rpeaks + _, r_peaks = ecg_peaks( + data_, + sampling_rate=sampling_rate, + method=neurokit_method, + correct_artifacts=correct_artifacts + ) + rpeaks = r_peaks['ECG_R_Peaks'].astype(np.int32) + + except Exception as e: + logging.warning(f'Failure in neurokit: {e}\n') + return None, None + + return rpeaks def _resample_multichannel(xs, fs, fs_target): @@ -160,8 +159,6 @@ def _resample_signal(x, fs, fs_target): Note: The method have been modified from wfdbs resample_multichan and resample_sig. """ - #t = np.arange(x.shape[0]).astype("float64") - if fs == fs_target: return x @@ -181,4 +178,4 @@ def _check_3d_array(X): X = check_array(X, ensure_2d=False, allow_nd=True) if X.ndim != 3: raise ValueError(f"X must be 3-dimensional (got {X.ndim}).") - return X \ No newline at end of file + return X