Skip to content

User Guide: Core Signal Processing & Abstraction

The ECGSignal Dataclass

The ECGSignal class is the central data representation in the AI ECG SDK. It provides a container for multi-channel biomedical biopotential signals.

Data Dimensions & Invariants

An ECGSignal guarantees that the underlying voltage array has shape:

\[\mathbf{X} \in \mathbb{R}^{N \times L}\]

where: - \(N\): Number of temporal samples (\(N = \text{round}(\text{duration} \times f_s)\)). - \(L\): Number of physical electrode leads / channels (\(L \ge 1\)).


Slicing & Resampling

Zero-Phase Fourier Resampling with Annotation Scaling

Resampling changes the discrete sampling rate \(f_{s,\text{orig}} \to f_{s,\text{target}}\) using frequency-domain zero-phase Fourier method via scipy.signal.resample.

Crucially, all companion ground-truth annotations (such as cardiologist R-peak sample indices \(r_i\)) are automatically scaled proportionally to preserve exact temporal alignment:

\[r_{i,\text{resampled}} = \text{round}\left(r_{i,\text{orig}} \times \frac{f_{s,\text{target}}}{f_{s,\text{orig}}}\right)\]
from ecg_sdk.core.generator import generate_synthetic_ecg

sig = generate_synthetic_ecg(duration=4.0, sampling_rate=250.0, lead_names=["I", "II"])
print(f"Original: {sig.n_samples} samples @ {sig.sampling_rate} Hz")

# Downsample from 250 Hz to 100 Hz
resampled = sig.resample(target_fs=100.0)
print(f"Resampled: {resampled.n_samples} samples @ {resampled.sampling_rate} Hz")
Original: 1000 samples @ 250.0 Hz
Resampled: 400 samples @ 100.0 Hz
\[\hat{N} = \left\lfloor N \cdot \frac{f_{s,\text{target}}}{f_{s,\text{orig}}} \right\rfloor\]

Normalization Methods

The SDK provides three normalization strategies:

1. Z-Score Standardization (method="zscore")

Centers each lead to zero mean and unit variance:

\[z(t) = \frac{x(t) - \mu}{\sigma + \epsilon}\]
norm_z = sig.normalize(method="zscore")
lead_i = norm_z.get_lead("I")
print(f"Mean: {lead_i.mean():.4f}, Std: {lead_i.std():.4f}")
Mean: 0.0000, Std: 1.0000

2. Min-Max Normalization (method="minmax")

Scales the voltage values into the range \([0, 1]\):

\[\tilde{x}(t) = \frac{x(t) - x_{\min}}{(x_{\max} - x_{\min}) + \epsilon}\]

3. Robust Scaling (method="robust")

Uses median and Interquartile Range (\(IQR = Q_{75} - Q_{25}\)) to prevent outlier distortion caused by motion artifacts or high-amplitude spikes:

\[x_{\text{robust}}(t) = \frac{x(t) - \text{median}(x)}{IQR + \epsilon}\]

Integrated Clinical Paper Plotting (signal.plot_clinical(...))

Every ECGSignal instance can be visualized directly with the .plot_clinical(...) method:

  • 1:1 Metric Scale by Default (display_mode="paper"): Formatted to standard medical thermal printer roll dimensions (\(25\text{ mm/s}, 10\text{ mm/mV}\)).
  • Screen Display Mode (display_mode="digital"): Expanded responsive layout for monitors and notebooks.
  • Sparse Ticks (sparse_ticks=True): Keeps axis numerical labels clean and uncluttered.
# Plot directly from the signal object
fig, ax = sig.plot_clinical(
    lead="I",
    display_mode="paper",
    sparse_ticks=True,
    time_tick_step=1.0,
    voltage_tick_step=0.5,
)