tar_nlmixr_multimodel() lets you declare several
candidate models for the same dataset in one call. Each
model gets its own simplify/fit/relabel chain, but the
data-simplification step is shared when possible, and within-list piping
(models[["A"]] |> ini(...) referenced from another
entry) is resolved into a dependency on the prior fit so the data does
not have to be re-prepared.
The output is a list of targets you can drop into your
_targets.R plan.
library(targets)
library(tarchetypes)
library(nlmixr2targets)
pheno_base <- function() {
ini({
lcl <- log(0.008); label("Typical clearance")
lvc <- log(0.6); label("Typical volume of distribution")
etalcl + etalvc ~ c(1, 0.01, 1)
cpaddSd <- 0.1; label("Additive residual SD")
})
model({
cl <- exp(lcl + etalcl)
vc <- exp(lvc + etalvc)
kel <- cl / vc
d / dt(central) <- -kel * central
cp <- central / vc
cp ~ add(cpaddSd)
})
}
list(
tar_nlmixr_multimodel(
name = candidate_fits,
data = nlmixr2data::pheno_sd,
est = "saem",
"Base" = pheno_base,
"Base + tighter residual prior" = pheno_base |> ini(cpaddSd = 0.05),
"Base + alternate residual" = pheno_base |> model({
cp ~ prop(cpaddSd)
}, append = TRUE)
),
tar_target(
aic_table,
data.frame(
model = names(candidate_fits),
aic = vapply(candidate_fits, AIC, numeric(1)),
bic = vapply(candidate_fits, BIC, numeric(1)),
ofv = vapply(candidate_fits, function(f) f$objDf$OBJF[1], numeric(1))
)
)
)After tar_make(), the aic_table target
gives you a compact summary of the candidates. Add dAIC or
weight columns to taste.
A common pattern is to pull a particular fixed effect out of every candidate fit to compare:
tar_target(
clearance_estimates,
data.frame(
model = names(candidate_fits),
lcl = vapply(
candidate_fits,
function(f) f$ui$iniDf$est[f$ui$iniDf$name == "lcl"],
numeric(1)
)
)
)This works because tar_nlmixr_multimodel() returns a
target whose value is a named list of fits.
If the second model is an edit of the first, refer to the first by
its key inside the same tar_nlmixr_multimodel() call:
tar_nlmixr_multimodel(
name = candidate_fits,
data = nlmixr2data::pheno_sd,
est = "saem",
"Base" = pheno_base,
"Tighter residual" =
candidate_fits[["Base"]] |> ini(cpaddSd = 0.05)
)Behind the scenes, nlmixr2targets rewrites
candidate_fits[["Base"]] to a dependency on the base
model’s _fit_simple target. Iteratively resolving these
references is what makes within-list piping work without forcing the
entire pipeline to re-run when one model changes.
If you write a circular reference (A piping from B and B piping from A) the function errors out at construction time with a clear message.
tar_nlmixr_multimodel() is scoped to one dataset. If you
have several datasets, use one call per dataset (with distinct
names) and combine the resulting lists at the plan
level.tar_outdated() aggressively when
iterating. Cosmetic edits (labels, metadata) do not invalidate
_fit_simple thanks to the strip-restore behaviour. See
vignette("caching", package = "nlmixr2targets").nlmixr2targets_cache_status() and
nlmixr2targets_cache_prune() periodically.