← Methods repository
CONCEPTINTERMEDIATEPYTHON · R · SAS4 citations

Mortality Source Hierarchy

A pre-specified rule that combines multiple death-information sources (claims discharge status, EHR, registries, the Social Security Death Master File, the National Death Index, and linked vital records) into a single deterministic priority order to assign each patient a binary death flag and a single date of death for survival analysis.

Outcome Measuremortality-ascertainmentoverall-survivaldeath-datenational-death-indexdeath-master-filecompeting-risksinformative-censoring
On this page
Methods reference only. Use primary source citations and local policy before applying this in a study protocol, regulatory submission, payer dossier, or clinical decision.
In plain language

A mortality source hierarchy is a written rule, decided before any data analysis begins, that tells a researcher which database to trust most when multiple sources disagree about whether a patient died and when. Because no single database catches every death, analysts combine several feeds — the National Death Index, insurance enrollment records, hospital discharge codes, and the Social Security Death Master File — and rank them from most to least reliable. The hierarchy picks the best available date for each patient and flags anyone missed by a lower-ranked source that a higher-ranked source caught. Without this rule, patients who die but whose deaths are invisible in one database can be silently treated as alive, which makes a drug look safer or more effective than it really is.

When to use it
Any consequential effectiveness, safety, or economic analysis where missed or misdated deaths would bias survival, RMST, or cost-per-life-year.
Survival/OS endpoints where undercounting deaths is worse than the occasional false positive; use concordance instead for confirmatory or automated-safety deliverables.
Whenever a death index or linked vital record is obtainable and disenrollment is plausibly related to health status (almost always in elderly or seriously ill cohorts).
Watch out for
Requires more code, linkage agreements, and explicit conflict-reconciliation rules.
Admits false positives from mismatched linkage or miscoded discharge status.
Depends on obtaining and linking a death index; introduces linkage selection and month/year date error that must be QC'd.

A mortality source hierarchy is the operational algorithm that turns several incomplete, partially overlapping death feeds into one analyzable endpoint: a death indicator and a single, defensible date of death per patient. No single real-world source captures all deaths completely or dates them exactly, so a composite that ranks sources by validity and fills gaps is now the standard of practice for any RWE study with a survival, all-cause-mortality, or time-to-event endpoint. The hierarchy must be written in protocol language before programming because every downstream quantity — follow-up time, censoring, hazard ratios, restricted mean survival time, cost-per-life-year — inherits its sensitivity, specificity, and date error.

Core conceptual distinction

Three things are being decided, and they are separable.

  1. Capture (the death flag): which sources count as evidence that a patient died, and in what priority. Sources differ in sensitivity (does it catch the death at all) and specificity (false positives — e.g., a discharge "expired" status keyed in error, or an SSDI record matched to the wrong person).
  2. Dating (the death date): once a death is established, which source's date is authoritative. The Death Master File and NDI are accurate to month/year but the day is unreliable when coded as the last day of the month; claims give a precise service date but only if death occurred during an observed encounter.
  3. Cause: all-cause vs cause-specific mortality is a different ascertainment problem entirely — cause requires NDI Plus / death- certificate ICD coding and cannot be read off enrollment files. The estimand must name which of these it needs. A naive "last claim = censor" default silently conflates no longer observed with alive, which is the single most common and most dangerous error this concept exists to prevent.

Pros, cons, and trade-offs

  • vs a single-source death flag (e.g., enrollment-file death indicator alone): A hierarchy raises sensitivity dramatically — composite endpoints benchmarked to the NDI reach ~98% sensitivity versus ~83-92% for any one administrative source — and corrects systematically wrong dates. Cost: more code, more linkage agreements, and the need to reconcile disagreements between sources. Prefer the hierarchy for any consequential effectiveness, safety, or economic analysis where missed or misdated deaths bias the result.
  • vs "high-specificity only" (require ≥2 concordant sources before flagging a death): Demanding agreement maximizes specificity and is appropriate when a false death is catastrophic (e.g., an automated safety signal). Cost: it sacrifices sensitivity and undercounts deaths that only one good source saw. Prefer concordance rules for confirmatory/regulatory deliverables; prefer the union/ priority hierarchy when undercounting deaths is the worse error (most survival analyses).
  • vs treating end-of-enrollment as the endpoint (administrative censoring only): Censoring at disenrollment is defensible only if disenrollment is unrelated to death. It usually is not — sick patients disenroll, switch to hospice, or move to Medicare Advantage where fee-for-service claims vanish. Substituting censoring for an actual death feed induces informative censoring and inflates survival. Prefer a real mortality source whenever a death index or linked vital record is obtainable.

