Skip to main content

ECGEN-Eval logo

ECGEN-Eval

Evaluation metrics and interactive HTML reports for synthetic ECG quality assessment. Part of the ECGEN research suite (ECGEN-FM, ECGEN-VAE, Pulse2Pulse).

📖 Documentation: https://vajira.info/ECGEN-Eval/

Installation

Install the latest release from PyPI:

pip install ecgen-eval

Optional extras:

pip install "ecgen-eval[features]"        # clinical feature extraction (neurokit2 + scipy)
pip install "ecgen-eval[fed]"             # Fréchet ECG Distance (torch, transformers, huggingface_hub)
pip install "ecgen-eval[features,fed]"    # both

Other optional packages:

pip install wfdb           # for loading PTB-XL / WFDB records
pip install scipy          # for Welch PSD, resampling, and R-peak detection (recommended)

To work on ECGEN-Eval itself, install from source instead; see the contributing guide.

Quick start

import numpy as np
from ecgen_eval import load_ecg_npy, AVAILABLE_METRICS

# Load model outputs (shape: N × L × T)
real  = load_ecg_npy("real_ecgs.npy",  label="PTB-XL")
synth = load_ecg_npy("synth_ecgs.npy", label="ECGEN-FM")

# Compute a metric
mmd = AVAILABLE_METRICS["mmd"]()
result = mmd.compute(real.data, synth.data, lead_names=real.lead_names)
print(f"MMD = {result.score:.6f}")
print(result.per_lead_scores)

Data loaders

Function Input Use case
load_ecg_npy(path) .npy / .npz (N, L, T) ECGEN-FM, ECGEN-VAE, Pulse2Pulse outputs
load_ecg_wfdb(dir) WFDB .hea/.dat records PTB-XL and PhysioNet datasets
load_ecg_folder(path) auto-detected Any of the above + CSV

load_ecg_folder auto-detects the format based on file extensions.

Metrics

All metrics are available via AVAILABLE_METRICS["key"]() and return a MetricResult with .score, .per_lead_scores, and .extra.

Original metrics

Key Name Lower / Higher Paper
mmd Maximum Mean Discrepancy Lower ↓ Gretton et al., JMLR 2012
dtw Dynamic Time Warping Lower ↓ Berndt & Clifford, KDD 1994
prd Percent Root-mean-square Difference Lower ↓ Zigel et al., IEEE TBME 2000
psd PSD Divergence (Jensen-Shannon) Lower ↓ Welch, IEEE Trans Audio 1967 · Applied: Golany & Radinsky, AAAI 2019
fd Fréchet Distance Lower ↓ Heusel et al., NeurIPS 2017 · Applied: Thambawita et al., Sci Rep 2021

New metrics (literature review 2018 – 2025)

Key Name Lower / Higher Paper
psnr Peak Signal-to-Noise Ratio Higher ↑ Huynh-Thu & Ghanbari, Electron Lett 2008
ssim Structural Similarity Index (1-D) Higher ↑ Wang et al., IEEE TIP 2004 · ECG: Hayn et al., Physiol Meas 2018
swd Sliced Wasserstein Distance Lower ↓ Rabin et al., LNCS 2011 · Kolouri et al., NeurIPS 2019
mae Mean Absolute Error (nearest-neighbour) Lower ↓ Zigel et al., IEEE TBME 2000
hrv Heart Rate Variability (SDNN/rMSSD/pNN50) Lower ↓ Task Force ESC/NASPE, Circulation 1996
pr_dist Distribution Precision & Recall Higher ↑ Kynkäänniemi et al., NeurIPS 2019
sqi Signal Quality Index (QRS template) Higher ↑ Clifford et al., CinC 2017
nn_dist Nearest-Neighbour Distance (memorisation) Lower ↓ Meehan et al., AISTATS 2020
heart_rate Heart Rate Distribution (JS divergence) Lower ↓ Pan & Tompkins, IEEE TBME 1985 · Applied: Golany & Radinsky, AAAI 2019
spectral_entropy Spectral Entropy Divergence Lower ↓ Inouye et al., EEG Clin Neurophysiol 1991

