Source code for gastropy.metrics.bands

"""Gastric frequency band definitions and band power computation."""

from dataclasses import dataclass

import numpy as np


[docs] @dataclass(frozen=True) class GastricBand: """A named gastric frequency band. Parameters ---------- name : str Human-readable band name. f_lo : float Lower frequency bound in Hz. f_hi : float Upper frequency bound in Hz. """ name: str f_lo: float f_hi: float @property def cpm_lo(self): """Lower bound in cycles per minute.""" return self.f_lo * 60.0 @property def cpm_hi(self): """Upper bound in cycles per minute.""" return self.f_hi * 60.0
BRADYGASTRIA = GastricBand("brady", 0.02, 0.03) """Bradygastria band: 1-2 cycles per minute (0.02-0.03 Hz).""" NORMOGASTRIA = GastricBand("normo", 0.03333, 0.06666) """Normogastria band: 2-4 cycles per minute (0.033-0.067 Hz).""" TACHYGASTRIA = GastricBand("tachy", 0.07, 0.17) """Tachygastria band: 4-10 cycles per minute (0.07-0.17 Hz).""" GASTRIC_BANDS = [BRADYGASTRIA, NORMOGASTRIA, TACHYGASTRIA] """All three standard gastric frequency bands."""
[docs] def band_power(freqs, psd, band, total_range=(0.01, 0.2)): """Compute power metrics for a specific frequency band. Parameters ---------- freqs : array_like Frequency values in Hz (from PSD computation). psd : array_like Power spectral density values. band : GastricBand Frequency band to analyze. total_range : tuple of float, optional ``(f_lo, f_hi)`` defining the total frequency range for proportion and ratio calculations. Default is ``(0.01, 0.2)``. Returns ------- dict Dictionary with keys: - ``peak_freq_hz`` : Peak frequency in the band (Hz). - ``max_power`` : Maximum power in the band. - ``mean_power`` : Mean power in the band. - ``prop_power`` : Proportion of total power in the band. - ``mean_power_ratio`` : Ratio of band mean to total mean power. All values are NaN if the band has no frequency coverage. Examples -------- >>> from gastropy.signal import psd_welch >>> from gastropy.metrics import band_power, NORMOGASTRIA >>> freqs, psd = psd_welch(signal, sfreq=10.0, fmin=0.01, fmax=0.2) >>> info = band_power(freqs, psd, NORMOGASTRIA) >>> info["peak_freq_hz"] 0.05 """ freqs = np.asarray(freqs, dtype=float) psd = np.asarray(psd, dtype=float) nan_result = { "peak_freq_hz": np.nan, "max_power": np.nan, "mean_power": np.nan, "prop_power": np.nan, "mean_power_ratio": np.nan, } mask_total = (freqs >= total_range[0]) & (freqs <= total_range[1]) if not np.any(mask_total): return nan_result mask_band = (freqs >= band.f_lo) & (freqs <= band.f_hi) if not np.any(mask_band): return nan_result psd_band = psd[mask_band] freqs_band = freqs[mask_band] psd_total = psd[mask_total] i_max = int(np.argmax(psd_band)) peak_freq = float(freqs_band[i_max]) max_power = float(psd_band[i_max]) mean_power = float(np.mean(psd_band)) total_sum = float(np.sum(psd_total)) prop_power = float(np.sum(psd_band)) / total_sum if total_sum > 0 else np.nan total_mean = float(np.mean(psd_total)) mean_ratio = mean_power / total_mean if total_mean > 0 else np.nan return { "peak_freq_hz": peak_freq, "max_power": max_power, "mean_power": mean_power, "prop_power": prop_power, "mean_power_ratio": mean_ratio, }