Source code for biobss.ppgtools.ppg_freqdomain

from numpy.typing import ArrayLike

from biobss.common.signal_fft import *
from biobss.common.signal_psd import *

# Frequency domain features
FUNCTIONS_FREQ_SEGMENT = {
    "p_1": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 1),
    "f_1": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 1, loc=True),
    "p_2": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 2),
    "f_2": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 2, loc=True),
    "p_3": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 3),
    "f_3": lambda sigfft, freq, _0, _1: fft_peaks(sigfft, freq, 3, loc=True),
    "pow": lambda _0, _1, pxx, fxx: sig_power(pxx, fxx, [0, 2]),
    "rpow": lambda _0, _1, pxx, fxx: sig_power(pxx, fxx, [0, 2.25]) / sig_power(pxx, fxx, [0, 5]),
}


[docs]def ppg_freq_features( sig: ArrayLike, sampling_rate: float, input_types: list, fiducials: dict = None, prefix: str = "ppg" ) -> dict: """Calculates frequency-domain features Segment-based features: p_1: The amplitude of the first peak from the fft of the signal f_1: The frequency at which the first peak from the fft of the signal occurred p_2: The amplitude of the second peak from the fft of the signal f_2: The frequency at which the second peak from the fft of the signal occurred p_3: The amplitude of the third peak from the fft of the signal f_3: The frequency at which the third peak from the fft of the signal occurred pow: Power of the signal at a given range of frequencies ([0,2] Hz). rpow: Ratio of the powers of the signal at given ranges of frequencies ([0,2.25] Hz/[0,5] Hz). Args: sig (ArrayLike): Signal to be analyzed. sampling_rate (float): Sampling rate of the signal (Hz). input_types (list): Type of feature calculation, should be 'segment'. fiducials (dict, optional): Dictionary of fiducial point locations. Defaults to None. prefix (str, optional): Prefix for signal type. Defaults to 'ppg'. Raises: ValueError: If sampling rate is not greater than 0. ValueError: If type is not 'segment'. Returns: dict: Dictionary of calculated features """ if sampling_rate <= 0: raise ValueError("Sampling rate must be greater than 0.") input_types = [x.lower() for x in input_types] features_freq = {} for type in input_types: if type == "segment": freq, sigfft = sig_fft(sig=sig, sampling_rate=sampling_rate) f, pxx = sig_psd(sig=sig, sampling_rate=sampling_rate, method="welch") features_freq = {} for key, func in FUNCTIONS_FREQ_SEGMENT.items(): try: features_freq["_".join([prefix, key])] = func(sigfft, freq, pxx, f) except: features_freq["_".join([prefix, key])] = np.nan else: raise ValueError("Undefined type for frequency domain.") return features_freq