Skip to main content

icu-leakage-check

Find out whether your clinical prediction model is predicting, or peeking.

icu-leakage-check audits clinical machine learning pipelines for data leakage: information that would not be available at the moment a prediction is made, or that links the evaluation data back to the training data. It runs 16 checks covering 19 leakage mechanisms, explains every finding in plain language, points to the affected features, and measures how much a leak inflates reported performance.

It works on any tabular EHR dataset and has built-in timing knowledge of every table in MIMIC-IV (hosp and icu) and the eICU Collaborative Research Database.

tests python license

Try it in ten seconds, with simulated ICU data and no credentials:

pip install icu-leakage-check
icu-leakage-check demo --html demo_report.html
Verdict: Leakage detected
Checks: 7 fail, 0 warn, 9 pass, 4 skipped

[FAIL] CRITICAL event_timing: 42,641 events recorded after the prediction time (42.7% of events, 2,352 prediction units), feeding 20 features.
           affected: lactate__last, lactate__max, lactate__mean, lactate__sum, potassium__last, ... (+12 more)
           fix: Restrict every feature to events at or before the prediction time.
[FAIL] CRITICAL preprocessing_fit: 3 learned statistic(s) match all rows, including evaluation rows, rather than the training rows: impute.statistics_, scale.mean_, scale.var_
[FAIL] HIGH     entity_overlap: 313 patient identifiers (uniquepid) appear in both 'train' and 'test'; 349 rows (49.3%) of 'test' are affected.
[FAIL] HIGH     event_timing: 4,904 events charted before the prediction time but only available after it (4.9% of events, 1,942 prediction units), feeding 16 features.
[FAIL] HIGH     hindsight_features: 1 feature(s) flagged: unitdischargeoffset
[FAIL] MEDIUM   event_timing: 881 interval events still running at the prediction time (0.9% of events, 881 prediction units), feeding 4 features.

Test-set performance as each pipeline would report it
                       clean (honest)  leaky (as reported)
auroc                           0.862                0.941
auprc                           0.532                0.783
brier                           0.072                0.039

The same model looks far better when it is allowed to peek. The demo also prints the paired, patient-level bootstrap estimate of the inflation on identical test rows. An example HTML report is in docs/example_report.html.


Why this exists

A model that sees the future looks excellent on paper and fails at the bedside. In clinical data the future hides in ordinary places:

  • billing diagnosis codes that are only assigned after discharge,
  • "worst value in the first 24 hours" scores used by a model that predicts at hour 6,
  • a laboratory result drawn at 10:00 but not available until 14:00,
  • an infusion total that includes the hours after the prediction,
  • a patient's second admission in the test set and first admission in the training set,
  • a scaler or SMOTE step run before the train/test split,
  • a "died in hospital" column sitting in the same table as the predictors.

General-purpose leakage tools inspect code. This package inspects the data, the timing of every event and the fitted pipeline objects, with knowledge of how clinical databases record time, so it catches leaks that code analysis cannot see.

What it checks

Family Check Leakage it detects
Split integrity entity_overlap The same patient, admission or stay in more than one split
site_overlap Hospitals reused in a split described as external
duplicate_records Identical feature vectors in training and evaluation
temporal_order Overlapping periods in a split described as temporal, and temporal splits on MIMIC-IV shifted dates
Timing event_timing Events after the prediction time, results not yet available, intervals crossing the prediction time, undated sources, events after the outcome
truncation_invariance Features that change when everything after the prediction time is deleted
outcome_timing Positive cases whose outcome had already happened
eligibility_timing Cohort criteria that use information from after the prediction time
Feature content hindsight_features Outcome columns, discharge-time columns, undated billing sources, first-day summaries used too early
outcome_proxies Do-not-resuscitate, comfort care, palliative care, brain death and related codes or documentation
single_feature_dominance Single features that separate outcomes almost perfectly, and copies of the label
missingness_proxy Missingness patterns that encode the outcome
Pipeline preprocessing_fit Scalers and imputers whose fitted statistics match all rows instead of the training rows
synthetic_samples SMOTE-style synthetic records inside the evaluation data
threshold_selection Decision thresholds that coincide with the optimum on the test set
declarations A record of practices that cannot be verified from data

