Quickstart¶
This page takes one 10-second, 12-lead recording from a NumPy file to a stored,
queried and plotted ECG Record. It assumes pip install "ecg-records[viz,table]".
A runnable notebook version is quickstart.ipynb.
1. Load a signal¶
ecg-records takes a channel-major array, one row per lead, with an
explicit name for every row. Amplitudes default to millivolts.
import numpy as np
signal = np.load("ecg.npy") # shape (12, 5000): 12 leads, 10 s at 500 Hz
leads = ["I", "II", "III", "aVR", "aVL", "aVF", "V1", "V2", "V3", "V4", "V5", "V6"]
print(signal.shape, signal.dtype)
If your data is sample-major, shaped (n_samples, n_leads) (common in CSV
files and in wfdb), transpose it with signal.T.
Preparing input signals covers units, lead order, WFDB
files and fewer than 12 leads.
Files and the bundled examples skip this step: read_signal and
load_example return a SignalData that carries its sampling rate, lead names
and unit, and ecg_record accepts it directly.
import ecgfeat
example = ecgfeat.load_example("sinus") # PTB-XL record, CC BY 4.0
from_file = ecgfeat.read_signal("ecg.npz", sampling_rate=500) # WFDB, EDF, CSV, NumPy
print(example) # SignalData(12 leads [...], 10 s @ 500 Hz, mV)
2. Measure¶
from ecgfeat import ecg_record
record = ecg_record(signal, sampling_rate=500, lead_names=leads)
print(record.record_id) # rec-5f0b3c...: derived from the signal and settings
print(record.schema_version) # 1.1.0
print(record.profile) # summary
print(len(record.axes["beats"]), "beats")
ecg_record returns an immutable, already validated
ECGRecord. With the same input and settings, the
record, and its record_id, are identical on every run.
3. Read values¶
from ecgfeat import query_measurement
hr = query_measurement(record, "heart_rate_bpm")
print(hr.value, hr.unit) # 60 1/min
qrs = query_measurement(record, "qrs_duration_ms", lead="V1", beat=3)
print(qrs.value, qrs.unit) # e.g. 160 ms
print(qrs.absence) # None: the value was measured
print(qrs.validation.tier) # unvalidated
print(qrs.address) # ecg-record:rec-…@1.1.0#/measurements/intervals/qrs_duration_ms/values/3/6
Per-beat, per-lead measurements need a lead and a beat (a zero-based index
into record.axes["beats"]). Record-level measurements such as the heart rate
take neither, and median-beat values per lead take only a lead. When a value
is missing, value is None and absence says why; see
Reading and querying records.
The record itself has shortcuts:
print(record.summary()) # global (record-level) rate, intervals, axes
print(record.query("qt_interval_ms")) # qt_interval_ms = 443 ms [record; validated]
print(record.query("median_r_amplitude_uv", lead="V5")) # ... µV [lead V5; unvalidated]
qt = record.get("qt_interval_ms") # the bare value (443) when that is all you need
table = record.to_dataframe("global") # pandas: value, unit, state, validation
print(table.loc["qrs_duration_ms", ["value", "unit", "validation"]].tolist())
4. Save and load¶
from ecgfeat import dumps_record, load_record
with open("ecg.record.json", "wb") as handle:
handle.write(dumps_record(record)) # canonical UTF-8 JSON bytes
restored = load_record("ecg.record.json") # strict validation by default
assert dumps_record(restored) == dumps_record(record)
Reading a record needs neither the signal nor the numerical engine. Anyone with the JSON file can query it.
5. Plot¶
import matplotlib
matplotlib.use("Agg") # headless; omit in a notebook
from ecgfeat.viz import plot_record
fig, axes = plot_record(signal, record, leads=["II", "V1", "V5"],
start_s=1.0, end_s=4.0, annotations="all")
fig.savefig("record.png", dpi=150)
The plot shows the raw signal with the record's P, QRS and T landmarks. The signal must be the exact array that produced the record (its SHA-256 is checked). The conventional 12-lead print is one call:
from ecgfeat.viz import plot_ecg
fig, ax = plot_ecg(signal, record) # 3 x 4 + rhythm strip, 25 mm/s, 10 mm/mV
fig.savefig("ecg.png", dpi=150)
See Plotting for median beats, ST, rhythm and quality views.
6. The same from a shell¶
ecg-record measure ecg.npy --sampling-rate 500 --lead-names standard_12 -o ecg.record.json
ecg-record measure ptbxl/records500/00000/00001_hr.hea -o rec.json # rate and leads from the file
ecg-record validate ecg.record.json
ecg-record summary ecg.record.json
ecg-record query ecg.record.json qrs_duration_ms --lead V1 --beat 3
ecg-record plot ptbxl/records500/00000/00001_hr.hea --kind paper -o ecg.png
See Command line for every subcommand, including parallel batch processing.
7. Interpretation (optional)¶
With pip install ecginterpret, analyze measures once and returns the record
and a rule-based Interpretation document; render_report prints it.
from ecginterpret import analyze, render_report
record, document = analyze(signal, sampling_rate=500, lead_names="standard_12")
print(render_report(document, record)) # also format="markdown" or "html"
Next steps¶
- The ECG Record: what each member means
- Measuring ECGs: profiles, the staged API, reuse
- Limitations and intended use: read this before relying on any value