Subtitle. PIC v1.1.0 pediatric ICU mortality, canonical study cohort. Authors: Max Moldovan (Adelaide, ORCID 0000-0001-9680-8474), Usman Iqbal (Bond). Spec version 1.1.0 (2026-05-08).
Abstract. Authoritative cohort definition for the PIC v1.1.0 pediatric ICU mortality analysis. The calibration-first manuscript pipeline inherits this specification verbatim. Deviations are forbidden; sensitivity analyses operate on the same base cohort with documented filters.
Frozen contract — version 1.1.0 (2026-05-08). Inherited by the calibration-first manuscript pipeline. Changes require a new minor version, an entry in the changelog at the foot of this vignette, and an explicit Methods paragraph in any paper that adopts the new version. Sensitivity analyses do not require a version bump — they operate on the base cohort with documented filters.
The cohort builder lives in R/cohort.R::build_cohort();
this vignette is the human-readable specification it implements. The
testthat invariants in
tests/testthat/test-cohort-invariants.R are the executable
contract.
ADMISSIONS.csv
at gate G1).data_links/pic_v110/ symlink.
picMort::pic_paths() is the only sanctioned access route.
The Box-synced source folder is never read directly by package
code.One row per first ICU stay per patient. Multiple ICU stays within the same hospitalization collapse to the index stay; ICU readmissions in later hospitalizations are excluded from this cohort entirely.
Rationale: the prediction-window framing requires a well-defined T0; mixing first and subsequent stays leaks information through the patient’s prior trajectory.
| # | Criterion | Source |
|---|---|---|
| I1 | Age at hospital admission \(\in [0, 18]\) years | PATIENTS.DOB, ADMISSIONS.ADMITTIME |
| I2 | Valid ICU admission and discharge timestamps (non-null, in window) | ICUSTAYS.INTIME, ICUSTAYS.OUTTIME |
| I3 | First ICU stay per patient (row_number() == 1 ordered
by INTIME) |
ICUSTAYS |
| I4 | ICU length of stay \(\geq\)
min_los_hours (default 24 h) |
derived |
| # | Criterion | Note |
|---|---|---|
| E1 | Age \(> 18\) y at admission | safety margin: also exclude rows with implausible negative age |
| E2 | Any of INTIME, OUTTIME,
ADMITTIME, DISCHTIME missing |
hard fail |
| E3 | OUTTIME < INTIME or
DISCHTIME < ADMITTIME |
data-quality flag, dropped |
| E4 | ICU LOS \(<\)
min_los_hours |
excluded from prediction-window analysis; counted in attrition |
| E5 | Duplicate subject_id after first-stay selection |
sanity check; should be empty |
Each exclusion is logged with a reason in
cohort_attrition() and rendered as a CONSORT-style flow
diagram in the manuscript.
Primary: hospital_expire_flag —
in-hospital mortality, derived from
ADMISSIONS.HOSPITAL_EXPIRE_FLAG. Cross-checked against
ADMISSIONS.DEATHTIME (any non-null DEATHTIME
within the index hospitalization \(\Rightarrow\) flag = 1; mismatches logged
and reviewed).
Rationale (D1, locked default): in-hospital mortality is the most defensible pediatric-ICU outcome on PIC because (i) the database has no post-discharge follow-up, so 30-day mortality requires opaque imputation for any patient discharged before day 30 alive; (ii) it is the outcome PIM3 was developed to predict, so the comparator is principled; (iii) earlier reference pipelines used 30-day mortality with unclear linkage — a defect this specification explicitly fixes.
Features lock at T0 + 24 h, where
T0 = ICUSTAYS.INTIME. No feature in the matrix
may be derived from any timestamp at or after \(T_0 + 24\) h. The hard rule: no
LOS, no discharge-time, no post-window vital, no post-window lab,
ever. audit_no_leakage() runs as a runtime
invariant in the targets pipeline (gate G2).
Sensitivity arm (D2 secondary default): T0 + 12 h. Reported as a row-level robustness check in the manuscript supplement; no separate cohort build — only the feature window changes.
The cohort table returned by build_cohort() has exactly
these columns, in this order, with these dtypes:
| Variable | Type | Source | Notes |
|---|---|---|---|
subject_id |
integer | PATIENTS |
primary key |
hadm_id |
integer | ADMISSIONS |
join key |
icustay_id |
integer | ICUSTAYS |
join key for events |
intime |
POSIXct (UTC) | ICUSTAYS.INTIME |
T0 |
outtime |
POSIXct (UTC) | ICUSTAYS.OUTTIME |
excluded from features |
los_hours |
double | derived | excluded from features |
age_months |
integer | derived from DOB, ADMITTIME |
for pediatric subgroups |
age_years |
double | derived | float for plotting |
sex |
factor c("F","M") |
PATIENTS.GENDER |
unknown \(\Rightarrow\) NA, excluded |
hospital_expire_flag |
integer 0/1 | ADMISSIONS |
outcome |
admit_year |
integer | ADMITTIME |
for stratification + internal-external split |
is_surgical |
logical | derived from SURGERY_VITAL_SIGNS non-empty |
binary |
primary_icd_chapter |
factor | DIAGNOSES_ICD \(\bowtie\) D_ICD_DIAGNOSES,
top-N + “other” |
Time-varying features (vitals, labs, interventions) live in the
feature matrix built by build_features(),
not in the cohort table. The cohort table is row-static. Weight at
admission is a PIM3 input and is sourced inside
compute_pim3(), not the cohort table (spec v1.1.0 change;
see Changelog).
hospital_expire_flag and admit_year quartile.
Test set frozen at study start; touched once for final reporting.Every cohort build must satisfy the invariants below. Violations
raise errors in assert_cohort_invariants() and fail the
targets pipeline.
list(
n_min = 8500L, # PIC v1.1.0 + min_los_hours = 24
n_max = 9000L,
mortality_rate = c(0.075, 0.095),
age_range_years = c(0, 18),
sex_levels = c("F", "M"),
distinct_subject = TRUE, # one row per subject_id
no_overlap_stays = TRUE # ICU stays do not overlap within subject
)Observed at G1 (2026-05-08, locked). n =
8,736; mortality 0.0844 (737 / 8,736
deaths); age range [0, 17.842] y; admit-year span 2060–2118 (PIC
date-anonymisation preserves relative ordering); sex F/M = 3,684 /
5,052; surgical fraction 36 %. Cohort attrition recorded in
attr(cohort, "attrition"): the LOS ≥ 24 h filter removes
4,075 of 12,811 (32 %) eligible stays — the largest
single exclusion. The 32 % short-stay attrition is a manuscript
Discussion-point on selection bias for the prediction-window framing
(many of the excluded stays are likely “well kid briefly observed” or
“very-early death”; the T+12 h sensitivity arm probes this).
20260508 (set at every stochastic step).targets::tar_make() reproduces every number in
the manuscript from raw CSVs in $<$30 min on a 16 GB laptop after
data_links/ is mounted.renv.lock at the package root pins every
dependency.picMort::pic_paths() raises if any source
CSV is missing, so a half-mounted Box folder cannot silently produce a
partial cohort.EMR_SYMPTOMS text features.The TRIPOD+AI checklist for the calibration-first manuscript points to this specification for items 4a (study design), 5a (data source), 5b (inclusion / exclusion), 6a (outcome) and 6b (predictors).
The package is a teaching artefact as well as the engine behind the
manuscript: function names mirror the clinical concepts
(build_cohort, compute_pim3,
decision_curve), and the targets DAG mirrors the methods
narrative one-to-one. Where pipeline mechanics could obscure clinical
interpretation, callout boxes (“Why this matters clinically”) sit
alongside callouts for mechanics (“Why this matters mechanically”). Both
are first-class.
weight_kg_admit from the cohort table — sourced
in compute_pim3() instead of build_cohort(),
keeps the cohort contract a thin row-static frame and avoids reading the
190 MB CHARTEVENTS table at cohort-build time. (b) Concrete
invariants committed against PIC v1.1.0 (n = 8,736, mortality 0.0844).
(c) Attrition cascade documented; LOS ≥ 24 h filter is the dominant
exclusion (32 %) and a Discussion-point for selection bias.