Every check runs when its inputs are present and is listed as not verified otherwise, so a report always shows what was and was not tested. Run icu-leakage-check taxonomy --full for the complete catalogue of leakage types, with an example and a remedy for each.

Install

pip install icu-leakage-check            # numpy, pandas, scikit-learn only
pip install "icu-leakage-check[fast]"    # adds duckdb and pyarrow for very large tables

To install from a clone:

git clone https://github.com/Krutarth007/icu-leakage-check
cd icu-leakage-check
pip install -e ".[dev]"
pytest

The core package has no compiled dependencies of its own and runs on older laptops. Nothing leaves your machine: the package never sends data anywhere.

Quick start (any dataset)

from icu_leakage_check import audit

result = audit(
    features,                    # one row per prediction instance
    label="died",
    split="split",               # train / valid / test
    patient_id="patient_id",
    index_time="prediction_time",
)
print(result.summary())
result.to_html("leakage_report.html")

result can also be exported with to_frame(), to_json() and to_markdown(). In a notebook it renders as a formatted report. result.leaky_features("high") returns the features to drop.

Working with MIMIC-IV and eICU-CRD

1. Point the package at your files

Tables can be stored as .csv.gz, .csv, .parquet, or in a folder named like the file (lab.csv/lab.csv), with or without the hosp and icu folders.

icu-leakage-check locate --dataset eicu     --root "C:\data\eicu-collaborative-research-database-2.0"
icu-leakage-check locate --dataset mimic-iv --root "C:\data\mimic-iv-2.2"

Optional, and strongly recommended for the large event tables: convert once to Parquet.

icu-leakage-check convert --dataset eicu --root "C:\data\eicu-collaborative-research-database-2.0" --out "C:\data\eicu_parquet" --memory-limit 4GB --temp-dir "C:\data\tmp"

2. Read only what you need

from icu_leakage_check import read_table

patient = read_table(root, "patient", "eicu")
lab = read_table(root, "lab", "eicu",
                 columns=["patientunitstayid", "labname", "labresult", "labresultoffset", "labresultrevisedoffset"],
                 filters={"patientunitstayid": cohort_ids})

Filters are applied while reading, so a 40-million-row table never needs to fit in memory.

3. Build features that cannot leak

from icu_leakage_check import events_from_table, build_window_features

events = events_from_table(lab, "lab", "eicu")      # knows which column is the result time
index = cohort[["patientunitstayid"]].rename(columns={"patientunitstayid": "entity_id"})
index["index_time"] = 1440                          # predict at 24 hours

features, provenance = build_window_features(events, index, window=1440, return_provenance=True)

By default events are filtered on the time they became available (storetime in MIMIC-IV, entry offsets in eICU-CRD), interval events are prorated at the prediction time, date-only rows count as available at the end of their day and undated billing codes are never used for the same encounter. Setting cutoff="none" or time_basis="event" creates deliberately leaky versions for comparison.

4. Audit with full provenance

from icu_leakage_check import LeakageAudit

result = LeakageAudit(
    data, label="died", split="split", dataset="eicu",
    index_time="index_offset", outcome_time="death_offset",
    calendar_time="hospitaldischargeyear",
    events=provenance,                         # what each feature was built from
    preprocessor=fitted_pipeline,              # inspected for fitting on test rows
    feature_sources={"apachescore": "apachePatientResult.apachescore"},
    split_strategy="patient",
).run()

With dataset set, patient, encounter, stay and hospital identifiers are recognised automatically when the columns exist.

5. Measure what a leak is worth

from icu_leakage_check import leakage_impact

leakage_impact(y_test, clean_scores, leaky_scores, n_boot=1000, groups=patient_ids_test)

