library(naniar)
library(palmerpenguins)
vis_miss(penguins) # row-by-column heatmap
sex variable carries most of the missingness.
Absence of evidence is not evidence of absence.
Douglas G. Altman and J. Martin Bland, British Medical Journal (1995)
The survey of peer US biostatistics MS programs (Appendix B) found missing-data handling taught as a core or near-core topic at a majority of them, including dedicated courses at Michigan (University of Michigan School of Public Health, 2024) and the University of Washington (Sadinle, 2019), and coverage within the curricula at Emory (Emory University Rollins School of Public Health, 2024), Yale (Yale School of Public Health, 2024), Iowa (University of Iowa College of Public Health, 2024), UT Health Houston (UT Health Houston School of Public Health, 2024), and Florida (University of Florida College of Public Health and Health Professions, 2025). Every real clinical dataset has missing values, and the decisions around them often move point estimates more than the model choice does. This chapter is the Practicum’s response.
Answer the following questions to see if you can bypass this chapter. You can find the answers at the end of the chapter in Section 23.19.
By the end of this chapter you should be able to:
naniar::vis_miss() and naniar::gg_miss_var().mice using predictive mean matching for continuous variables and logistic regression for binary variables.Missing data is, perhaps, the least-loved topic in an MS curriculum, and the reason is that its answers are rarely clean. Complete-case analysis is simple, but it throws information away and biases estimates under anything stronger than MCAR. Multiple imputation preserves the information, but it adds a modeling layer with assumptions of its own. Sensitivity analysis is essential, but it is open-ended. There is, in short, no method here that is free of difficulty.
The pragmatic stance is: document the missingness, be honest about the mechanism, implement one principled approach (most often multiple imputation via mice), and report the sensitivity of conclusions to alternative approaches.
Missing data is the chapter where judgment matters most.
The mechanism is an assumption, not a fact. MCAR/MAR/MNAR cannot be distinguished from the observed data alone. Whatever mechanism you assume is a substantive claim about why the missingness occurred, defended by what you know about the data collection. ‘The patient was lost to follow-up’ could be MCAR (random administrative loss), MAR (loss correlated with observed baseline), or MNAR (loss correlated with the unobserved outcome). The correct response is to argue from the clinical context, not from a hypothesis test.
Imputation is modeling. When you impute, you are fitting a model for the missing values. That model has assumptions. Bad imputation introduces bias more cleanly than complete-case analysis would. The tools (mice, Amelia, mi) make imputation easy; making it correct requires attending to the imputation model’s specification.
Outcome and predictor missingness are not symmetric. Missing outcome data with MAR-on- covariates is well-handled by likelihood methods. Missing predictor data is trickier and almost always demands imputation. The two cases warrant different strategies.
Pre-specify the missing-data plan. The SAP (Chapter 24) should specify the primary missing-data approach before data access. Choosing the strategy after seeing the data – ‘we tried complete case, then MI, and report MI because it gave a smaller p-value’ – is indistinguishable from p-hacking.
These judgments are what make missing-data handling defensible rather than mechanical.
Rubin (1976) defines three missingness mechanisms. Let \(Y\) be the variable of interest; \(R\) the indicator that \(Y\) is observed; \(X\) other observed variables.
MCAR (Missing Completely At Random). \(P(R \mid Y, X) = P(R)\): missingness is independent of both observed and unobserved data. Example: a clinical CRF page is occasionally lost in the mail, with the loss process unrelated to patient or measurement.
MAR (Missing At Random). \(P(R \mid Y, X) = P(R \mid X)\): missingness depends only on observed data. Example: older patients more often miss follow-up visits than younger ones; conditional on age, missingness is independent of the missing outcome.
MNAR (Missing Not At Random). \(P(R \mid Y, X)\) depends on \(Y\) itself, even after conditioning on \(X\). Example: patients with worse symptoms are more likely to drop out because they get worse, and the dropout itself is the bad news. Conditional on observed covariates, missingness still depends on the unobserved outcome.
The three are progressively more restrictive in the methods they allow:
| Mechanism | Complete case | Likelihood / MI | Sensitivity needed? |
|---|---|---|---|
| MCAR | Unbiased | Unbiased | No |
| MAR | Biased | Unbiased | No |
| MNAR | Biased | Biased | Yes |
In practice, MCAR is rare, MAR is the default working assumption, and MNAR is what sensitivity analyses are for. The data cannot distinguish MAR from MNAR, so the choice rests on substantive reasoning.
One case that recurs in biomarker work does not fit this taxonomy comfortably, and it is worth naming because the reflex it invites is wrong. An assay reports a concentration only above its limit of detection; below that, the instrument returns not a value but the information that the value is small. This is left censoring rather than missingness. The distinction matters because the data are not absent: knowing a concentration lies below a known threshold is considerably more than knowing nothing, and an analysis that discards those observations, or that substitutes a constant such as the limit divided by the square root of two, throws away information and distorts the variance in ways multiple imputation under MAR will not repair. The methods that suit this case are its own family: regression on order statistics, censored maximum likelihood, and multiple imputation adapted for a censoring threshold. The in-house zzmesoimpute package provides a common interface across them for multiplexed immunoassay data, which is the setting where the problem is most often met. The general lesson generalizes past the package: before choosing a missing-data method, establish whether the values are missing or merely unobserved below a bound, because those are different problems with different remedies.
Before any imputation, look at the pattern. The naniar package provides ggplot-friendly diagnostics. The row-by-column heatmap is the first thing to look at: it shows at a glance which variables carry missingness and whether it clusters in particular rows.
library(naniar)
library(palmerpenguins)
vis_miss(penguins) # row-by-column heatmap
sex variable carries most of the missingness.
A per-variable bar chart quantifies what the heatmap suggests:
gg_miss_var(penguins) # bar chart of missing per variable
sex account for essentially all the missingness in the penguin data.
Two further diagnostics are worth knowing. The UpSet plot shows how missingness co-occurs across variables, and the case summary counts missing cells per row:
gg_miss_upset(penguins) # UpSet-style intersection plot
miss_case_summary(penguins) # how many missing per rowPatterns to look for:
The pattern affects strategy. Monotone missingness admits simpler imputation models (sequential regression). Arbitrary missingness needs the full multivariate machinery (mice with chained equations).
The simplest approach: drop rows with any missingness. R’s default for lm, glm, and most modeling functions is na.omit (or na.action = na.omit).
fit_cc <- lm(Ozone ~ Solar.R + Wind + Temp, data = airquality)
nrow(fit_cc$model) # rows actually usedWhen complete-case is acceptable:
When complete-case is not acceptable: substantial missingness with plausible MAR. The bias can be large.
Mean imputation replaces missing values with the sample mean. Two problems:
# do not do this
df$x[is.na(df$x)] <- mean(df$x, na.rm = TRUE)Last observation carried forward (LOCF) is deprecated except as a pre-specified sensitivity analysis. It assumes patients who drop out remain at their last observed value, which is biologically implausible for most diseases. The ICH E9(R1) addendum (International Council for Harmonisation, 2019) explicitly cautions against it, and Harrell’s author checklist (Harrell, 2024) rules it out without the sensitivity-analysis exemption, on the grounds that an assumption no one believes is not made believable by being labeled.
Single regression imputation without noise produces coefficients that are too tight: the imputed values lie exactly on a regression surface, hiding the variability that should be present.
The common theme: single imputation underestimates variance because it pretends the imputed values have no uncertainty. Multiple imputation fixes this by drawing many plausible values per missing cell.
miceMultiple imputation proceeds in three steps, shown in Figure 23.3: impute several completed datasets, analyze each one separately, then pool the results. The reason for the fan-out and recombination is the whole point of the method: the variation between the \(M\) analyses is what carries the uncertainty due to missingness that single imputation discards.
flowchart TD D["Incomplete<br/>data"] --> S1["Set 1"] D --> S2["Set 2"] D --> Sm["Set M"] S1 --> F1["Fit 1"] S2 --> F2["Fit 2"] Sm --> Fm["Fit M"] F1 --> P["Pool<br/>Rubin's rules<br/><i>within + between variance</i>"] F2 --> P Fm --> P P --> R["One pooled<br/>estimate and SE"]
The three steps in detail:
In R:
library(mice)
# step 1: impute
imp <- mice(airquality, m = 25, method = "pmm", seed = 1,
printFlag = FALSE)
# step 2: analyze each
fits <- with(imp, lm(Ozone ~ Solar.R + Wind + Temp))
# step 3: pool
pooled <- pool(fits)
summary(pooled, conf.int = TRUE)mice defaults:
method = "pmm" for continuous variables: predictive mean matching. The imputed value is drawn from the observed values whose predicted values are closest to the missing observation’s predicted value. PMM is robust to model misspecification.method = "logreg" for binary, polyreg for unordered categorical, polr for ordered categorical. mice picks defaults appropriately.m = 5 is the historical default; m = 25 or higher is the modern recommendation (Bodner, 2008): \(M\) should exceed the percentage of missing information.seed: set for reproducibility.Always include the outcome in the imputation model when imputing predictors: omitting it biases the imputation toward the null (Moons et al., 2006).
Pool the \(M\) analyses:
The \((1 + 1/M)\) factor is a finite-sample correction.
Degrees of freedom use the Barnard-Rubin adjustment (the mice default), which produces slightly conservative t-tests for moderate \(M\).
mice::pool() does all of this automatically. Inspect the components:
summary(pooled)$df # degrees of freedom per coefficient
summary(pooled)$fmi # fraction of missing information
summary(pooled)$lambda # proportion of variance attributable to missingnessfmi (fraction of missing information) is a useful summary: how much of the variance in \(\hat\beta\) is due to missing data. Values above 0.5 suggest the analysis is heavily influenced by imputation; consider sensitivity.
Using airquality:
library(naniar)
library(mice)
library(broom.mixed)
# 1. visualize pattern
vis_miss(airquality)
gg_miss_upset(airquality)
# 2. complete-case
fit_cc <- lm(Ozone ~ Solar.R + Wind + Temp, data = airquality)
broom::tidy(fit_cc, conf.int = TRUE)
nrow(fit_cc$model) # 111 rows used
# 3. multiple imputation
imp <- mice(airquality, m = 25, method = "pmm", seed = 1,
printFlag = FALSE)
fits <- with(imp, lm(Ozone ~ Solar.R + Wind + Temp))
pooled <- pool(fits)
summary(pooled, conf.int = TRUE)
nrow(airquality) # 153 rows analyzed via MI
# 4. sensitivity: delta adjustment
# (assume Ozone with missingness is systematically
# 0.5 SD lower than MAR-imputed values)
imp_delta <- mice(airquality, m = 25, method = "pmm",
seed = 1, printFlag = FALSE)
imp_delta$imp$Ozone <- imp_delta$imp$Ozone -
0.5 * sd(airquality$Ozone, na.rm = TRUE)
fits_delta <- with(imp_delta, lm(Ozone ~ Solar.R + Wind + Temp))
pool(fits_delta) |> summary()Compare the three coefficient estimates and their standard errors. If the conclusions are similar under all three, you have a robust result. If they diverge, the missing-data assumption is load-bearing and the paper should say so.
CONSORT 2010 (item 13b, randomized trials) (Schulz et al., 2010) and STROBE (item 12c, observational studies) (Elm et al., 2007) require:
A sample reporting paragraph:
Of 1,000 enrolled patients, 23 (2.3%) had missing baseline body mass index and 47 (4.7%) had missing 12-month follow-up outcome. Patterns of missingness are shown in Supplementary Figure S1. Missing baseline BMI was assumed missing at random (MAR) given age, sex, and treatment arm. Missing follow-up outcomes were imputed using multiple imputation with chained equations (\(M = 25\), predictive mean matching for continuous variables, logistic regression for binary), including all baseline covariates and the treatment indicator. Pooled estimates use Rubin’s rules. Sensitivity to MNAR was assessed via a delta adjustment of \(\pm 0.5\) SD on the imputed outcomes; results were qualitatively similar (Supplementary Table S5).
The level of detail demonstrates that you have thought about the issue, not papered over it.
CONSORT and STROBE set the floor. Harrell’s author checklist (Harrell, 2024), written from the reviewer’s side of the exchange, adds an item the reporting standards leave implicit: an incomplete record is not a disposable one. Dropping every patient with any missing covariate is a decision with consequences for both power and bias, and it requires the same justification in the methods section as an imputation model would. If the complete-case sample differs from the enrolled sample in ways the reader can check, show the comparison; if it does not differ on anything you measured, say so, and say what you could not measure.
Three standard sensitivity approaches:
Delta adjustment. After MI under MAR, shift the imputed values by \(\pm \delta\) to mimic an MNAR scenario. Refit the analysis. The result shows how robust conclusions are to the MNAR direction. Implementation: edit imp$imp after calling mice, then re-fit.
Tipping-point analysis. Vary \(\delta\) continuously and find the value at which the conclusion ‘tips’ (e.g., the treatment effect loses statistical significance). The tipping point’s clinical plausibility is the question: if it requires a \(\delta\) much larger than clinical experience suggests is realistic, the conclusion is robust.
Pattern-mixture models. Specify different imputation models for different missingness patterns. The framework is explicit about MNAR assumptions per pattern; the implementation is more involved than delta adjustment.
For a typical paper, delta adjustment with one or two values of \(\delta\) is sufficient sensitivity. For high-stakes regulatory submissions or papers where the missingness is large, tipping-point analysis is more thorough.
The SAP (Chapter 24) should specify the primary missing-data strategy before data access. A typical pre-specification:
Primary analysis will use multiple imputation with \(M = 25\) datasets, predictive mean matching for continuous variables and logistic regression for binary variables, including all outcome and covariate information. Pooling follows Rubin’s rules. Sensitivity to the MAR assumption will be assessed via a delta adjustment of \(\pm 0.5\) SD on the imputed outcome.
Specifying details (\(M\), methods, sensitivity) prevents the post-hoc choice that turns missingness handling into another researcher degree of freedom.
LLMs handle the mechanics; the substantive mechanism reasoning is the analyst’s.
Prompt 1: classifying the mechanism. Paste a missingness summary and a brief description of the data collection; ask: ‘classify each variable’s likely mechanism (MCAR / MAR / MNAR) and justify.’
What to watch for. The LLM may overreach: it cannot know the substantive context, so its classifications are guesses. Treat the output as a starting list, not an answer. Add the clinical reasoning yourself.
Verification. Discuss with the clinical collaborator. Their substantive view wins; update the classifications accordingly.
Prompt 2: writing the mice call. Describe the dataset (variable types, missingness rates) and ask: ‘write the mice call with appropriate methods per variable and an explanation of the choices.’
What to watch for. Method selection per variable type. Inclusion of the outcome in the predictor matrix. Use of m = 25 or higher rather than the historical m = 5. The LLM should know all of this; if it does not, push.
Verification. Run the call. Inspect imp$method; verify each variable has the expected method. Check imp$predictorMatrix; verify the outcome is included for predictor imputation.
Prompt 3: writing the SAP missing-data section. Describe the trial and ask the LLM to draft the missing-data subsection of the SAP.
What to watch for. The pre-specification should be specific enough that a reader cannot ask ‘which?’ on key choices. Vague language (‘appropriate imputation’) is not pre-specification. The LLM may be vague; push for specifics.
Verification. Show to a colleague who has seen FDA or EMA SAPs; their feedback recalibrates.
Three habits keep missing data a stated assumption rather than a silent one:
airquality (built-in). Visualize the missingness pattern with naniar. Fit lm(Ozone ~ Solar.R + Wind + Temp) via complete-case analysis; then via multiple imputation with m = 20. Compare coefficients and standard errors.mice::ampute() function to induce MAR missingness on a complete dataset. Fit the analysis on the full data and on the amputed-then-imputed data. Compare estimates; verify MI recovers the truth approximately.mice approach.mice package documentation at amices.org/mice, package-level reference.