When NOT to use — and when it is actively misleading or dangerous

  • When you need cause-specific mortality but only have all-cause feeds. A hierarchy of enrollment + SSDI + discharge status tells you that a patient died, never why. Reporting "cardiovascular death" off such sources is fabrication; you need death-certificate cause coding (NDI Plus) or adjudication.
  • When false positives dominate the decision. For an automated pharmacovigilance trigger, a union-style hierarchy that flags on any single source will fire on mismatched SSDI links and erroneous discharge codes; require concordance or manual review instead.
  • When the date error interacts with a short-window estimand. If the estimand is 30-day mortality and your authoritative date is month/year-only (snapped to mid-month or month-end), the day-level error can flip events across the window boundary. Do not paper over month-only dates with a fixed imputation and then estimate a day-resolution endpoint.
  • When disenrollment and death are confounded and you censor instead of ascertaining. Using end-of-coverage as a pseudo-death or as the only censoring event when sick patients drop coverage makes the treated arm look immortal. This is the failure mode that most often produces a spuriously protective drug effect.

Data-source operational depth

  • Claims (FFS vs Medicare Advantage): Death is inferable from the inpatient discharge status code (`DSTATUS`/`disp` = expired) and, in Medicare, from the enrollment/denominator death indicator and `death_date` (DMF-sourced). Failure modes: (a) MA-only person-time lacks adjudicated FFS claims, so in-hospital deaths are invisible and "no death" can be pure missingness — restrict mortality follow-up to A/B-enrolled FFS time or link to a death index; (b) out-of-hospital deaths (home, hospice, ED-on-arrival) never generate a discharge status; (c) discharge "expired" can be miscoded, creating false positives; (d) claims lag and reversals mean the death date is not final for months. Workaround: take the date from the enrollment/DMF feed, use discharge status as a sensitivity-raising supplement, and never censor MA-only person-time as if it were observed alive.
  • EHR: Death is captured only if it happens inside, or is reported back to, the network — a patient who leaves the system and dies elsewhere is silently censored. Structured `death_date`/`deceased_flag` fields are encounter-driven and notoriously incomplete; obituary and SSDI augmentation recover large numbers of out-of-network deaths. Failure mode: differential capture by exposure if one arm is sicker and referred out. Workaround: link to commercial claims and a death index; report capture completeness by site and by arm.
  • Registry: Disease registries (e.g., SEER, cancer registries) often have actively followed, adjudicated vital status and cause of death, but linkage eligibility and lag limit completeness and the followed population may not be transportable to the analysis cohort. Workaround: link to claims for the interval between registry contacts; document the linkage denominator.
  • Linked claims-EHR-vital records (DMF/NDI): The reference substrate. The National Death Index is the benchmark for both fact and cause of death; the Death Master File / SSDI is broad but lost many state-reported deaths after the 2011 records-access changes (a known sensitivity drop for recent years). Failure modes: linkage selection (only the linkable subset), false matches (specificity), and month/year-only dates. Workaround: probabilistic-match QC, benchmark composite sensitivity/specificity to the NDI, and pre-specify a date-imputation rule for month-only records.

Estimand and competing-risks note

With death as the outcome, all-cause mortality is a single risk and a Cox/pooled-logistic model on the hierarchy-derived flag/date is appropriate. With a non-fatal primary outcome (e.g., first hospitalization for heart failure), death is a competing risk and the choice of estimand is consequential: the cause-specific hazard (treat death as censoring; `PROC PHREG`, `coxph`) answers an etiologic question, whereas the subdistribution hazard / cumulative incidence function (Fine-Gray; `PROC PHREG eventcode=`, `cmprsk`/`PROC LIFETEST plots=cif`) answers an absolute- risk/decision question. In elderly claims populations, mortality is high and may differ by exposure, so Kaplan-Meier on the non-fatal event overstates its incidence; pre-specify cause-specific vs subdistribution and report the CIF.

