Multiple imputation with vimpute: pooling, tuning and diagnostics

knitr::opts_chunk$set(collapse = TRUE, comment = "#>",
                      fig.width = 7, fig.height = 4.5)
run_mice <- requireNamespace("mice", quietly = TRUE)

This vignette walks through a complete multiple-imputation workflow with vimpute() — every chunk below is executed: simulate missingness with a known mechanism, impute multiply, check convergence and calibration, pool with Rubin's rules, and validate against the truth.

library(VIM)
set.seed(2026)

data(sleep, package = "VIM")
truth <- na.omit(sleep[, c("BodyWgt", "BrainWgt", "NonD", "Sleep", "Span", "Gest")])
truth <- as.data.frame(scale(truth))   # common scale keeps the example compact
nrow(truth)

Simulate missingness with a known mechanism

makeMissing() generates MCAR/MAR/MNAR missingness in complete data — here MAR: the probability that Sleep and Span go missing grows with the other (observed) variables. The returned "where" attribute marks the amputed cells, so the truth stays available for validation.

amp <- makeMissing(truth, prop = 0.25, mechanism = "MAR",
                   vars = c("Sleep", "Span"), seed = 1)
colSums(is.na(amp))

Multiple imputation

m = 5 imputations; with m > 1, each imputation refits its models on a bootstrap sample (boot = TRUE is the default for multiple imputation) and the default uncert = "pmm" draws from observed donor values, so the imputations differ between runs (a prerequisite for Rubin's rules). Per-variable settings use the spec interface; three sequential iterations give the convergence chains something to show.

mi <- vimpute(amp,
              spec = list(.default = vs_ranger(num.trees = 100)),
              m = 5, sequential = TRUE, nseq = 3, seed = 7, verbose = FALSE)
mi

print() already answers the practitioner's first question — can I trust this? — with a per-variable model-quality metric (NRMSE, out-of-bag for ranger; PFC for factors).

Convergence and distribution diagnostics

plot(mi)             # chains: mean/sd of the imputed values per iteration
plot(mi, "density")  # observed (blue, bold) vs per-imputation imputed (red)

Pooling with Rubin's rules

with() fits a model on each completed dataset and returns a mice-compatible mira, so the standard pipeline applies unchanged.

fits <- with(mi, lm(Sleep ~ BodyWgt + Span))
pooled <- mice::pool(fits)
summary(pooled)

Alternatively, convert the whole object: vim_as_mids(mi) yields a genuine mice::mids for any downstream mice infrastructure.

mids <- vim_as_mids(mi)
class(mids)

Hyperparameter tuning inside the imputation

Tuning is controlled per variable (spec) and per call (tune_control); with m > 1 the tuner runs once and all imputations share its parameters. The tuning log records what was chosen.

mi_tuned <- vimpute(amp,
                    spec = list(Sleep    = vs_ranger(num.trees = 100, tune = TRUE),
                                .default = vs_ranger(num.trees = 100)),
                    tune_control = vimpute_tune_control(budget = 4, folds = 3),
                    m = 2, sequential = FALSE, seed = 7, verbose = FALSE)
tl <- mi_tuned$tuning_log
tail(tl, 1)[[1]][c("variable", "tuned", "tuned_better", "n_evals", "folds")]

Calibration: overimputation

overimpute() treats the observed cells of a variable as missing (fold by fold), imputes them multiply, and compares observed values with the imputed intervals — a model-agnostic calibration check that needs no ground truth.

ov <- overimpute(amp, "Sleep",
                 spec = list(.default = vs_ranger(num.trees = 100)),
                 draws = 5, folds = 3, sequential = FALSE, seed = 3)
ov
plot(ov)

Validation against the truth

Because the missingness was simulated, the imputations can be scored against the true values — the loop makeMissing()vimpute()evaluation() that any benchmark study needs.

completed <- vim_complete(mi, 1)
evaluation(truth, completed, where = attr(amp, "where"))

A note on assumptions

Like all conditional imputation, vimpute() assumes MAR (which includes MCAR). Under MNAR — missingness driven by the unobserved values themselves — estimates can be biased and no imputation method can repair this from the observed data alone; makeMissing(mechanism = "MNAR") supports exactly the sensitivity simulations such situations call for.



Try the VIM package in your browser

Any scripts or data that you put into this service are public.

VIM documentation built on Sept. 2, 2026, 5:07 p.m.