Missing Data Pattern Table
A diagnostic artifact that cross-tabulates the rate and co-occurrence structure of missing values by variable, treatment arm, calendar time, site, and outcome status to characterize the missingness pattern and inform a defensible mechanism judgment (MCAR / MAR / MNAR) before any analysis decision.
On this page
A missing data pattern table is a simple diagnostic you build before any analysis to see exactly which variables have gaps, how many gaps, and whether those gaps are spread evenly or clustered in ways that matter. You cross-tabulate the percent missing for each variable by treatment group, site, and time period, then examine which variables tend to be missing together on the same record. The pattern tells you whether gaps appear random or are tied to something measurable in your data, which in turn tells you which statistical method is appropriate to handle them.
A missing data pattern table is the descriptive backbone of any principled missing-data strategy in real-world evidence. It is not an estimator and not an imputation; it is the audit that turns "we have missingness" into a specific, testable claim about which variables are missing, how much, for whom, and together with what. Two distinct objects travel under this name and both belong in the table: (1) the per-variable missingness summary — the proportion missing for each analysis variable, ideally stratified by the factors that drive selection in routinely collected data (treatment arm, calendar quarter, care site/plan, and outcome status); and (2) the monotone-vs-non-monotone pattern matrix — the set of distinct missingness patterns across variables (the binary observed/missing footprint per record) and how many records share each, which reveals whether missingness is monotone (dropout-like, amenable to simpler methods) or arbitrary (requiring chained-equations imputation or pattern-mixture modeling).
Core conceptual distinction
(table vs mechanism). The pattern table answers a different question than the mechanism. The table is purely observable: counts and cross-tabs you can compute from the data. The mechanism — Rubin's taxonomy of MCAR (missingness independent of all data), MAR (missingness depends only on observed data), and MNAR (missingness depends on the unobserved value itself) — is partly unverifiable and must be argued, not measured. The table constrains that argument: if HbA1c is missing far more often in one treatment arm or in Medicare Advantage enrollees, MCAR is empirically implausible (Little's test on the pattern matrix can formalize this), and you are forced to choose between MAR-justified methods (multiple imputation, IPCW) conditional on the observed drivers, or MNAR-justified sensitivity analysis (pattern-mixture, delta-adjustment). The estimand is unchanged by the table, but the credibility of the identifying assumption required to estimate it is exactly what the table is built to interrogate.
Pros, cons, and trade-offs
(specific and comparative).
- vs jumping straight to complete-case analysis: The pattern table exposes whether complete-case is even admissible. Complete-case is unbiased only under MCAR (or, for a regression coefficient, when missingness is independent of the outcome given covariates); the table is what lets you reject MCAR. Cost: it is descriptive overhead that does not itself fix anything. Prefer the table before any deletion-based analysis — it is cheap insurance against a silently biased default.
- vs jumping straight to multiple imputation (MI): MI is valid under MAR, but MAR-given-what? The pattern table tells you which observed variables drive missingness and therefore which auxiliary variables the imputation model must include to make MAR plausible. Skipping the table risks an imputation model that omits the very predictors that make missingness ignorable, reintroducing bias while projecting false confidence (Hughes et al.). Prefer the table to specify the imputation model, not as a substitute for it.
- vs a single overall "% missing" figure: The stratified table is the entire value-add. A 5% overall HbA1c-missing rate that is 1% in one arm and 20% in the other is a confounded-missingness alarm; the marginal 5% hides it. Cost: more programming and more cells to interpret. Always prefer the stratified, arm-by-time-by-site version for any comparative analysis.
When NOT to use — and when it is actively misleading or dangerous
(decision rules). The table itself is never the wrong thing to compute; the danger is misreading it.
- Do not treat a passing Little MCAR test as license for complete-case. The test has low power, ignores MNAR entirely, and a non-significant result in a small or sparse cell pattern is uninformative — acting on it is the classic trap.
- Do not stratify only by baseline variables when the missingness is outcome-driven. If a lab is ordered because the patient is deteriorating, the value is MNAR and no amount of observed-covariate stratification rescues MAR; the table must include outcome status precisely to surface this, and if it does, MI alone is misleading and you need MNAR sensitivity analysis.
- Do not confuse structural/legitimate missingness with informative missingness. A pregnancy field "missing" for men, or a lab not drawn because it was clinically unnecessary, are not the same as a deteriorating-patient lab gap; collapsing them in one table and imputing across them fabricates data.
- Do not let a clean-looking table mask differential ascertainment masquerading as data presence — a zero "missing" rate for diagnoses in claims does not mean the disease is absent; it means a code was or was not submitted, which is an exposure/outcome-misclassification problem the missingness table cannot see.
Data-source operational depth
(real failure modes and workarounds).
- Claims (FFS vs Medicare Advantage): Most "missingness" in claims is really non-observation of person-time, and it is structurally differential. Medicare Advantage encounter data are notoriously incomplete relative to fee-for-service claims, so any variable derived from utilization (comorbidity flags, prior therapy, baseline cost) is differentially missing for MA-only person-time — and MA enrollment correlates with health and region, making this MNAR with respect to the very confounders you need. Workaround: restrict to enrollees with the relevant benefit (A/B/D or commercial medical+pharmacy) across the lookback, flag MA-only spans explicitly, and stratify the pattern table by plan type so the differential is visible rather than averaged away. Lab results are absent from most medical claims entirely (you get the CPT for the test, not the value), so an HbA1c "value" column is ~100% missing in claims-only data.
- EHR: Missingness is encounter-driven and informative. A field is populated only if a visit occurred and the clinician charted it; sicker, more-engaged patients accrue more data, so completeness correlates with the outcome. External-care leakage means a patient treated elsewhere looks "missing" when they are merely unobserved in this system. Workaround: stratify by site and by within-system utilization, treat loss to follow-up as potentially informative (IPCW rather than naive complete-case), and use linked claims to distinguish "not done" from "done elsewhere."
- Registry: Pre-specified data dictionaries make missingness more interpretable, but completeness varies by site and over the enrollment period, and adjudicated fields may be missing precisely for the hardest-to-adjudicate (often most severe) cases. Stratify by site and accrual era.
- Linked claims–EHR–vital records: The richest substrate but linkage itself induces missingness — the unlinkable subset is a selected population, and date discrepancies between order, fill, and service dates create apparent missingness in time-windowed variables. Report the linkage denominator as the first row of the pattern table.
Worked claims/EHR example
Question: comparative HbA1c control (mean HbA1c at 12 months) for GLP-1 RA vs basal insulin initiators in a linked commercial-claims + EHR diabetes cohort.
- For each analysis variable — baseline HbA1c, baseline eGFR, BMI, the 12-month outcome HbA1c, and each propensity-score confounder — compute the proportion of `person_id` with a non-missing value.
- Cross-tabulate the 12-month HbA1c missingness by `arm` × calendar quarter of `index_date` × care `site_id` × `plan_type` (FFS vs MA-equivalent commercial PPO vs HMO).
- Suppose the table shows outcome HbA1c missing in 18% of the basal-insulin arm but 9% of the GLP-1 arm, concentrated in HMO sites in 2020-Q2 (a COVID lab-access shock) — a textbook MAR-on-observed-factors (arm, site, time) plus a suspected MNAR component (insulin initiators with poor control may skip labs).
- Build the monotone-vs-arbitrary pattern matrix: if a record missing the 12-month HbA1c is also missing the 12-month eGFR (both lab-draw-dependent), the pattern is co-clustered, signaling a shared visit-attendance mechanism to encode as an auxiliary "had a 12-month encounter" predictor.
- Decision: complete-case is rejected (Little's test significant; rates differ by arm); specify multiple imputation by chained equations including arm, site, quarter, the encounter-attendance flag, and the co-missing labs as predictors, and add a tipping-point/delta-adjusted MNAR sensitivity analysis shifting imputed values in the insulin arm to test robustness. The pattern table is what justified every one of these choices in the SAP.
Interpreting the output
Consider the GLP-1 vs. basal insulin cohort above, where the 12-month HbA1c outcome is missing in 9% of GLP-1 arm patients and 19% of basal insulin patients — a 10 percentage-point differential between arms.
(1) Formal statistical interpretation. The observed arm-differential of 10 percentage points in outcome missingness is direct evidence against the missing completely at random (MCAR) assumption, which requires that the probability of missingness be unrelated to both observed and unobserved variables. Little's MCAR test further quantifies this departure. The co-clustering of outcome and eGFR missingness within the same patients — the monotone pattern — indicates a shared visit-attendance mechanism rather than independent item non-response; this structural feature must be encoded in any imputation model as an auxiliary predictor. Under MCAR, complete-case analysis would be valid; the pattern table rules that out. Under missing at random (MAR) conditional on arm, site, and time, multiple imputation with those variables as predictors is valid; the suspected MNAR component requires an additional sensitivity analysis.
(2) Practical interpretation for a decision-maker. The pattern table is the evidentiary basis for every subsequent missing-data decision in the SAP. A 10-point arm differential means the complete-case sample is differentially depleted in insulin initiators — likely the sicker ones — biasing any unadjusted comparison. Documenting the pattern before analysis forces transparency: reviewers can see exactly which variables are missing, by how much, and in which groups, so the choice of imputation method is grounded in data rather than assumption.
Decision diagram
flowchart TD
Tab[Missing data pattern table<br/>per-variable rates by arm x time x site + pattern matrix] --> Q1{Rates differ by<br/>arm / site / time?<br/>Little MCAR test}
Q1 -->|No, plausibly MCAR| CC[Complete-case admissible<br/>verify cell counts not just p-value]
Q1 -->|Yes, depends on OBSERVED data| MAR[MAR-justified handling]
MAR --> MI[Multiple imputation incl. arm, site,<br/>quarter, auxiliary attendance flag]
MAR --> IPCW[IPCW for informative<br/>loss to follow-up]
Q1 -->|Driven by the UNOBSERVED value<br/>e.g. lab skipped because patient worse| MNAR[MNAR]
MNAR --> PM[Pattern-mixture / delta-adjusted<br/>tipping-point sensitivity analysis]
MI --> Sens[Report mechanism judgment + handling in SAP]
IPCW --> Sens
PM --> Sens
CC --> Sensflowchart LR A[Per-variable missingness<br/>stratified by arm x quarter x site x plan] --> C[Mechanism judgment<br/>MCAR vs MAR vs MNAR] B[Pattern matrix<br/>monotone vs arbitrary footprints<br/>+ co-clustered missingness] --> C C --> D[Imputation-model spec<br/>or MNAR sensitivity plan] A -.claims: stratify FFS vs MA.-> A B -.linked: linkage denominator first row.-> B
Worked example
Scenario
You are studying a diabetes drug comparison: GLP-1 agonist versus basal insulin, using a linked claims and EHR dataset of 200 patients. Before you run any models, your statistician asks you to build a missing data pattern table for four key analysis variables: baseline HbA1c, baseline eGFR, BMI, and the 12-month outcome HbA1c. The table below is what you would produce. You then read the pattern to decide how to handle the missingness.
Dataset
Per-variable missingness summary table: rows are analysis variables, columns show total patients with a value, count missing, percent missing, and the missingness pattern type across all four variables. Two treatment arms are shown side by side. N = 100 per arm (200 total).
| variable | glp1_n_observed | glp1_n_missing | glp1_pct_missing | insulin_n_observed | insulin_n_missing | insulin_pct_missing | pattern_type |
|---|---|---|---|---|---|---|---|
| baseline_hba1c | 97 | 3 | 3% | 96 | 4 | 4% | arbitrary |
| baseline_egfr | 95 | 5 | 5% | 94 | 6 | 6% | arbitrary |
| bmi | 92 | 8 | 8% | 90 | 10 | 10% | arbitrary |
| outcome_hba1c_12m | 91 | 9 | 9% | 81 | 19 | 19% | monotone |
Steps
Result
Dominant pattern: outcome HbA1c is missing in 9/100 = 9% of GLP-1 patients and 19/100 = 19% of insulin patients, a 10 percentage-point arm difference that rules out MCAR. The monotone co-missing structure for outcome labs points to a shared visit-attendance mechanism. The implied handling:
- complete-case is rejected because missingness is differential by arm;
- multiple imputation is warranted and must include arm, site, and calendar quarter as predictors to make a MAR assumption plausible;
- a sensitivity analysis shifting imputed insulin-arm values by a delta (MNAR tipping-point) should be pre-specified to test whether conclusions hold if the missing patients had worse-than-imputed control.
Trade-offs
Runnable example
Builds a missing data pattern table from a one-row-per-patient analysis frame. Required input (post data-management): df : person_id (unique), arm in {'STUDY','COMPARATOR'}, index_date (datetime), site_id, plan_type ('FFS'/'MA_EQUIV'), plus the analysis variables (e.g. base_hba1c, base_egfr, bmi, out_hba1c_12m, ...).
import pandas as pd
import numpy as np
ANALYSIS_VARS = ["base_hba1c", "base_egfr", "bmi", "out_hba1c_12m"]
def missingness_by_stratum(df: pd.DataFrame, var: str) -> pd.DataFrame:
# % missing for one variable by arm x calendar quarter of index_date.
q = df["index_date"].dt.to_period("Q").astype(str)
out = (df.assign(_miss=df[var].isna(), _qtr=q)
.groupby(["arm", "_qtr"])
.agg(n=("person_id", "size"), n_missing=("_miss", "sum")))
out["pct_missing"] = (out["n_missing"] / out["n"]).round(3)
return out.reset_index()
def variable_summary(df: pd.DataFrame, vars_: list[str]) -> pd.DataFrame:
# Overall and by-arm missingness for every analysis variable (the headline table).
rows = []
for v in vars_:
overall = df[v].isna().mean()
by_arm = df.groupby("arm")[v].apply(lambda s: s.isna().mean())
rows.append({"variable": v, "pct_missing_overall": round(overall, 3),
**{f"pct_missing_{a}": round(p, 3) for a, p in by_arm.items()}})
return pd.DataFrame(rows)
def pattern_matrix(df: pd.DataFrame, vars_: list[str]) -> pd.DataFrame:
# Distinct observed(1)/missing(0) footprints across vars_ and how many records share each.
# A single dominant footprint => near-complete; staircase of footprints => monotone (dropout-like);
# many scattered footprints => arbitrary pattern -> chained-equations imputation required.
obs = df[vars_].notna().astype(int)
pat = (obs.groupby(vars_).size()
.reset_index(name="n_records")
.sort_values("n_records", ascending=False))
pat["n_missing_vars"] = (len(vars_) - pat[vars_].sum(axis=1)).astype(int)
return pat
var_table = variable_summary(df, ANALYSIS_VARS)
outcome_by_stratum = missingness_by_stratum(df, "out_hba1c_12m")
patterns = pattern_matrix(df, ANALYSIS_VARS)Missing data pattern table in R. Input `df` is one row per patient with: person_id, arm, index_date (Date), site_id, plan_type, and the analysis variables, with missing encoded as NA (not sentinel codes).
library(dplyr)
library(naniar)
library(mice)
analysis_vars <- c("base_hba1c", "base_egfr", "bmi", "out_hba1c_12m")
# (1) Per-variable missingness, overall and stratified by arm x calendar quarter.
var_summary <- naniar::miss_var_summary(df %>% select(all_of(analysis_vars)))
stratified <- df %>%
mutate(qtr = paste0(lubridate::year(index_date), "Q",
lubridate::quarter(index_date))) %>%
group_by(arm, qtr) %>%
summarise(n = n(),
pct_missing_outcome = mean(is.na(out_hba1c_12m)),
.groups = "drop")
# (2) Pattern matrix: rows = distinct observed/missing footprints, last col = count missing,
# row counts = records sharing the footprint. A staircase indicates monotone missingness.
pattern <- mice::md.pattern(df %>% select(all_of(analysis_vars)), plot = FALSE)Missing data pattern table in SAS. Input WORK.ANALYTIC is one row per patient with: person_id, arm, index_date, site_id, plan_type, and numeric analysis variables with missing as standard SAS missing (.), not sentinel values.
/* (1) Per-variable missing counts and rates, stratified by arm. */
proc means data=work.analytic n nmiss;
class arm;
var base_hba1c base_egfr bmi out_hba1c_12m;
run;
/* Outcome missingness by arm x calendar quarter (the differential-missingness alarm). */
data _strat;
set work.analytic;
qtr = cats(year(index_date), 'Q', qtr(index_date));
miss_out = missing(out_hba1c_12m);
run;
proc freq data=_strat;
tables arm*qtr*miss_out / nopercent norow nocol;
run;
/* (2) Observed/missing footprint string -> frequency = the pattern matrix
(1 = observed, 0 = missing). Many distinct strings => arbitrary pattern. */
data _pat;
set work.analytic;
footprint = cats(^missing(base_hba1c), ^missing(base_egfr),
^missing(bmi), ^missing(out_hba1c_12m));
run;
proc freq data=_pat order=freq;
tables footprint / out=pattern_counts;
run;
/* Canonical SAS missing-data pattern report (no imputation performed). */
proc mi data=work.analytic nimpute=0;
var base_hba1c base_egfr bmi out_hba1c_12m;
run;Citations
- [1]Rubin DB. Inference and missing data. Biometrika. 1976;63(3):581-592.
- [2]Little RJA. A test of missing completely at random for multivariate data with missing values. Journal of the American Statistical Association. 1988;83(404):1198-1202.
- [3]Sterne JAC, White IR, Carlin JB, et al. Multiple imputation for missing data in epidemiological and clinical research: potential and pitfalls. BMJ. 2009;338:b2393.
- [5]Pedersen AB, Mikkelsen EM, Cronin-Fenton D, et al. Missing data and multiple imputation in clinical epidemiological research. Clinical Epidemiology. 2017;9:157-166.
- [6]Hughes RA, Heron J, Sterne JAC, Tilling K. Accounting for missing data in statistical analyses: multiple imputation is not always the answer. International Journal of Epidemiology. 2019;48(4):1294-1304.