Worked claims example

Goal: an all-cause mortality endpoint for a 100% Medicare FFS cohort of new initiators, index_date = first qualifying fill.

  1. Require continuous Parts A/B FFS enrollment from index forward; flag and separately handle any switch to MA (where FFS claims stop).
  2. Build candidate death records from three feeds: the enrollment/denominator `death_date` (DMF-sourced), inpatient claims where `discharge_status` ∈ expired codes (take the claim `thru_date` as the date), and a linked NDI/state-file date where available.
  3. Apply the priority hierarchy for the flag: a patient is dead if ANY source indicates death (union maximizes sensitivity), but require that the death date fall on or after index and within enrollment (drop physiologically impossible records).
  4. Assign the date by priority: NDI > enrollment/DMF > inpatient discharge `thru_date`; if only month/year is available, pre-specify imputation (e.g., 15th of month) and carry a flag for sensitivity analysis.
  5. Reconcile conflicts: if two sources disagree by >X days, log and review; if discharge "expired" appears but no death is in DMF within 90 days, treat as a possible false positive in a high-specificity sensitivity run.
  6. Define follow-up from index to the assigned death date or to administrative censoring (end of A/B FFS enrollment, end of data, or MA switch) — and crucially, do NOT censor at the last observed claim, which would convert unobserved deaths into spurious survival.
  7. Sensitivity analyses: vary the date-imputation rule, swap the union flag for a ≥2-source concordance flag, and report endpoint sensitivity/specificity against the NDI benchmark where the linkage exists.

Interpreting the output

From the worked example: 4 patients; hierarchy yields 3 deaths (P001 NDI 2022-08-14, P002 DMF 2022-11-15 day-imputed, P003 discharge 2022-06-20); P004 alive. NDI alone would capture only P001 (1/3 true deaths, 33%). Discharge-status alone captures only P003 (33%); P001 and P002 died outside hospital. Composite union flag captures all 3 (100% in this small example).

(1) Formal interpretation. The hierarchy assigns one death flag per patient (union of all sources) and one authoritative death date (priority: NDI > DMF > discharge > inferred). For P001, NDI date 2022-08-14 overrides the DMF date 2022-08-01 per the pre-specified rule, giving follow-up 225 days. For P002, DMF is the sole source and day-imputation to the 15th is documented as a sensitivity flag. For P003, discharge "expired" with no NDI or DMF match is accepted at face value, giving follow-up 170 days. P004 is censored at administrative end of enrollment. Single-source analyses would systematically misclassify P001 and P002 as censored, inflating apparent survival times for those patients by the interval between last claim and true death.

(2) Practical interpretation. A Kaplan-Meier or Cox model using only discharge-status deaths would lose P001 and P002 from the risk set as events, instead censoring them — inflating survival estimates and potentially biasing treatment comparisons if mortality undercount is differential by arm (e.g., Drug B patients more likely to die in non-acute settings). Conversely, NDI-only linkage misses P002 and P003. The composite hierarchy maximizes completeness while the date-priority rule minimizes measurement error in follow-up time, both of which are essential for unbiased survival analysis.

Decision diagram

flowchart TD
  P[Patient in cohort<br/>index_date assigned] --> NDI{Linked NDI /<br/>state vital record?}
  NDI -- yes --> DT[Death date = NDI date<br/>source priority 1]
  NDI -- no --> DMF{Enrollment / DMF<br/>death_date present?}
  DMF -- yes --> IMP[Snap month/year-only<br/>to pre-specified day] --> DT2[Death date = DMF date<br/>priority 2]
  DMF -- no --> DIS{Inpatient discharge<br/>status = expired?}
  DIS -- yes --> DT3[Death date = claim thru_date<br/>priority 3]
  DIS -- no --> ALIVE[No death evidence<br/>administrative censor at end of FFS enrollment<br/>NOT at last claim]
  DT --> VAL{Date on/after index<br/>and within enrollment?}
  DT2 --> VAL
  DT3 --> VAL
  VAL -- yes --> DEAD[death_flag = 1<br/>follow-up ends at death date]
  VAL -- no --> DROP[Drop impossible record<br/>fall through to next source]