Feature Extraction

Extract 10 clinical ECG features per recording and compare their distributions between real and synthetic datasets.

from ecgen_eval import load_ecg_npy
from ecgen_eval.features import extract_features

real  = load_ecg_npy("real_ecgs.npy",  label="PTB-XL")
synth = load_ecg_npy("synth_ecgs.npy", label="ECGEN-FM")

# Returns (beat_df, summary_df)
_, real_feat  = extract_features(real)
_, synth_feat = extract_features(synth)

print(real_feat[["rr_mean_ms", "hr_bpm", "qrs_duration_ms",
                  "qt_interval_ms", "hrv_sdnn_ms"]].describe())

Extracted features (one value per recording):

Feature Column Description
RR interval rr_mean_ms, rr_std_ms Mean and std of peak-to-peak intervals
Heart rate hr_bpm 60 000 / mean(RR)
PR interval pr_interval_ms P-onset to QRS-onset
QRS duration qrs_duration_ms Width of QRS complex
QT interval qt_interval_ms QRS-onset to T-end
QTc (Bazett) qtc_bazett_ms QT / √RR(s)
ST deviation st_deviation_mv Mean ST voltage vs isoelectric line
T-wave amplitude t_amplitude_mv Signed T-peak amplitude
P-wave duration p_wave_duration_ms P-onset to P-offset
HRV hrv_sdnn_ms, hrv_rmssd_ms, hrv_pnn50 SDNN, RMSSD, pNN50

2D and 3D distribution plots

from ecgen_eval.visualization.feature_plots import (
    plot_feature_2d,
    plot_feature_pair_grid,
    plot_feature_3d,
    plot_feature_triple_grid,
)

# Single 2D scatter + KDE contour
fig = plot_feature_2d(real_feat, synth_feat, "rr_mean_ms", "hr_bpm")
fig.show()

# Grid of default feature pairs
fig = plot_feature_pair_grid(real_feat, synth_feat)
fig.show()

# Interactive 3D scatter
fig = plot_feature_3d(real_feat, synth_feat, "rr_mean_ms", "hr_bpm", "qrs_duration_ms")
fig.show()

CLI

# Run all metrics (except fed) and generate interactive HTML report
ecgen-eval eval \
  --real   /path/to/real_ecgs.npy \
  --synthetic /path/to/model1_ecgs.npy \
  --synthetic /path/to/model2_ecgs.npy \
  --output report.html

# Include clinical feature distributions in the report (requires neurokit2)
ecgen-eval eval \
  --real /path/to/real_ecgs.npy \
  --synthetic /path/to/synth_ecgs.npy \
  --feature-plots \
  --output report.html

# Extract features only — save summary CSV
ecgen-eval features \
  --real /path/to/real_ecgs.npy \
  --synthetic /path/to/synth_ecgs.npy \
  --output features.csv

# List available metrics
ecgen-eval list-metrics

# ECG waveform comparison only (no metrics)
ecgen-eval visualize \
  --real /path/to/real.npy \
  --synthetic /path/to/synth.npy \
  --compare-mode overlay

Key options for eval:

  • -m mmd -m prd -m psd — run a subset of metrics (repeat -m per metric; default: all except fed)
  • --feature-plots — add clinical feature extraction + 2D/3D distribution plots
  • --fs 500 — sampling frequency (also passed to psd, spectral_entropy, hrv, heart_rate)
  • --n-samples 5 — ECG samples shown in gallery
  • --offline — embed Plotly.js for offline viewing
  • --max-samples 500 — cap loaded recordings per folder

Report

The HTML report includes:

  • Dataset summary table
  • Interactive ECG gallery (overlay / side-by-side / grid)
  • Per-metric: score table, per-lead bar chart, PSD overlay (PSD metric), violin plot (DTW)
  • Radar chart summarising all metrics
  • Metrics summary table with Download CSV button
  • Clinical Feature Summary table (mean ± std, real vs synthetic) — with --feature-plots
  • 2D feature distribution plots (scatter + KDE contours) — with --feature-plots
  • 3D feature distribution plots (interactive rotation) — with --feature-plots
  • References / citations

