inst/tinytest/test_vimpute_uncert_default.R

## Skipped on CRAN (check-time budget): runs locally and on CI with NOT_CRAN=true
## (devtools::check()/test(), GitHub Actions) -- see tests/tinytest.R.
if (!at_home()) exit_file("skipped on CRAN: long-running (run with NOT_CRAN=true)")

library(VIM)

## Wave 2: safe default uncert = "pmm" (audit P2 "safe defaults"; Matthias's
## call 2026-07-07: deliberate, NEWS-documented change at 7.3.0).
## The old default (uncert = "none", pmm = FALSE) imputed numeric targets with
## deterministic conditional means -- imputed values cluster at the regression
## surface, understating variability and losing any density-overlay comparison
## against mice. The default is now a PMM draw among the 5 nearest donors
## (random draw), so default imputations are observed donor values.
## uncert = "none" restores the old behaviour explicitly.

data(sleep, package = "VIM")
d <- sleep[, c("Sleep", "Dream", "Span", "BodyWgt")]
miss_sleep <- which(is.na(d$Sleep))
obs_sleep <- d$Sleep[!is.na(d$Sleep)]

## --- the default is pmm -------------------------------------------------------
expect_identical(formals(vimpute)$uncert, "pmm")

## --- PMM property: default numeric imputations are observed donor values ------
out <- vimpute(d, method = "ranger", sequential = FALSE, seed = 1,
               imp_var = FALSE, verbose = FALSE)
expect_true(all(out$Sleep[miss_sleep] %in% obs_sleep),
            info = "default imputations are not donor values (PMM not applied)")
## reproducible under seed
out2 <- vimpute(d, method = "ranger", sequential = FALSE, seed = 1,
                imp_var = FALSE, verbose = FALSE)
expect_identical(out, out2)

## --- uncert = "none" restores deterministic conditional-mean behaviour --------
out_none <- vimpute(d, method = "ranger", sequential = FALSE, seed = 1,
                    imp_var = FALSE, verbose = FALSE, uncert = "none")
expect_false(all(out_none$Sleep[miss_sleep] %in% obs_sleep))

## --- pmm = TRUE with the DEFAULT uncert must not warn about a conflict --------
w1 <- character()
invisible(withCallingHandlers(
  vimpute(d, method = "ranger", sequential = FALSE, pmm = TRUE, seed = 1,
          imp_var = FALSE, verbose = FALSE),
  warning = function(cond) {
    w1 <<- c(w1, conditionMessage(cond)); invokeRestart("muffleWarning")
  }
))
expect_false(any(grepl("takes precedence", w1)),
             info = "spurious pmm-vs-uncert warning with the default uncert")

## --- explicitly setting both still warns --------------------------------------
expect_warning(
  vimpute(d, method = "ranger", sequential = FALSE, pmm = TRUE,
          uncert = "normalerror", seed = 1, imp_var = FALSE, verbose = FALSE),
  "takes precedence"
)

## --- m > 1 defaults are now proper-ish: stochastic, no improper-MI warning ----
w2 <- character()
res <- withCallingHandlers(
  vimpute(d, method = "ranger", sequential = FALSE, m = 2, seed = 2,
          imp_var = FALSE, verbose = FALSE),
  warning = function(cond) {
    w2 <<- c(w2, conditionMessage(cond)); invokeRestart("muffleWarning")
  }
)
expect_false(any(grepl("m > 1 without", w2)),
             info = "default m > 1 still triggers the improper-MI warning")
expect_inherits(res, "vimmi")
expect_false(identical(res$imp[["Sleep"]][[1]], res$imp[["Sleep"]][[2]]),
             info = "default m > 1 imputations are identical (not stochastic)")

## --- wrappers keep their deterministic contract (uncert pinned to "none") -----
set.seed(1)
rf <- rangerImpute(Sleep ~ Dream + Span + BodyWgt, sleep, imp_var = FALSE)
expect_false(all(rf$Sleep[miss_sleep] %in% obs_sleep),
             info = "rangerImpute silently switched to PMM draws")

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.