Skip to content

Frequency-domain Metrics

biosigpy.hrv.fdmetrics

Frequency-domain heart-rate variability metrics.

FdMetricsResult

Bases: NamedTuple

Conventional single-spectrum frequency-domain metrics.

Source code in src/biosigpy/hrv/fdmetrics.py
22
23
24
25
26
27
28
class FdMetricsResult(NamedTuple):
    """Conventional single-spectrum frequency-domain metrics."""

    hf: float
    lf: float
    lfn: float
    lfhf: float

SeparatedFdMetricsResult

Bases: NamedTuple

Frequency-domain metrics from OSP-separated spectra.

Source code in src/biosigpy/hrv/fdmetrics.py
31
32
33
34
35
36
class SeparatedFdMetricsResult(NamedTuple):
    """Frequency-domain metrics from OSP-separated spectra."""

    urlf: float
    re: float
    r: float

FdMetricsWarning

Bases: UserWarning

Structured Biosiglib diagnostic emitted by :func:fdmetrics.

Attributes:

Name Type Description
warning_id str

Canonical Biosiglib warning identifier.

affected_ids tuple of str

Complete set of affected canonical input or output identifiers.

Source code in src/biosigpy/hrv/fdmetrics.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class FdMetricsWarning(UserWarning):
    """Structured Biosiglib diagnostic emitted by :func:`fdmetrics`.

    Attributes
    ----------
    warning_id : str
        Canonical Biosiglib warning identifier.
    affected_ids : tuple of str
        Complete set of affected canonical input or output identifiers.
    """

    def __init__(self, warning_id: str, affected_ids: tuple[str, ...]) -> None:
        self.warning_id = warning_id
        self.affected_ids = affected_ids
        affected = ", ".join(affected_ids)
        super().__init__(f"{warning_id}; affected_ids: {affected}")

fdmetrics

fdmetrics(pxx: ArrayLike | None = None, f: ArrayLike | None = None, limit_hf: bool = True, *, related_pxx: ArrayLike | None = None, unrelated_pxx: ArrayLike | None = None) -> FdMetricsResult | SeparatedFdMetricsResult

Calculate frequency-domain HRV metrics on a supplied frequency grid.

Use pxx and f for conventional LF/HF metrics. For spectra produced after respiration-related OSP decomposition, omit pxx and provide related_pxx and unrelated_pxx together with f.

Parameters:

Name Type Description Default
pxx array_like

Nonnegative single-spectrum power spectral density. NaN values produce an all-NaN single-spectrum result.

None
f array_like

Finite, nonnegative, strictly increasing frequency samples in hertz.

None
limit_hf bool

Limit the high-frequency band to the first sample at or above 0.4 Hz. This option applies only to single-spectrum mode.

True
related_pxx array_like

Nonnegative respiration-related OSP spectrum.

None
unrelated_pxx array_like

Nonnegative respiration-unrelated OSP spectrum.

None

Returns:

Type Description
FdMetricsResult or SeparatedFdMetricsResult

Named, unpackable metrics for the selected call mode.

Warns:

Type Description
FdMetricsWarning

Emits the canonical excessive_vlf_power or zero_required_power diagnostic. The warning object exposes its canonical warning_id and complete affected_ids tuple.

Raises:

Type Description
TypeError

If numeric inputs or limit_hf have invalid types.

ValueError

If call modes are mixed, vectors have invalid values or shapes, or spectrum lengths do not match the frequency grid.

Examples:

>>> result = fdmetrics([1, 1, 1], [0.04, 0.15, 0.4])
>>> result.lf, result.hf
(0.10999999999999999, 0.25)
>>> separated = fdmetrics(
...     f=[0.04, 0.15, 0.4],
...     related_pxx=[0.01, 0.01, 0.01],
...     unrelated_pxx=[0.001, 0.001, 0.001],
... )
>>> round(separated.r, 6)
0.02965
Source code in src/biosigpy/hrv/fdmetrics.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def fdmetrics(
    pxx: ArrayLike | None = None,
    f: ArrayLike | None = None,
    limit_hf: bool = True,
    *,
    related_pxx: ArrayLike | None = None,
    unrelated_pxx: ArrayLike | None = None,
) -> FdMetricsResult | SeparatedFdMetricsResult:
    """Calculate frequency-domain HRV metrics on a supplied frequency grid.

    Use ``pxx`` and ``f`` for conventional LF/HF metrics. For spectra produced
    after respiration-related OSP decomposition, omit ``pxx`` and provide
    ``related_pxx`` and ``unrelated_pxx`` together with ``f``.

    Parameters
    ----------
    pxx : array_like, optional
        Nonnegative single-spectrum power spectral density. NaN values produce
        an all-NaN single-spectrum result.
    f : array_like
        Finite, nonnegative, strictly increasing frequency samples in hertz.
    limit_hf : bool, default=True
        Limit the high-frequency band to the first sample at or above 0.4 Hz.
        This option applies only to single-spectrum mode.
    related_pxx : array_like, optional
        Nonnegative respiration-related OSP spectrum.
    unrelated_pxx : array_like, optional
        Nonnegative respiration-unrelated OSP spectrum.

    Returns
    -------
    FdMetricsResult or SeparatedFdMetricsResult
        Named, unpackable metrics for the selected call mode.

    Warns
    -----
    FdMetricsWarning
        Emits the canonical ``excessive_vlf_power`` or
        ``zero_required_power`` diagnostic. The warning object exposes its
        canonical ``warning_id`` and complete ``affected_ids`` tuple.

    Raises
    ------
    TypeError
        If numeric inputs or ``limit_hf`` have invalid types.
    ValueError
        If call modes are mixed, vectors have invalid values or shapes, or
        spectrum lengths do not match the frequency grid.

    Examples
    --------
    >>> result = fdmetrics([1, 1, 1], [0.04, 0.15, 0.4])
    >>> result.lf, result.hf
    (0.10999999999999999, 0.25)
    >>> separated = fdmetrics(
    ...     f=[0.04, 0.15, 0.4],
    ...     related_pxx=[0.01, 0.01, 0.01],
    ...     unrelated_pxx=[0.001, 0.001, 0.001],
    ... )
    >>> round(separated.r, 6)
    0.02965
    """

    if not isinstance(limit_hf, (bool, np.bool_)):
        raise TypeError("limit_hf must be a boolean")

    single_mode = (
        pxx is not None and related_pxx is None and unrelated_pxx is None
    )
    separated_mode = (
        pxx is None and related_pxx is not None and unrelated_pxx is not None
    )
    if not single_mode and not separated_mode:
        raise ValueError(
            "provide either pxx or both related_pxx and unrelated_pxx"
        )
    if separated_mode and not bool(limit_hf):
        raise ValueError("limit_hf is not available in separated mode")

    frequencies = as_real_vector(f, name="f")
    _validate_frequencies(frequencies)

    if single_mode:
        spectrum = _validate_spectrum(pxx, name="pxx")
        _require_matching_length(spectrum, frequencies, name="pxx")
        if np.any(np.isnan(spectrum)):
            return _nan_single_result()
        return _single_metrics(spectrum, frequencies, bool(limit_hf))

    related = _validate_spectrum(related_pxx, name="related_pxx")
    unrelated = _validate_spectrum(unrelated_pxx, name="unrelated_pxx")
    _require_matching_length(related, frequencies, name="related_pxx")
    _require_matching_length(unrelated, frequencies, name="unrelated_pxx")
    if np.any(np.isnan(related)) or np.any(np.isnan(unrelated)):
        return _nan_separated_result()
    return _separated_metrics(related, unrelated, frequencies)

View executable example