Running tests

pytest tests/ -v

Project structure

ecgen_eval/
├── data/
│   ├── dataset.py          ECGDataset container
│   └── loader.py           npy / WFDB / CSV loaders
├── features/               Clinical ECG feature extraction (optional: neurokit2)
│   ├── __init__.py         Exports FeatureExtractor, extract_features
│   ├── extractor.py        FeatureExtractor class → (beat_df, summary_df)
│   ├── qrs_detection.py    R-peak detection (neurokit2 primary, scipy fallback)
│   ├── interval_features.py  RR, HR, PR, QRS, QT, QTc, P-wave duration
│   ├── amplitude_features.py ST deviation, T-wave amplitude
│   ├── hrv_features.py     SDNN, RMSSD, pNN50
│   └── utils.py            Bandpass filter, peak helpers
├── metrics/
│   ├── base.py             BaseMetric + MetricResult
│   ├── mmd.py              MMD
│   ├── dtw.py              DTW
│   ├── prd.py              PRD
│   ├── psd.py              PSD divergence
│   ├── fd.py               Fréchet Distance
│   ├── psnr.py             PSNR
│   ├── ssim.py             1-D SSIM
│   ├── swd.py              Sliced Wasserstein Distance
│   ├── mae.py              MAE / RMSE
│   ├── hrv.py              HRV statistics (SDNN, rMSSD, pNN50)
│   ├── pr_dist.py          Distribution Precision & Recall
│   ├── sqi.py              Signal Quality Index
│   ├── nn_dist.py          Nearest-Neighbour Distance
│   ├── heart_rate.py       Heart Rate Distribution
│   └── spectral_entropy.py Spectral Entropy Divergence
├── visualization/
│   ├── ecg_paper.py        ECG graph-paper figure factory
│   ├── ecg_waveform.py     Waveform comparison plots
│   ├── metric_plots.py     Bar, violin, PSD overlay, radar
│   └── feature_plots.py    2D/3D clinical feature distribution plots
├── report/
│   └── html_report.py      HTML report generator
└── cli.py                  Click CLI entry point (eval, visualize, features, list-metrics)
tests/
├── conftest.py
├── test_dataset.py
├── test_loader.py
├── test_metrics.py
└── test_features.py
docs/
└── ecg_features_review.md  Literature review of the 10 clinical features

Metadata

Release files for ecgen-eval 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ecgen-eval 0.6.0
File Size Uploaded Provenance Actions
ecgen_eval-0.6.0.tar.gz 3.3 MB With provenance Details for ecgen_eval-0.6.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ecgen-eval 0.6.0
File Interpreter ABI Platform Actions
ecgen_eval-0.6.0-py3-none-any.whl Python 3 none any Details for ecgen_eval-0.6.0-py3-none-any.whl

Total release size: 3.5 MB

Release files / ecgen_eval-0.6.0.tar.gz

Download URL ecgen_eval-0.6.0.tar.gz
Size 3.3 MB
Tags Source
SHA-256 checksum
How to use checksums
cfd472afe6b93f2b32c21cdcd3d0b1cf41265ed4c8fe27cf8b1c07b4cf56ea3b
BLAKE2b-256 checksum
How to use checksums
7a08792ef31d54e8c0b60e6cac25410ac12611ffb68d1bb4fa0876d596bd9686
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / ecgen_eval-0.6.0-py3-none-any.whl

Download URL ecgen_eval-0.6.0-py3-none-any.whl
Size 103.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dca9300f54a83de2ba5aa44778f9de38bceab8fec6a234d7c15e3194b748efb2
BLAKE2b-256 checksum
How to use checksums
011a3aa3a82094874d6f4cf1dca05377b2789f5de2bdafa422e5d4dc831ac62d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for ecgen-eval 0.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ecgen_eval-0.6.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page