Getting Started¶
This guide walks you through the core concepts and verified workflows of the AI ECG SDK (ecg_sdk).
1. Universal ECG Ingestion with load_ecg¶
The SDK provides a single, high-level factory function load_ecg that intelligently dispatches to PhysioNet databases, local files (CSV/WFDB), in-memory arrays/DataFrames, and synthetic generators.
from ecg_sdk import load_ecg, list_physionet_databases
# 1. Stream MIT-BIH Arrhythmia record from PhysioNet via URI
sig_mit = load_ecg("physionet:mitdb/100", start_time=0.0, duration=5.0)
print(f"PhysioNet MIT-BIH: {sig_mit}")
# 2. Stream St. Petersburg INCART 12-lead ECG from PhysioNet
sig_incart = load_ecg("I01", database="incartdb", duration=5.0)
print(f"INCART 12-Lead: {sig_incart}")
# 3. Generate on-the-fly synthetic ventricular ectopy (PVC)
sig_synth = load_ecg("synthetic:pvc", duration=6.0)
print(f"Synthetic Signal: {sig_synth}")
PhysioNet MIT-BIH: ECGSignal(record='100', leads=['MLII', 'V5'], fs=360.0Hz, duration=5.00s, shape=(1800, 2))
INCART 12-Lead: ECGSignal(record='I01', leads=['I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6'], fs=257.0Hz, duration=5.00s, shape=(1285, 12))
Synthetic Signal: ECGSignal(record='SYNTH_PVC', leads=['Lead_I', 'Lead_II'], fs=250.0Hz, duration=6.00s, shape=(1500, 2))
2. Ingestion Standardization (target_fs & normalize)¶
Different benchmark databases operate at heterogeneous sampling rates (MIT-BIH at \(360\text{ Hz}\), INCART at \(257\text{ Hz}\), PTB-XL at \(500\text{ Hz}\), SVDB at \(128\text{ Hz}\)).
To prepare cross-dataset records for neural networks or feature extractors, load_ecg provides automatic sampling frequency standardization (target_fs) and amplitude normalization (normalize) at load time. Ground-truth cardiologist annotations (r_peaks) are automatically scaled proportionally!
from ecg_sdk import load_ecg
# 1. Stream MIT-BIH (native 360 Hz) and standardize to 250 Hz with Z-Score normalization
sig_mit_std = load_ecg(
"physionet:mitdb/100",
start_time=0.0,
duration=5.0,
target_fs=250.0, # Automatically resamples from 360 Hz to 250 Hz & scales R-peak annotations!
normalize="zscore", # Centers each lead to mean=0, std=1
load_annotations=True
)
print(f"Standardized MIT-BIH (360Hz -> 250Hz, Z-Score): {sig_mit_std}")
ann = sig_mit_std.metadata.get("annotations", {})
print(f"Proportionally Scaled R-Peaks: {len(ann.get('r_peaks', []))} beats (Indices: {ann.get('r_peaks', [])[:4]})")
# 2. Stream INCART 12-lead (native 257 Hz) standardized to 250 Hz
sig_incart_std = load_ecg("I01", database="incartdb", duration=5.0, target_fs=250.0)
print(f"Standardized INCART (257Hz -> 250Hz): {sig_incart_std}")
Standardized MIT-BIH (360Hz -> 250Hz, Z-Score): ECGSignal(record='100', leads=['MLII', 'V5'], fs=250.0Hz, duration=5.00s, shape=(1250, 2))
Proportionally Scaled R-Peaks: 7 beats (Indices: [ 12 53 257 460])
Standardized INCART (257Hz -> 250Hz): ECGSignal(record='I01', leads=['I', 'II', 'III', 'aVR', 'aVL', 'aVF', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6'], fs=250.0Hz, duration=5.00s, shape=(1250, 12))
3. Exploring Supported PhysioNet Databases¶
Inspect available datasets and standard MIT-BIH benchmark records directly from Python:
from ecg_sdk import list_physionet_databases, list_mitbih_records
# View available PhysioNet databases
dbs = list_physionet_databases()
print("Available Benchmark Databases:")
for db_id, info in list(dbs.items())[:5]:
print(f" • {db_id:<10}: {info['name']} (fs={info.get('nominal_fs', 'N/A')} Hz)")
# List all 48 MIT-BIH records
records = list_mitbih_records()
print(f"\nTotal MIT-BIH Records: {len(records)} ({records[:8]}...)")
Available Benchmark Databases:
• mitdb : MIT-BIH Arrhythmia Database (fs=360.0 Hz)
• incartdb : St. Petersburg INCART 12-lead Arrhythmia Database (fs=257.0 Hz)
• ptb-xl : PTB-XL 12-Lead Diagnostic ECG Database (fs=500.0 Hz)
• nstdb : MIT-BIH Noise Stress Test Database (fs=360.0 Hz)
• afdb : MIT-BIH Atrial Fibrillation Database (fs=250.0 Hz)
Total MIT-BIH Records: 48 (['100', '101', '102', '103', '104', '105', '106', '107']...)
4. Clinical ECG Paper Plotting ("paper" vs "digital" Modes)¶
The SDK provides authentic clinical ECG paper plotting with 1:1 physical millimeter scale as default (display_mode="paper"), and an expanded screen-optimized mode (display_mode="digital"):
- Paper Mode (
display_mode="paper"— Default): 1:1 physical metric scale (\(25\text{ mm/s}, 10\text{ mm/mV}\)) formatted to standard thermal rolls (\(50\text{ mm}\) channel width) or US Letter/A4 diagnostic sheets. - Digital Mode (
display_mode="digital"): Enlarged responsive layout tailored for monitors and Jupyter notebooks. - Sparse Tick Formatting (
sparse_ticks=True): Unclutters the axes with clean labels (e.g.time_tick_step=1.0s,voltage_tick_step=0.5mV) while retaining the full \(100\%\) millimeter grid.
from ecg_sdk import load_ecg
import matplotlib.pyplot as plt
# Ingest MIT-BIH Record 101 in physical mV standardized to 250 Hz
sig_101 = load_ecg("physionet:mitdb/101", duration=6.0, target_fs=250.0, load_annotations=True)
ann = sig_101.metadata.get("annotations", {})
# Plot on 1:1 physical ECG paper with sparse tick labels
fig, ax = sig_101.plot_clinical(
lead="MLII",
display_mode="paper",
sparse_ticks=True,
time_tick_step=1.0,
voltage_tick_step=0.5,
grid_style="clinical_pink",
r_peaks=ann.get("r_peaks"),
show_calibration_pulse=True,
title="PHYSIONET MIT-BIH RECORD 101 — 1:1 PHYSICAL PAPER MODE"
)
plt.show()

Loading Ventricular Ectopy (PVCs) on Clinical Red Paper¶
# Load MIT-BIH Record 106 with frequent Premature Ventricular Contractions
sig_106 = load_ecg("physionet:mitdb/106", start_time=10.0, duration=6.0, target_fs=250.0, load_annotations=True)
ann_106 = sig_106.metadata.get("annotations", {})
fig, ax = sig_106.plot_clinical(
lead="MLII",
display_mode="paper",
grid_style="clinical_red",
r_peaks=ann_106.get("r_peaks"),
show_calibration_pulse=True,
title="PHYSIONET MIT-BIH RECORD 106 (VENTRICULAR ECTOPY & PVCS)"
)
plt.show()

5. Saving and Loading CSV Records¶
Export and reload signals losslessly with comma-separated values:
from ecg_sdk import generate_synthetic_ecg, save_to_csv, load_csv_record
# Generate synthetic recording
sig = generate_synthetic_ecg(duration=4.0, sampling_rate=200.0)
# Save to disk
save_to_csv(sig, "patient_01.csv")
# Load back
loaded = load_csv_record("patient_01.csv", sampling_rate=200.0)
print(f"Loaded: {loaded}")