| Format | 12-lead · 10 s · 500 Hz |
|---|---|
| Patients | 161,352 |
| Records | 800,035 |
| Leads | 12 |
| License | PhysioNet DUA |
| Origin | Beth Israel Deaconess Medical Center — USA — Boston, MA |
800,035 twelve-lead ECGs from 161,352 patients at Beth Israel Deaconess Medical Center — the largest dataset in this catalogue by two orders of magnitude, about 96.5 GB of waveforms. Each record is a fixed 10 s at 500 Hz. Access is credentialed under the PhysioNet DUA.
Do not confuse it with MIMIC-IV-ECG Demo, the open 659-record sample:
that one ships identifiers only, while this release adds
machine_measurements.csv and so is the version that actually has labels.
They are separate ECGBench configs, mimic_iv_ecg and mimic_iv_ecg_demo.
The stored lead order transposes aVF and aVL — I, II, III, aVR, aVF,
aVL, V1–V6, identical in all 3,000 headers sampled. signal[4] is aVF
here and aVL in every other 12-lead dataset in ECGBench, so a model trained
across datasets by index silently crosses two leads. leads=[...] selects
by name and fixes it.
Patient grouping is not optional at this scale. 92.8% of all studies
come from patients who contributed more than one, and a single patient
contributed 260. Folds are grouped by subject_id.
Labels are the ECG cart’s own free-text report — up to 18 lines per study — plus nine interval and axis measurements. They are machine output, not an adjudicated cardiologist reading, and they come with two traps documented below: the first report line is not always a rhythm, and the numeric measurements encode “not measurable” as integer sentinels rather than as missing values.
| Studies | Patients | % of patients | Studies contributed |
|---|---|---|---|
| 1 | 57,344 | 35.5% | 57,344 |
| 2 | 29,218 | 18.1% | 58,436 |
| 3–4 | 28,666 | 17.8% | 97,249 |
| 5–9 | 25,322 | 15.7% | 164,799 |
| 10–19 | 13,700 | 8.5% | 183,141 |
| 20–49 | 6,263 | 3.9% | 179,452 |
| 50–259 | 838 | 0.5% | 59,354 |
| 260 | 1 | 0.0% | 260 |
| **total** | **161,352** | **800,035** |
| First line of the machine report | Records | Share |
|---|---|---|
| sinus rhythm | 381,884 | 47.7% |
| sinus bradycardia | 71,034 | 8.9% |
| sinus tachycardia | 54,665 | 6.8% |
| atrial fibrillation | 41,209 | 5.2% |
| sinus rhythm with borderline 1st degree a-v block | 15,428 | 1.9% |
| atrial fibrillation with rapid ventricular response | 14,925 | 1.9% |
| sinus rhythm with 1st degree a-v block | 14,788 | 1.8% |
| sinus arrhythmia | 13,764 | 1.7% |
| sinus rhythm with pac(s) | 10,539 | 1.3% |
| *** consider acute st elevation mi *** | 8,516 | 1.1% |
| ventricular pacing | 8,445 | 1.1% |
| --- warning: data quality may affect interpretation --- | 7,079 | 0.9% |
| (938 further values) | 157,758 | 19.7% |
Recomputed from the shipped record_list.csv and machine_measurements.csv.
The catalogue previously carried the rounded figures “~800,000” and
“~160,000”; the exact counts are 800,035 records and 161,352
patients, and both are confirmed twice over — record_list.csv and
machine_measurements.csv hold exactly the same 800,035 study_ids, and
SHA256SUMS.txt accounts for exactly 800,035 .dat and 800,035 .hea
files. The MIMIC-IV-ECG-Ext-ICD derivative independently covers the same
800,035 studies and 161,352 subjects.
Verify your own copy of machine_measurements.csv. It is the file every
label comes from, and the release publishes its SHA-256 in
SHA256SUMS.txt:
56f6b1413221bce95bd6f48b28ca1acf27ae0b073d6f2c1d12f3af7500eabbb6, for
800,035 rows — one per study. A filtered copy under the same name produces
records with no labels and figures that do not match this page; ECGBench
logs a warning naming the shortfall when the file does not cover every
study.
The report table above is the first line only, and it is not a rhythm
label. It is report_0, normalised (lower-cased, whitespace collapsed,
trailing period dropped — which is what turns 1,571 raw values into 950).
That line is usually a rhythm statement, but 7,079 records lead with a
data-quality warning, 4,980 with a note that age was not entered, and 8,516
with a finding rather than a rhythm. Exactly one record of 800,035 has no
report text at all. Train on report_text — the populated lines joined —
not on primary_report.
The report uses the cart’s vocabulary, not clinical prose. Keyword
extraction over report_text has to match what the machine actually wrote:
infarct appears in 179,588 records while myocardial infarction
appears in 211, and hypertrophy in 69,282 against left ventricular
hypertrophy in 50,748. Searching for the clinical phrase suggests this
hospital population has almost no infarcts.
The stratification label is a pooled version of that first line.
Classes with fewer than 1,000 records are pooled into OTHER, leaving 47
named classes covering 92.6% of records plus OTHER at 58,933. It exists so
folds are balanced; it is not a clinical grouping.
Nine numeric measurements, all 100% populated and none of them complete. Missing values are integer sentinels, not blanks:
| Sentinel | Meaning | Records affected |
|---|---|---|
29999 |
wave timing not measurable | p_onset 123,434 · p_end 230,323 |
32767 / -32768 |
axis not measurable | p_axis 7,199 · t_axis 1,440 · qrs_axis 1 |
65535 |
RR interval not measurable | 5 |
That these mean “not measurable” rather than “not recorded” is checkable:
p_end is 29999 in 100.0% of atrial-fibrillation records and
p_onset in 90.7% of them, against 0% p_onset sentinels among
sinus-rhythm records — atrial fibrillation has no organised P wave to
measure. ECGBench converts all of them to NaN, which is lossless because
the source columns contain no genuine blanks. It also drops values outside a
physiologic range, which is why p_axis ends up missing in 132,388 records
rather than only the 7,199 exact sentinels. On what survives,
qrs_onset < qrs_end < t_end holds in 99.98% of records and the derived QRS
duration has a median of 94 ms.
No age or sex ships in this module. Both must be joined from MIMIC-IV
itself on subject_id. Timestamps are HIPAA date-shifted into the future,
which is why ecg_time runs from 2097 to 2211 and why no real acquisition
date can be recovered.
| Version | Records | Note |
|---|---|---|
| original | 800,035 | all records, with is_valid + quality_issues |
| clean | 786,699 | 98.33% pass rate |
| excluded | 13,336 | 10,554 with NaN samples, 1,950 with an all-zero lead, 989 amplitude outliers |
1.67% of records fail validation, and the reasons are worth knowing because they are properties of the waveforms, not of the metadata.
NaN samples (10,554 records, 1.32%) — the dominant reason. These records
have literal NaN values in the signal, so they cannot be trained on
without imputation. If you have seen a MIMIC-IV-ECG pipeline that filters
machine_measurements.csv down to 789,481 rows, this is why: that is
exactly 800,035 − 10,554, and the removed studies are precisely this set
(verified by set equality, not by count). ECGBench reaches the same
conclusion from the waveforms rather than from a pre-filtered CSV.
All-zero leads (1,950 records) — one or more leads recorded as exactly
0.0. missing_leads catches these; they are not NaN.
Amplitude outliers (989 records) — beyond ±10 mV. Rare here: the 16-bit
rail at this gain is ±163.8 mV, so these are genuine excursions rather than
saturation, and 15 records additionally trip flat_line.
Fold membership is identical between original/ and clean/ — clean/ is a
row subset, never a re-split — so a record excluded here keeps the fold it
would have had.
No subject spans a fold. Verified after the run: 0 of 161,352 subjects appear in more than one of the 10 folds, and none appears in more than one of train/val/test. Folds are even to within one record (80,003–80,004) and 16,127–16,145 subjects each.
ecgbench splits --dataset mimic_iv_ecg --data-path /path/to/mimic-iv-ecg/1.0/
from ecgbench import ECGDataset
STANDARD_12 = ["I", "II", "III", "aVR", "aVL", "aVF",
"V1", "V2", "V3", "V4", "V5", "V6"]
# 800k records, so one fold is usually enough to start. leads= reorders the
# stored aVF/aVL back to the conventional order, by name.
ds = ECGDataset(
"mimic_iv_ecg",
split="train",
data_path="/path/to/mimic-iv-ecg/1.0/",
metadata_source="local",
fold_numbers=[1],
leads=STANDARD_12,
labels=True,
)
ds[0]["signal"].shape # (12, 5000)
ds[0]["labels"]["report_text"] # 'Sinus rhythm | Possible right atrial abnormality | ...'
ds[0]["labels"]["primary_report"] # 'sinus rhythm' <- first line only
ds[0]["labels"]["qrs_duration"] # 88.0 ms, derived from qrs_end - qrs_onset
ds[0]["labels"]["p_axis"] # NaN where the machine could not measure it
# Without leads= the stored order applies, and signal[4] is aVF, not aVL:
ds.config.lead_names # ['I','II','III','aVR','aVF','aVL','V1',...,'V6']
# A multi-hot target from the free text:
import pandas as pd
# Match the cart's wording: "infarct", not "myocardial infarction".
FINDINGS = ["atrial fibrillation", "sinus bradycardia", "infarct"]
text = ds.labels_df["report_text"].fillna("").str.lower()
targets = pd.DataFrame({f: text.str.contains(f).astype(int) for f in FINDINGS})