This returns each metric (AUROC, AUPRC, Brier score, calibration slope and intercept, ECE) for both pipelines, the paired difference with a bootstrap interval and a p-value. Resampling by patient is supported.

Command line and continuous integration

icu-leakage-check audit --data features.parquet --label died --split split \
    --dataset eicu --index-time index_offset --html report.html --json report.json --fail-on high

The exit code is 1 when any finding reaches the --fail-on severity, so the audit can block a model release in CI:

- run: pip install icu-leakage-check
- run: icu-leakage-check audit --config audit.json --fail-on high

Other commands: scan (screen column names only), schema (show the timing registry), taxonomy, checks, locate and convert.

Who uses this and for what

  • Hospital AI governance and model risk committees. Run the audit on every internally built or vendor-supplied model before deployment and keep the HTML report as evidence.
  • Health plans, payers and health-tech teams. Validate risk models built on claims and EHR extracts, where discharge-time codes and repeat members are common sources of leakage.
  • Regulatory and quality documentation. The report gives direct evidence for the FDA, Health Canada and MHRA Good Machine Learning Practice principle that training data sets are independent of test sets, and supports the validity-on-test-data information required for predictive decision support interventions under the ONC HTI-1 rule.
  • MLOps. Use it as a release gate in CI so leaky pipelines never reach production.
  • Researchers, reviewers and educators. Check a pipeline before submission, verify a shared code base during review, or teach leakage with real clinical schemas.

Severity and status

Status Meaning
fail Leakage detected
warn Strong signal that needs a human decision (for example an outcome proxy that may have been documented before the prediction)
pass The check ran and found nothing
skipped Inputs for the check were not provided

Severities run from info to critical. critical means the reported performance cannot be trusted until the issue is fixed.

Extending

from icu_leakage_check import Check, register
from icu_leakage_check.findings import Severity, Status

@register
class NoNightShiftFeature(Check):
    check_id = "no_night_shift"
    leakage_type = "HINDSIGHT_VARIABLE"
    title = "Example custom rule"

    def run(self, ctx):
        bad = [c for c in ctx.feature_columns if c.endswith("_night")]
        if bad:
            return [self.finding(Status.WARN, Severity.LOW, "Night-shift features present.", affected=bad)]
        return [self.passed("No night-shift features.")]

LeakageAudit(df, "y", "split").run(checks=["entity_overlap", "no_night_shift"])

Data use

MIMIC-IV and eICU-CRD are credentialed datasets. Keep them on your own machine, never commit them or patient-level outputs to a repository, and follow the PhysioNet data use agreement. The open demo versions of both databases on PhysioNet are suitable for tutorials and automated tests.

Citing

If you use the package, please cite it using the metadata in CITATION.cff.

Licence

MIT. See LICENSE.

Metadata

Release files for icu-leakage-check 0.1.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 icu-leakage-check 0.1.0
File Size Uploaded
icu_leakage_check-0.1.0.tar.gz 88.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for icu-leakage-check 0.1.0
File Interpreter ABI Platform
icu_leakage_check-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 172.9 kB

Release files / icu_leakage_check-0.1.0.tar.gz

Download URL icu_leakage_check-0.1.0.tar.gz
Size 88.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c0b021e2d4d2648c324b597048f4b0ae8ced9765d0f029f5eac14820df98704f
BLAKE2b-256 checksum
How to use checksums
85bb00e334d2f90d0ec796d9abaf70b08ca9b307e4b52c7b006543ee56666c70
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.5

Release files / icu_leakage_check-0.1.0-py3-none-any.whl

Download URL icu_leakage_check-0.1.0-py3-none-any.whl
Size 84.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
61eced27863e4b0ee491cc00aa04e7f1829e17a16aa6d09c71ac85afcf527694
BLAKE2b-256 checksum
How to use checksums
12bbf3c3539b4fdf17a080fd69c9c05cb5f4115b158888cb4fc33cd8d2174358
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.5

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

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