MIMIC-IV-ECG

Credentialed Completed

Quick facts

Format12-lead · 10 s · 500 Hz
Patients161,352
Records800,035
Leads12
LicensePhysioNet DUA
OriginBeth Israel Deaconess Medical Center — USA — Boston, MA

Overview

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 aVLI, 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 per patient

StudiesPatients% of patientsStudies contributed
157,34435.5%57,344
229,21818.1%58,436
3–428,66617.8%97,249
5–925,32215.7%164,799
10–1913,7008.5%183,141
20–496,2633.9%179,452
50–2598380.5%59,354
26010.0%260
**total****161,352****800,035**

Most frequent first report line

First line of the machine reportRecordsShare
sinus rhythm381,88447.7%
sinus bradycardia71,0348.9%
sinus tachycardia54,6656.8%
atrial fibrillation41,2095.2%
sinus rhythm with borderline 1st degree a-v block15,4281.9%
atrial fibrillation with rapid ventricular response14,9251.9%
sinus rhythm with 1st degree a-v block14,7881.8%
sinus arrhythmia13,7641.7%
sinus rhythm with pac(s)10,5391.3%
*** consider acute st elevation mi ***8,5161.1%
ventricular pacing8,4451.1%
--- warning: data quality may affect interpretation ---7,0790.9%
(938 further values)157,75819.7%

About those counts

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.

Validation summary (500 Hz)

VersionRecordsNote
original800,035all records, with is_valid + quality_issues
clean786,69998.33% pass rate
excluded13,33610,554 with NaN samples, 1,950 with an all-zero lead, 989 amplitude outliers

About the excluded records

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.

Building the splits

ecgbench splits --dataset mimic_iv_ecg --data-path /path/to/mimic-iv-ecg/1.0/

Loading with ECGBench

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})