| loo | R Documentation |
Computes leave-one-out (LOO) cross-validation for a fitted INLAvaan model from a single fit, with no refitting and no sampling, via a Taylor approximation of the case-deletion posterior around the Laplace summary. Single-level models are scored per subject (leave-one-subject-out, LOSO); two-level models are scored per cluster (leave-one-cluster-out, LOCO).
loo(x, ...)
## S3 method for class 'INLAvaan'
loo(
x,
type = c("auto", "loso", "loco"),
units = NULL,
second_order = TRUE,
theta = NULL,
Omega = NULL,
cores = NULL,
verbose = FALSE,
...
)
## S3 method for class 'inlavaan_internal'
loo(
x,
type = c("auto", "loso", "loco"),
units = NULL,
second_order = TRUE,
theta = NULL,
Omega = NULL,
cores = NULL,
verbose = FALSE,
...
)
add_loo(object, cores = NULL, verbose = FALSE)
## S3 method for class 'inlavaan_loo'
print(x, ...)
## S3 method for class 'inlavaan_loo'
summary(object, ...)
x |
A fitted INLAvaan object (or its |
... |
Not used, beyond catching the deprecated argument name
|
type |
Unit type: |
units |
Optional integer vector of unit indices to score; defaults to all units. For LOSO these are case numbers (row numbers of the analysed dataset); for LOCO, cluster positions. |
second_order |
Logical; compute the second-order correction
(default |
theta, Omega |
Optional posterior mean vector and covariance matrix
(in the unconstrained parameter space, as stored in |
cores |
Number of cores for the Hessian stage. The default |
verbose |
Logical; print progress (default |
object |
A fitted INLAvaan object, or an |
For a unit u (a subject for LOSO, a cluster for LOCO) with
log-likelihood contribution \ell_u(\theta), score s_u and Hessian
H_u evaluated at the posterior summary (\theta^*, \Omega), the
log conditional predictive ordinate is approximated to first and second order
by
\log \mathrm{CPO}_u^{(1)} = \ell_u - \tfrac{1}{2} s_u' \Omega s_u,
\log \mathrm{CPO}_u^{(2)} = \ell_u
- \tfrac{1}{2} s_u' (\Omega^{-1} + H_u)^{-1} s_u
+ \tfrac{1}{2} \log |I + \Omega H_u|.
The reported elpd_loo is the sum of the second-order terms (first-order
when second_order = FALSE), with standard error
\sqrt{n \, \mathrm{var}(\log \mathrm{CPO}_u)} and looic
= -2 \, \mathrm{elpd}. p_loo is the loo package's effective
number of parameters,
p_{\mathrm{loo}} = \sum_u (\mathrm{lpd}_u - \log \mathrm{CPO}_u), where
\mathrm{lpd}_u is the analogous Taylor approximation of the
full-posterior pointwise log predictive density – the same definition
loo::loo() reports, and not the p_D of the DIC.
\log \mathrm{CPO}_u^{(2)} exists exactly when \Omega^{-1} + H_u
is positive definite (recorded in per_unit$ok) and
\mathrm{lpd}_u^{(2)} exactly when \Omega^{-1} - H_u is. A unit
failing the former drops every estimate to first order over all units (with a
warning), while one failing only the latter contributes its first-order
difference to p_loo.
The leverages k_max, k_min, and k_sum read these conditions off the
spectrum of -\Omega H_u (k_max < 1, k_min > -1), with k_sum
summing across units to the trace form of p_D. p_loo (cross-product
form) and p_D (second-derivative form) agree only in the
correct-specification limit, and printing a result reports the
first-to-second-order gap against its limit p_D/2 as a free check on
the Taylor truncation. Because the first-order elpd overstates the truth by
\tfrac12 p_D in the limit, keep second_order = TRUE whenever models
of different dimension are compared.
type = "auto" resolves to the marginal per-cluster "loco" for two-level
fits and per-subject "loso" otherwise. Forcing "loso" on a two-level
model scores the conditional predictive of Merkle, Furr & Rabe-Hesketh
(2019) instead, and warns. Multigroup units are scored against their own
group's implied moments and identified by case number, so compare() pairs
them across fits.
The score follows the fitted likelihood's treatment of exogenous
covariates, i.e. joint under fixed.x = FALSE, conditional under
fixed.x = TRUE (recorded as "joint" or "conditional" in the result's
flavour field), and the two flavours are never comparable (compare()
refuses to mix them).
Supplying theta/Omega evaluates the LOO at an arbitrary Gaussian
posterior summary (a singular Omega is restricted to its non-degenerate
block), the building block for refit-free submodel scoring. Sigma is
the deprecated former name of Omega, still accepted through ... with
a warning.
The LOO is stored with the fit when inlavaan()'s test includes
"loo" or "waic" (e.g. test = "full"), which also stores the WAIC
(see waic()) from the same Taylor pass, or afterwards with
add_loo(); loo(fit) with default arguments then returns the stored
result. Under the default test = "standard" nothing is stored and
loo(fit) computes it on demand.
The default cores = NULL runs serially, and cores > 1 parallelises the
Hessian stage. Supported models are continuous-indicator models fitted with
the ML estimator, single- or two-level, single-group or multigroup
(multigroup two-level models are not supported yet).
An object of class inlavaan_loo: a list with elements
per_unitData frame of pointwise results, one row per unit:
unitCase number for LOSO, cluster position for LOCO.
groupGroup membership (multigroup fits only).
nobs1 for LOSO, the cluster size for LOCO.
l_starUnit log-likelihood at the summary.
score_normNorm of the unit score s_u.
lpd_1, lpd_2Pointwise log predictive density, at first and second order.
log_cpo_1, log_cpo_2Pointwise LOO contributions, at first and second order.
det_term\tfrac12 \log |I + \Omega H_u|, the
determinant term of the second-order score.
k_max, k_min, k_sumLeverage diagnostics (see Details).
k_ssq\mathrm{tr}[(\Omega H_u)^2], consumed by
the closed-form waic() penalty.
okWhether the second-order \log \mathrm{CPO}
exists.
estimatesMatrix with rows elpd_loo, p_loo, looic and
columns Estimate, SE, at the highest order available to each.
elpd_1, elpd_2, se_1, se_2, p_loo_1, p_loo_2First- and second-order aggregates; the second-order ones are NA
when any \log \mathrm{CPO}_u^{(2)} does not exist.
elpd_gap, pd_traceThe two sides of the curvature check
(see Details); both are NA at first order and partial totals
under a units subset.
type, flavour, n_units, n_groups, n_ok, n_lpd_ok,
second_order, use_second, theta_overriddenMetadata.
summary() is an alias for print(): it prints the same output
and returns the result invisibly.
add_loo() returns a copy of object with the LOO and WAIC
results stored alongside the fit (the input object is unchanged);
reassign it, e.g. fit <- add_loo(fit). Only the default LOO is
stored, so the stored results always match loo(fit) and waic(fit).
Alhyari, M., Jamil, H., Montcho, H., & Rue, H. (2026). Deterministic leave-one-cluster-out cross-validation for multilevel Bayesian structural equation models. arXiv. (Preprint forthcoming; placeholder.)
Merkle, E. C., Furr, D., & Rabe-Hesketh, S. (2019). Bayesian comparison of latent variable models: Conditional versus marginal likelihoods. Psychometrika, 84(3), 802–829. \Sexpr[results=rd]{tools:::Rd_expr_doi("10.1007/s11336-019-09679-0")}
fitmeasures(), compare(), inlavaan()
HS.model <- "
visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9
"
utils::data("HolzingerSwineford1939", package = "lavaan")
fit <- acfa(HS.model, HolzingerSwineford1939, meanstructure = TRUE)
# Leave-one-subject-out (LOSO) from the single fit -- no refitting
res <- loo(fit)
res
head(res$per_unit)
# Score a submodel without refitting: condition the Laplace summary on the
# visual ~~ speed covariance being zero, then evaluate at that summary
int <- get_inlavaan_internal(fit)
theta <- int$theta_star
Omega <- int$Sigma_theta
p <- which(names(coef(fit)) == "visual~~speed")
theta_c <- theta - Omega[, p] * (theta[p] / Omega[p, p])
Omega_c <- Omega - tcrossprod(Omega[, p]) / Omega[p, p]
loo(fit, theta = theta_c, Omega = Omega_c)
# Two-level models are scored per cluster (LOCO) automatically
utils::data("Demo.twolevel", package = "lavaan")
model2l <- "
level: 1
fw =~ y1 + y2 + y3
fw ~ x1 + x2 + x3
level: 2
fb =~ y1 + y2 + y3
fb ~ w1 + w2
"
fit2l <- asem(model2l, Demo.twolevel, cluster = "cluster",
meanstructure = TRUE, fixed.x = FALSE)
loo(fit2l)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.