Priority hierarchy for assigning a single death flag and date. Capture uses the union of sources (any valid death counts); the date is taken from the highest-priority source; unobserved patients are administratively censored at end of FFS enrollment, never at the last observed claim.
flowchart LR
  subgraph Sources [Death feeds, ranked by validity]
    S1[NDI / linked vital record<br/>best fact + cause + date]
    S2[Enrollment / DMF death_date<br/>broad capture, month-level date]
    S3[Inpatient discharge = expired<br/>precise date, in-hospital only]
    S4[EHR deceased flag<br/>in-network deaths only]
  end
  S1 --> H[Mortality source hierarchy<br/>union flag + priority date]
  S2 --> H
  S3 --> H
  S4 --> H
  H --> EST{Estimand?}
  EST -- death is the outcome --> AC[All-cause mortality<br/>Cox / pooled logistic]
  EST -- non-fatal outcome,<br/>death competes --> CR[Cause-specific hazard<br/>vs subdistribution / CIF Fine-Gray]
Data flow from ranked death feeds into the composite endpoint, then the estimand fork that determines whether death is the analyzed outcome or a competing risk requiring a cause-specific vs subdistribution-hazard choice.

Worked example

Scenario

Four patients in a drug study all started treatment on January 1, 2022. The study team has three death data sources: the NDI (gold standard, but slow — only available through December 2022), the DMF (broad coverage, but records death month and year only, not the exact day), and hospital discharge codes (exact date, but only catches deaths that happen during a hospital stay). The hierarchy rule is: NDI first, then DMF, then discharge codes. A patient is flagged as dead if any source reports a death (union rule). The date is taken from the highest-priority source that reported a death for that patient.

Dataset

Raw death signals across three sources for four study patients. Each cell shows the reported date of death, or NONE if the source has no record.

person_idindex_dateNDI_death_dateDMF_death_datedischarge_death_date
P0012022-01-012022-08-142022-08-01NONE
P0022022-01-01NONE2022-11-012022-11-03
P0032022-01-01NONENONE2022-06-20
P0042022-01-01NONENONENONE

Steps

1P001: NDI reports death on 2022-08-14. NDI is priority 1, so the assigned death date is 2022-08-14. The DMF record (2022-08-01) is noted but not used for the date because NDI outranks it. Follow-up ends on 2022-08-14.
2P002: NDI has no record (perhaps the linkage file did not yet include this patient). DMF reports a death in November 2022 with month-year only, so the day is imputed to the 15th per the pre-specified rule: assigned date becomes 2022-11-15. The discharge code on 2022-11-03 is noted but DMF outranks it. Follow-up ends on 2022-11-15.
3P003: Neither NDI nor DMF has a record, but the hospital discharge code on 2022-06-20 shows the patient died in the hospital. Under the union rule, this single source is enough to flag a death. Assigned death date is 2022-06-20. Follow-up ends on 2022-06-20.
4P004: No source reports any death. The patient is treated as alive and their follow-up continues until the study end date or until they leave the insurance plan, whichever comes first. This administrative end-of-observation is the correct stopping point — NOT the date of their last doctor visit or last insurance claim.
5Result check: 3 of 4 patients are flagged as dead (P001, P002, P003). Had the team relied on NDI alone, only P001 would have been captured, missing 2 deaths and artificially inflating apparent survival. Had the team used only discharge codes, P001 and P002 — both of whom died outside a hospital — would have been missed entirely.

Result

Deaths captured: 3 out of 4 patients (P001, P002, P003). Assigned dates: P001 = 2022-08-14 (NDI), P002 = 2022-11-15 (DMF with day imputed to 15th), P003 = 2022-06-20 (discharge code, the only source). P004 remains alive in the analysis. Using only a single source would have missed at least 1 and up to 2 of these 3 real deaths, shortening apparent follow-up for survivors and biasing any comparison between treatment groups.

Trade-offs

vs. Single source death flag (enrollment file indicator alone)
Pros of this
Recovers out-of-network and out-of-hospital deaths and corrects systematically wrong dates; composite endpoints benchmark to ~98% sensitivity vs ~83-92% for any one administrative source.
vs. High specificity concordance rule (require >=2 sources to agree)
Pros of this
Higher sensitivity - captures deaths seen by only one valid source, which dominate when care is fragmented across systems.
vs. Administrative censoring at end of enrollment (no real death feed)
Pros of this
Uses an actual mortality source, avoiding the informative censoring that arises when sick patients disenroll, enter hospice, or switch to MA where FFS claims disappear.

Runnable example

Build an all-cause-mortality endpoint from multiple death feeds via a priority hierarchy. Required inputs (cleaned, one concept each; dates are datetime): cohort : person_id, index_date enroll : person_id, ffs_start, ffs_end # FFS A/B-observable mortality follow-up spans dmf : person_id, death_date, date_precision #...

requires: pandas · numpy
import pandas as pd
import numpy as np

EXPIRED_CODES = {"20", "40", "41", "42"}  # site-specific: discharge_status = expired
MONTH_IMPUTE_DAY = 15                       # pre-specified day for month/year-only dates

def build_mortality_endpoint(cohort, enroll, dmf, inp, ndi):
    idx = cohort[["person_id", "index_date"]]

    # --- Candidate death records from each feed, each tagged with a source priority. ---
    d_ndi = ndi[["person_id", "death_date"]].assign(source="ndi", priority=1)

    d_dmf = dmf.copy()
    impute = d_dmf["date_precision"].eq("month")
    d_dmf.loc[impute, "death_date"] = (
        d_dmf.loc[impute, "death_date"].values.astype("datetime64[M]")
        + np.timedelta64(MONTH_IMPUTE_DAY - 1, "D"))
    d_dmf = d_dmf[["person_id", "death_date"]].assign(source="dmf", priority=2)

    d_inp = (inp[inp["discharge_status"].astype(str).isin(EXPIRED_CODES)]
             .rename(columns={"thru_date": "death_date"})[["person_id", "death_date"]]
             .assign(source="discharge", priority=3))

    cand = pd.concat([d_ndi, d_dmf, d_inp], ignore_index=True).merge(idx, on="person_id")

    # --- Validity filter: death must be on/after index and within FFS-observable enrollment. ---
    cand = cand.merge(enroll, on="person_id")
    cand = cand[(cand["death_date"] >= cand["index_date"]) &
                (cand["death_date"] >= cand["ffs_start"]) &
                (cand["death_date"] <= cand["ffs_end"])]

    # --- FLAG (union): dead if ANY valid source fired. DATE: take highest-priority source. ---
    cand = cand.sort_values(["person_id", "priority", "death_date"])
    dead = (cand.groupby("person_id")
                .first()
                .reset_index()[["person_id", "death_date", "source"]]
                .rename(columns={"source": "date_source"}))

    out = idx.merge(dead, on="person_id", how="left")
    out["death_flag"] = out["death_date"].notna().astype(int)

    # --- Follow-up end: death date if dead, else administrative censor at end of FFS enrollment. ---
    ffs_end = enroll.groupby("person_id")["ffs_end"].max().rename("ffs_end")
    out = out.merge(ffs_end, on="person_id", how="left")
    out["fup_end"] = out["death_date"].fillna(out["ffs_end"])   # never censor at last claim
    out["fup_days"] = (out["fup_end"] - out["index_date"]).dt.days
    return out[["person_id", "index_date", "death_flag", "death_date",
                "date_source", "fup_end", "fup_days"]]

Citations

FOUNDATIONAL / METHODS
  1. [1]Curtis MD, Griffith SD, Tucker M, et al. Development and validation of a high-quality composite real-world mortality endpoint. Health Services Research. 2018;53(6):4460-4476.
  2. [2]Austin PC, Lee DS, Fine JP. Introduction to the analysis of survival data in the presence of competing risks. Circulation. 2016;133(6):601-609.
APPLIED EXAMPLES
  1. [3]Jamal-Allial A, Vojjala S, Zhang J, et al. Validation of mortality data sources compared to the National Death Index in the Healthcare Integrated Research Database. Pragmatic and Observational Research. 2025;16:1-11.
REPORTING & GUIDANCE
  1. [4]Shao S, Adamson BJS, Bruno AM, et al. Improving real-world mortality data quality in oncology research: augmenting electronic medical records with obituary, Social Security Death Index, and commercial claims data. JCO Clinical Cancer Informatics. 2023;7:e2300014.