| Title: | Approximate Bayesian Latent Variable Analysis |
|---|---|
| Description: | Implements approximate Bayesian inference for Structural Equation Models (SEM) using a custom adaptation of the Integrated Nested Laplace Approximation (Rue et al., 2009) <doi:10.1111/j.1467-9868.2008.00700.x> as described in Jamil and Rue (2026a) <doi:10.48550/arXiv.2603.25690>. Provides a computationally efficient alternative to Markov Chain Monte Carlo (MCMC) for Bayesian estimation, allowing users to fit latent variable models using the 'lavaan' syntax. See also the companion paper on implementation and workflows, Jamil and Rue (2026b) <doi:10.48550/arXiv.2604.00671>. |
| Authors: | Haziq Jamil [aut, cre, cph] (ORCID: <https://orcid.org/0000-0003-3298-1010>), Håvard Rue [ctb] (ORCID: <https://orcid.org/0000-0002-0222-1881>, Statistical and computational methodology), Alvin Bong [ctb] (Initial site build) |
| Maintainer: | Haziq Jamil <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.3.2.9006 |
| Built: | 2026-10-09 16:40:26 UTC |
| Source: | https://github.com/haziqj/inlavaan |
Fit an Approximate Bayesian Confirmatory Factor Analysis Model
acfa( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )acfa( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )
model |
A description of the user-specified model. Typically, the model
is described using the lavaan model syntax. See
|
data |
An optional data frame containing the observed variables used in the model. If some variables are declared as ordered factors, lavaan will treat them as ordinal variables. |
dp |
Default prior distributions for the different types of model
parameters; a named character vector as returned by |
test |
Character vector naming the post-estimation quantities to
compute and store with the fit. The atoms are |
vb_correction |
Logical indicating whether to apply a variational Bayes
correction for the posterior mean vector of estimates. Defaults to |
n_qmc |
Number of quasi-Monte Carlo nodes used by the VB mean
correction. Defaults to |
vb_method |
Integration rule for the VB mean correction. |
marginal_method |
The method for approximating the marginal posterior
distributions. Options include |
marginal_correction |
Which type of correction to use when fitting the
skew-normal or two-piece Gaussian marginals. |
nsamp |
The number of samples to draw for all sampling-based approaches (including posterior sampling for model fit indices). |
samp_copula |
Logical. When |
samp_norta |
Logical. When |
cov_as_cor |
Logical. Residual and latent-disturbance covariance
parameters ( |
sn_fit_ngrid |
Number of grid points to lay out per dimension when
fitting the skew-normal marginals. A finer grid gives a better fit at the
cost of more joint-log-posterior evaluations. Defaults to |
sn_fit_logthresh |
The log-threshold for fitting the skew-normal. Points
with log-posterior drop below this threshold (relative to the maximum) will
be excluded from the fit. Defaults to |
sn_fit_temp |
Temperature parameter for fitting the skew-normal.
Defaults to |
sn_fit_sample |
Logical. When |
control |
A list of control parameters for the optimiser. For the
default |
verbose |
Logical indicating whether to print progress messages. |
debug |
Logical indicating whether to return debug information. |
add_priors |
Logical indicating whether to include prior densities in the posterior computation. |
optim_method |
The optimisation method to use for finding the posterior
mode. Options include |
numerical_grad |
Logical indicating whether to use numerical gradients
for the optimisation. Defaults to |
cores |
Integer or |
ppp_method |
How the PPP of a two-level model scores the observed and
the replicate data against the saturated model. |
ppp_nsamp |
The number of posterior draws, each with one replicate data
set, that the PPP of a two-level model uses. Defaults to |
... |
Additional arguments to be passed to the lavaan model fitting function. |
The acfa() function is a wrapper for the more general inlavaan()
function, using the following default arguments:
int.ov.free = TRUE
int.lv.free = FALSE
auto.fix.first = TRUE (unless std.lv = TRUE)
auto.fix.single = TRUE
auto.var = TRUE
auto.cov.lv.x = TRUE
auto.efa = TRUE
auto.th = TRUE
auto.delta = TRUE
auto.cov.y = TRUE
For further information regarding these arguments, please refer to the
lavaan::lavOptions() documentation.
An S4 object of class INLAvaan which is a subclass of the
lavaan class.
Typically, users will interact with the specific latent variable
model functions instead, including acfa(), asem(), and agrowth().
# The famous Holzinger and Swineford (1939) example HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") # Fit a CFA model with standardised latent variables fit <- acfa(HS.model, data = HolzingerSwineford1939, std.lv = TRUE, nsamp = 100) summary(fit)# The famous Holzinger and Swineford (1939) example HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") # Fit a CFA model with standardised latent variables fit <- acfa(HS.model, data = HolzingerSwineford1939, std.lv = TRUE, nsamp = 100) summary(fit)
Fit an Approximate Bayesian Growth Curve Model
agrowth( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )agrowth( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )
model |
A description of the user-specified model. Typically, the model
is described using the lavaan model syntax. See
|
data |
An optional data frame containing the observed variables used in the model. If some variables are declared as ordered factors, lavaan will treat them as ordinal variables. |
dp |
Default prior distributions for the different types of model
parameters; a named character vector as returned by |
test |
Character vector naming the post-estimation quantities to
compute and store with the fit. The atoms are |
vb_correction |
Logical indicating whether to apply a variational Bayes
correction for the posterior mean vector of estimates. Defaults to |
n_qmc |
Number of quasi-Monte Carlo nodes used by the VB mean
correction. Defaults to |
vb_method |
Integration rule for the VB mean correction. |
marginal_method |
The method for approximating the marginal posterior
distributions. Options include |
marginal_correction |
Which type of correction to use when fitting the
skew-normal or two-piece Gaussian marginals. |
nsamp |
The number of samples to draw for all sampling-based approaches (including posterior sampling for model fit indices). |
samp_copula |
Logical. When |
samp_norta |
Logical. When |
cov_as_cor |
Logical. Residual and latent-disturbance covariance
parameters ( |
sn_fit_ngrid |
Number of grid points to lay out per dimension when
fitting the skew-normal marginals. A finer grid gives a better fit at the
cost of more joint-log-posterior evaluations. Defaults to |
sn_fit_logthresh |
The log-threshold for fitting the skew-normal. Points
with log-posterior drop below this threshold (relative to the maximum) will
be excluded from the fit. Defaults to |
sn_fit_temp |
Temperature parameter for fitting the skew-normal.
Defaults to |
sn_fit_sample |
Logical. When |
control |
A list of control parameters for the optimiser. For the
default |
verbose |
Logical indicating whether to print progress messages. |
debug |
Logical indicating whether to return debug information. |
add_priors |
Logical indicating whether to include prior densities in the posterior computation. |
optim_method |
The optimisation method to use for finding the posterior
mode. Options include |
numerical_grad |
Logical indicating whether to use numerical gradients
for the optimisation. Defaults to |
cores |
Integer or |
ppp_method |
How the PPP of a two-level model scores the observed and
the replicate data against the saturated model. |
ppp_nsamp |
The number of posterior draws, each with one replicate data
set, that the PPP of a two-level model uses. Defaults to |
... |
Additional arguments to be passed to the lavaan model fitting function. |
The asem() function is a wrapper for the more general inlavaan()
function, using the following default arguments:
meanstructure = TRUE
int.ov.free = FALSE
int.lv.free = TRUE
auto.fix.first = TRUE (unless std.lv = TRUE)
auto.fix.single = TRUE
auto.var = TRUE
auto.cov.lv.x = TRUE
auto.efa = TRUE
auto.th = TRUE
auto.delta = TRUE
auto.cov.y = TRUE
An S4 object of class INLAvaan which is a subclass of the
lavaan class.
Typically, users will interact with the specific latent variable
model functions instead, including acfa(), asem(), and agrowth().
# Linear growth model with a time-varying covariate mod <- " # Intercept and slope with fixed coefficients i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4 s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4 # (Latent) regressions i ~ x1 + x2 s ~ x1 + x2 # Time-varying covariates t1 ~ c1 t2 ~ c2 t3 ~ c3 t4 ~ c4 " utils::data("Demo.growth", package = "lavaan") str(Demo.growth) fit <- agrowth(mod, data = Demo.growth, nsamp = 100) summary(fit)# Linear growth model with a time-varying covariate mod <- " # Intercept and slope with fixed coefficients i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4 s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4 # (Latent) regressions i ~ x1 + x2 s ~ x1 + x2 # Time-varying covariates t1 ~ c1 t2 ~ c2 t3 ~ c3 t4 ~ c4 " utils::data("Demo.growth", package = "lavaan") str(Demo.growth) fit <- agrowth(mod, data = Demo.growth, nsamp = 100) summary(fit)
Convert function to single string
as_fun_string(f)as_fun_string(f)
f |
Function to convert. |
A single character vector representing the function.
f <- function(x) { x^2 + 1 } as_fun_string(f)f <- function(x) { x^2 + 1 } as_fun_string(f)
Fit an Approximate Bayesian Structural Equation Model
asem( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )asem( model, data, dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )
model |
A description of the user-specified model. Typically, the model
is described using the lavaan model syntax. See
|
data |
An optional data frame containing the observed variables used in the model. If some variables are declared as ordered factors, lavaan will treat them as ordinal variables. |
dp |
Default prior distributions for the different types of model
parameters; a named character vector as returned by |
test |
Character vector naming the post-estimation quantities to
compute and store with the fit. The atoms are |
vb_correction |
Logical indicating whether to apply a variational Bayes
correction for the posterior mean vector of estimates. Defaults to |
n_qmc |
Number of quasi-Monte Carlo nodes used by the VB mean
correction. Defaults to |
vb_method |
Integration rule for the VB mean correction. |
marginal_method |
The method for approximating the marginal posterior
distributions. Options include |
marginal_correction |
Which type of correction to use when fitting the
skew-normal or two-piece Gaussian marginals. |
nsamp |
The number of samples to draw for all sampling-based approaches (including posterior sampling for model fit indices). |
samp_copula |
Logical. When |
samp_norta |
Logical. When |
cov_as_cor |
Logical. Residual and latent-disturbance covariance
parameters ( |
sn_fit_ngrid |
Number of grid points to lay out per dimension when
fitting the skew-normal marginals. A finer grid gives a better fit at the
cost of more joint-log-posterior evaluations. Defaults to |
sn_fit_logthresh |
The log-threshold for fitting the skew-normal. Points
with log-posterior drop below this threshold (relative to the maximum) will
be excluded from the fit. Defaults to |
sn_fit_temp |
Temperature parameter for fitting the skew-normal.
Defaults to |
sn_fit_sample |
Logical. When |
control |
A list of control parameters for the optimiser. For the
default |
verbose |
Logical indicating whether to print progress messages. |
debug |
Logical indicating whether to return debug information. |
add_priors |
Logical indicating whether to include prior densities in the posterior computation. |
optim_method |
The optimisation method to use for finding the posterior
mode. Options include |
numerical_grad |
Logical indicating whether to use numerical gradients
for the optimisation. Defaults to |
cores |
Integer or |
ppp_method |
How the PPP of a two-level model scores the observed and
the replicate data against the saturated model. |
ppp_nsamp |
The number of posterior draws, each with one replicate data
set, that the PPP of a two-level model uses. Defaults to |
... |
Additional arguments to be passed to the lavaan model fitting function. |
The asem() function is a wrapper for the more general inlavaan()
function, using the following default arguments:
int.ov.free = TRUE
int.lv.free = FALSE
auto.fix.first = TRUE (unless std.lv = TRUE)
auto.fix.single = TRUE
auto.var = TRUE
auto.cov.lv.x = TRUE
auto.efa = TRUE
auto.th = TRUE
auto.delta = TRUE
auto.cov.y = TRUE
For further information regarding these arguments, please refer to the
lavaan::lavOptions() documentation.
An S4 object of class INLAvaan which is a subclass of the
lavaan class.
Typically, users will interact with the specific latent variable
model functions instead, including acfa(), asem(), and agrowth().
# The industrialization and Political Democracy Example from Bollen (1989), page # 332 model <- " # Latent variable definitions ind60 =~ x1 + x2 + x3 dem60 =~ y1 + a*y2 + b*y3 + c*y4 dem65 =~ y5 + a*y6 + b*y7 + c*y8 # (Latent) regressions dem60 ~ ind60 dem65 ~ ind60 + dem60 # Residual correlations y1 ~~ y5 y2 ~~ y4 + y6 y3 ~~ y7 y4 ~~ y8 y6 ~~ y8 " utils::data("PoliticalDemocracy", package = "lavaan") fit <- asem(model, PoliticalDemocracy, test = "none") summary(fit)# The industrialization and Political Democracy Example from Bollen (1989), page # 332 model <- " # Latent variable definitions ind60 =~ x1 + x2 + x3 dem60 =~ y1 + a*y2 + b*y3 + c*y4 dem65 =~ y5 + a*y6 + b*y7 + c*y8 # (Latent) regressions dem60 ~ ind60 dem65 ~ ind60 + dem60 # Residual correlations y1 ~~ y5 y2 ~~ y4 + y6 y3 ~~ y7 y4 ~~ y8 y6 ~~ y8 " utils::data("PoliticalDemocracy", package = "lavaan") fit <- asem(model, PoliticalDemocracy, test = "none") summary(fit)
Compute posterior distributions of Bayesian fit indices for an INLAvaan
model, analogous to blavaan::blavFitIndices().
bfit_indices( object, baseline.model = NULL, rescale = c("devM", "MCMC"), nsamp = NULL, samp_copula = TRUE ) ## S3 method for class 'bfit_indices' summary(object, ...) ## S3 method for class 'bfit_indices' print(x, ...)bfit_indices( object, baseline.model = NULL, rescale = c("devM", "MCMC"), nsamp = NULL, samp_copula = TRUE ) ## S3 method for class 'bfit_indices' summary(object, ...) ## S3 method for class 'bfit_indices' print(x, ...)
object |
An object of class INLAvaan. |
baseline.model |
The baseline (null) model that the incremental fit
indices (BCFI, BTLI, BNFI) are scaled against. |
rescale |
Character string controlling how the Bayesian chi-square
is rescaled. |
nsamp |
Number of posterior samples to draw. Defaults to the value used when fitting the model. |
samp_copula |
Logical. When |
... |
Additional arguments passed to methods. |
x |
An object of class |
For a random-slope model, which has no saturated model, the chi-square and the number of sample moments come from the unrestricted random-coefficient model with the same random-effects design, fitted by maximum likelihood. This reference is INLAvaan's own construction. It is available on the closed-form route only, for models without between-only outcomes, and needs enough clusters for its parameters.
An S3 object of class "bfit_indices" containing:
indicesNamed list of numeric vectors (one per posterior sample) for each computed fit index.
detailsList with chisq (per-sample deviance), df, pD,
rescale, and nsamp.
Use summary() to obtain a table of posterior summaries (Mean, SD,
quantiles, Mode) for each index.
lavaan::fitMeasures(), blavaan::blavFitIndices(),
fitmeasures(), compare()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, verbose = FALSE) # Absolute fit indices bf <- bfit_indices(fit) bf summary(bf)HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, verbose = FALSE) # Absolute fit indices bf <- bfit_indices(fit) bf summary(bf)
Compare two or more Bayesian SEM fitted with INLAvaan, reporting model-fit statistics and (optionally) fit indices side by side.
compare(x, y, ..., fit.measures = NULL, loo = FALSE) ## S4 method for signature 'INLAvaan' compare(x, y, ..., fit.measures = NULL, loo = FALSE) ## S4 method for signature 'INLAvaan' anova(object, ...)compare(x, y, ..., fit.measures = NULL, loo = FALSE) ## S4 method for signature 'INLAvaan' compare(x, y, ..., fit.measures = NULL, loo = FALSE) ## S4 method for signature 'INLAvaan' anova(object, ...)
x, y, ...
|
Two or more INLAvaan (or |
fit.measures |
Character vector of additional fit-measure names to
include (e.g. |
loo |
Logical; if |
object |
An INLAvaan object (the |
All models appear in the comparison table. When incremental fit indices
(BCFI, BTLI, BNFI) are requested via fit.measures, they are scaled
against the independence (null) model, fitted once on the data of the
first model and shared by every model in the table (see
bfit_indices()).
The default table always includes:
npar: Number of free parameters.
Marg.Loglik: Approximated marginal log-likelihood.
logBF: Natural-log Bayes factor relative to the best model.
DIC / pD: Deviance Information Criterion and effective number
of parameters (when the fit computed the DIC, i.e. test included
"dic" during fitting; the default "standard" does).
Fit all models with the same vb_correction setting. compare() warns
when they differ.
Marginal likelihoods, Bayes factors and DIC of fits with composites (<~)
treat the indicator (co)variances that lavaan fixes at their sample values
as known, so compare() warns unless all models fix the same ones. The LOO
comparison (loo = TRUE) remains valid across such models.
Set fit.measures to a character vector of measure names (anything
returned by fitMeasures()) to append extra columns.
Use fit.measures = "all" to include every available measure.
Set loo = TRUE to compare models by leave-one-out cross-validation
(see loo()). This appends ELPD / SE (the Taylor expected log
predictive density and its standard error), p_loo, and,
against the best-ELPD model, the difference elpd_diff with its
paired standard error se_diff computed from the pointwise
contributions (the appropriate uncertainty for nested or same-data
comparisons). Every model is scored at one common Taylor order, the
lowest any of them can supply: if some unit of some model has no
second-order term, all models are compared at first order, since
otherwise a change of estimator between models would read as a
difference between the models themselves. The order used is stated when
the table is printed. The table is then sorted by descending ELPD. All models
must be fitted to the same data with matching units; units are paired
by id rather than by row order, so fits that stack groups differently
– a pooled fit against a multigroup fit, or multigroup fits with
different group orderings – still pair up unit by unit. For
missing-data (FIML) fits, "the same data" also means the same observed
entries: each unit is scored on the entries it has, so comparisons
require identical missingness patterns across models. All models
must also share the
score flavour (see loo()): mixing fits with modelled covariates
(fixed.x = FALSE, joint scores) and fixed covariates
(fixed.x = TRUE, conditional scores) is refused. Joint scores
additionally require identical variable sets across models, while
conditional scores require only matching outcome variables – covariate
sets may differ, which is the covariate-selection setting. Stored LOO
results (test including "loo" or "full", or add_loo()) are
reused.
When any of the models has random slopes (lavaan's rv() modifier; see
inlavaan()), compare() aborts unless all the models were fitted with
fixed.x = TRUE, score the same outcome variables, and share one
integration.ngh on the quadrature route. Their covariates may differ. The
fixed-slope model (the same path without rv()) is a valid comparator for
testing a random slope.
anova() is disabled for INLAvaan fits – there is no direct Bayesian
analogue of the classical likelihood-ratio test – and points here instead.
A data frame of class compare.inlavaan_internal containing model
fit statistics, sorted by descending marginal log-likelihood (or by
descending ELPD when loo = TRUE).
https://lavaan.ugent.be/tutorial/groups.html
# Model comparison on multigroup analysis (measurement invariance) HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") # Configural invariance fit1 <- acfa(HS.model, data = HolzingerSwineford1939, group = "school") # Weak invariance fit2 <- acfa( HS.model, data = HolzingerSwineford1939, group = "school", group.equal = "loadings" ) # Strong invariance fit3 <- acfa( HS.model, data = HolzingerSwineford1939, group = "school", group.equal = c("intercepts", "loadings") ) # Compare models (fit1 = configural = baseline, always first argument) compare(fit1, fit2, fit3) # With extra fit measures compare(fit1, fit2, fit.measures = c("BRMSEA", "BMc")) # With incremental indices (baseline = fit1, passed to fitMeasures()) compare(fit1, fit2, fit3, fit.measures = c("BCFI", "BTLI"))# Model comparison on multigroup analysis (measurement invariance) HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") # Configural invariance fit1 <- acfa(HS.model, data = HolzingerSwineford1939, group = "school") # Weak invariance fit2 <- acfa( HS.model, data = HolzingerSwineford1939, group = "school", group.equal = "loadings" ) # Strong invariance fit3 <- acfa( HS.model, data = HolzingerSwineford1939, group = "school", group.equal = c("intercepts", "loadings") ) # Compare models (fit1 = configural = baseline, always first argument) compare(fit1, fit2, fit3) # With extra fit measures compare(fit1, fit2, fit.measures = c("BRMSEA", "BMc")) # With incremental indices (baseline = fit1, passed to fitMeasures()) compare(fit1, fit2, fit3, fit.measures = c("BCFI", "BTLI"))
Density of a Beta distribution on a bounded interval
dbeta_box(x, shape1, shape2, a, b, log = FALSE)dbeta_box(x, shape1, shape2, a, b, log = FALSE)
x |
A numeric vector of quantiles. |
shape1, shape2
|
non-negative parameters of the Beta distribution. |
a |
The lower bound of the interval. |
b |
The upper bound of the interval. |
log |
Logical; if TRUE, probabilities p are given as log(p). |
A numeric vector of density values.
# Beta(2,5) on (0,100) x <- seq(0, 100, length.out = 100) y <- dbeta_box(x, shape1 = 2, shape2 = 5, a = 0, b = 100) plot(x, y, type = "l", main = "Beta(2,5) on (0,100)") # Beta(1,1) i.e. uniform on (-1, 1) x <- seq(-1, 1, length.out = 100) y <- dbeta_box(x, shape1 = 1, shape2 = 1, a = -1, b = 1) plot(x, y, type = "l", main = "Beta(1,1) on (-1,1)")# Beta(2,5) on (0,100) x <- seq(0, 100, length.out = 100) y <- dbeta_box(x, shape1 = 2, shape2 = 5, a = 0, b = 100) plot(x, y, type = "l", main = "Beta(2,5) on (0,100)") # Beta(1,1) i.e. uniform on (-1, 1) x <- seq(-1, 1, length.out = 100) y <- dbeta_box(x, shape1 = 1, shape2 = 1, a = -1, b = 1) plot(x, y, type = "l", main = "Beta(1,1) on (-1,1)")
Extract the (Bayesian) deviance of a fitted INLAvaan model. Unlike
lavaan, which has no deviance() method, this follows the
BUGS/JAGS/Stan convention: "deviance" is times the log-likelihood,
summarised over the posterior.
## S3 method for class 'INLAvaan' deviance(object, type = c("mean", "plugin"), ...)## S3 method for class 'INLAvaan' deviance(object, type = c("mean", "plugin"), ...)
object |
An object of class INLAvaan. |
type |
Character. |
... |
Currently unused. |
and are the two ingredients of the Deviance
Information Criterion, where
is the effective number of parameters. Use
compare() to compare models by DIC (or Bayes factors, or LOO/WAIC)
rather than comparing raw deviances directly.
This is estimated from the posterior draws. loo() reaches the
same quantity in closed form as pd_trace, the trace
; the two routes estimate one target
but do not agree exactly in a finite sample. Neither is p_loo, which
estimates something else entirely (see loo()).
A length-one numeric of class inlavaan_deviance, with the
effective number of parameters (pD) and DIC attached as
attributes.
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "standard", verbose = FALSE) deviance(fit) attr(deviance(fit), "DIC")HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "standard", verbose = FALSE) deviance(fit) attr(deviance(fit), "DIC")
Extract convergence and approximation-quality diagnostics from a fitted
INLAvaan model.
diagnostics(object, ...) ## S4 method for signature 'INLAvaan' diagnostics(object, type = c("global", "param"), ...)diagnostics(object, ...) ## S4 method for signature 'INLAvaan' diagnostics(object, type = c("global", "param"), ...)
object |
An object of class INLAvaan. |
... |
Currently unused. |
type |
Character. |
Global diagnostics (type = "global"):
nparNumber of free parameters.
nsampNumber of posterior samples drawn.
converged1 if the optimiser converged, 0 otherwise.
iterationsNumber of optimiser iterations.
grad_infL-infinity norm of the analytic gradient at the mode (max |grad|). Should be ~0 at convergence.
grad_inf_relRelative L-infinity norm of the analytic gradient (max |grad| / (|par| + 1e-6)).
grad_l2L2 (Euclidean) norm of the analytic gradient at the mode.
mode_shift_maxMaximum, across parameters, of the Newton
step at the reported mode in posterior-SD units
(max | grad| / se). Unlike the raw gradient norms
this is scale-free: it estimates how far the reported mode sits from
the true posterior mode relative to the posterior uncertainty. Should
be ~0 at convergence.
hess_condCondition number of the Hessian (precision matrix)
computed from . Large values indicate near-singularity.
vb_kld_globalGlobal KL divergence from the VB mean correction (NA if VB correction was not applied).
vb_applied1 if VB correction was applied, 0 otherwise.
vb_shift_maxMaximum, across parameters, of the absolute
VB correction in posterior-SD units (max |vb_shift_sigma|). This
is the quantity the fit-time check tests. NA if the VB correction was
not applied.
kld_maxMaximum per-parameter KL divergence from the VB correction.
kld_meanMean per-parameter KL divergence.
vb_mcse_maxMaximum, across parameters, of the estimated
quadrature error of the VB shift, in posterior-SD units. See
vb_mcse_sigma below. NA under
vb_method = "gauss_hermite".
vb_mcse_meanMean estimated quadrature error of the VB shift, in posterior-SD units.
nmad_maxMaximum normalised max-absolute-deviation across marginals (skew-normal method only; NA otherwise).
nmad_meanMean NMAD across marginals.
scan_end_mass_maxMaximum, across parameters, of
scan_end_mass (see below). NA unless the skew-normal marginal
method was used.
Per-parameter diagnostics (type = "param"):
A data frame with columns:
namesParameter name.
gradAnalytic gradient of the negative log-posterior at the mode. Should be ~0 at convergence.
grad_numNumerical (finite-difference) gradient at the mode.
Should agree with grad; large discrepancies indicate a bug in the
analytic gradient.
grad_diffDifference grad_num - grad: should be ~0.
grad_absAbsolute analytic gradient.
grad_relRelative analytic gradient |grad| / (|par| + 1e-6).
mode_shift_sigmaNewton step at the reported mode in posterior-SD units. Should be ~0 at convergence.
kldPer-parameter KL divergence from the VB correction.
vb_shiftVB correction shift (in original scale).
vb_shift_sigmaVB shift in units of posterior SD.
vb_mcse_sigmaEstimated quadrature error of the VB shift, in
posterior-SD units. The shift is the solution of an integral evaluated by
quasi-Monte Carlo over a finite node set, so it carries an integration
error of its own. This estimates that error by splitting the node set in
half and taking half the disagreement between the two half-set solutions.
Read it as an error bar on vb_shift_sigma: a value of 0.05 means
the reported posterior mean of that parameter could move by roughly that
much, in SD units, purely from the choice of node set. It runs
conservative, because the two halves are negatively correlated and
quasi-Monte Carlo error falls faster than root-n. It is exactly zero for
parameters whose shift is pinned by the saturated-means fast path, since
no quadrature is used there. It is NA for every parameter under
vb_method = "gauss_hermite", whose rule is deterministic.
nmadNormalised max-absolute-deviation of the skew-normal fit (NA when not using the skewnorm method).
alphaShape parameter of the fitted skew-normal marginal, on the unconstrained scale. Zero is a Gaussian marginal, and the sign gives the direction of the skew (NA when not using the skewnorm method).
scan_end_massMass the fitted marginal puts outside the
window that was scanned to fit it,
, where
is the fitted skew-normal distribution function,
the posterior mode and the Laplace
posterior SD. The marginal is fitted inside that window only, so mass
outside it is extrapolation, and when this is large the 2.5\
97.5\
gives = 6.3e-05, and healthy fits sit between 1e-03 and
1e-02 (NA when not using the skewnorm method).
Fit-time warnings: inlavaan() runs these checks once at the end
of every fit and emits a single consolidated warning (condition class
"inlavaan_diagnostics_warning") when any of them looks off:
the optimiser did not converge;
mode_shift_max above 0.1 posterior SDs;
any marginal with nmad above 0.1;
any marginal with scan_end_mass above 0.05;
vb_shift_max above 1 posterior SD;
hess_cond above 1e8.
Three of these checks have calibration behind them. Over 1,046 simulated
fits with MCMC references, the Spearman correlation with the worst-case
posterior-mean discrepancy was 0.64 for the VB shift in SD units, 0.59 for
the scan-endpoint mass and 0.48 for the NMAD. The convergence, mode-shift
and condition-number checks are rules of thumb. Every number above is a
package default, chosen so that a healthy fit stays silent, and not a
calibrated cut-off: a tripped check is a prompt to look again, and silence
is not a certificate of accuracy. Silence the check with
suppressWarnings(), or selectively by handling the condition class.
For type = "global", a named numeric vector (class
"diagnostics.INLAvaan"). For type = "param", a data frame
(class c("diagnostics.INLAvaan.param", "data.frame")).
timing(), fitmeasures(), plot()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Global convergence summary diagnostics(fit) # Per-parameter table diagnostics(fit, type = "param")HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Global convergence summary diagnostics(fit) # Per-parameter table diagnostics(fit, type = "param")
Density for the skew normal distribution with location xi, scale omega,
and shape alpha.
dsnorm(x, xi, omega, alpha, logC = 0, log = FALSE)dsnorm(x, xi, omega, alpha, logC = 0, log = FALSE)
x |
Vector of quantiles. |
xi |
Location parameter. |
omega |
Scale parameter. |
alpha |
Shape parameter. |
logC |
Log-normalization constant. |
log |
Logical; if TRUE, returns the log density. |
A numeric vector of (log) density values.
https://en.wikipedia.org/wiki/Skew_normal_distribution
x <- seq(-2, 5, length.out = 100) y <- dsnorm(x, xi = 0, omega = 1, alpha = 5) plot(x, y, type = "l", main = "Skew Normal Density")x <- seq(-2, 5, length.out = 100) y <- dsnorm(x, xi = 0, omega = 1, alpha = 5) plot(x, y, type = "l", main = "Skew Normal Density")
Fit a skew normal distribution to log-density evaluations
fit_skew_normal(x, y, threshold_log_drop = -6, temp = NA)fit_skew_normal(x, y, threshold_log_drop = -6, temp = NA)
x |
A numeric vector of points where the density is evaluated. |
y |
A numeric vector of log-density evaluations at points x. |
threshold_log_drop |
A negative numeric value indicating the log-density drop threshold below which points are ignored in the fitting. Default is -6. |
temp |
A numeric value for the temperature parameter k. If NA (default), it is included in the optimisation. |
This skew normal fitting function uses a weighted least squares
approach to fit the log-density evaluations provided in y at points x.
The weights are set to be the density evaluations raised to the power of
the temperature parameter k. This has somewhat an interpretation of
finding the skew normal fit that minimises the Kullback-Leibler divergence
from the true density to it.
In R-INLA, the C code implementation from which this was translated from can be found here.
A list with fitted parameters:
xi: location parameter
omega: scale parameter
alpha: shape parameter
logC: log-normalization constant
k: temperature parameter
rsq: R-squared of the fit
Note that logC and k are not used when fitting from a sample.
# Fit a SN curve to gamma log-density logdens <- function(x) dgamma(x, shape = 3, rate = 1, log = TRUE) x_grid <- seq(0.1, 8, length.out = 21) y_log <- sapply(x_grid, logdens) y_log <- y_log - max(y_log) # normalise to have maximum at zero res <- fit_skew_normal(x_grid, y_log, temp = 10) unlist(res) # Compare truth vs skew-normal approximation x_fine <- seq(0.1, 8, length.out = 200) y_true <- exp(logdens(x_fine)) y_sn <- dsnorm(x_fine, xi = res$xi, omega = res$omega, alpha = res$alpha) plot(x_fine, y_true, type = "n", xlab = "x", ylab = "Density", bty = "n") polygon(c(x_fine, rev(x_fine)), c(y_true, rep(0, length(x_fine))), col = adjustcolor("#131516", 0.25), border = NA) lines(x_fine, y_sn, col = "#00A6AA", lwd = 2) legend("topright", legend = c("Truth", "SN Approx."), fill = c(adjustcolor("#131516", 0.25), NA), border = NA, col = c(NA, "#00A6AA"), lwd = c(NA, 2), lty = c(NA, 1), bty = "n")# Fit a SN curve to gamma log-density logdens <- function(x) dgamma(x, shape = 3, rate = 1, log = TRUE) x_grid <- seq(0.1, 8, length.out = 21) y_log <- sapply(x_grid, logdens) y_log <- y_log - max(y_log) # normalise to have maximum at zero res <- fit_skew_normal(x_grid, y_log, temp = 10) unlist(res) # Compare truth vs skew-normal approximation x_fine <- seq(0.1, 8, length.out = 200) y_true <- exp(logdens(x_fine)) y_sn <- dsnorm(x_fine, xi = res$xi, omega = res$omega, alpha = res$alpha) plot(x_fine, y_true, type = "n", xlab = "x", ylab = "Density", bty = "n") polygon(c(x_fine, rev(x_fine)), c(y_true, rep(0, length(x_fine))), col = adjustcolor("#131516", 0.25), border = NA) lines(x_fine, y_sn, col = "#00A6AA", lwd = 2) legend("topright", legend = c("Truth", "SN Approx."), fill = c(adjustcolor("#131516", 0.25), NA), border = NA, col = c(NA, "#00A6AA"), lwd = c(NA, 2), lty = c(NA, 1), bty = "n")
Fit a skew normal distribution to a sample
fit_skew_normal_samp(x)fit_skew_normal_samp(x)
x |
A numeric vector of sample data. |
Uses maximum likelihood estimation to fit a skew normal distribution
to the provided numeric vector x. The fit is computed on standardised
draws, so it does not depend on the scale of x.
A list with fitted parameters:
xi: location parameter
omega: scale parameter
alpha: shape parameter
logC: log-normalization constant
k: temperature parameter
rsq: R-squared of the fit
Note that logC and k are not used when fitting from a sample.
x <- rnorm(100, mean = 5, sd = 1) unlist(fit_skew_normal_samp(x))x <- rnorm(100, mean = 5, sd = 1) unlist(fit_skew_normal_samp(x))
Fit Measures for a Latent Variable Model estimated using INLA
## S4 method for signature 'INLAvaan' fitMeasures(object, fit_measures = "all", baseline_model = NULL, h1_model = NULL, fm_args, output = "vector", level = NULL, ...) ## S4 method for signature 'INLAvaan' fitmeasures(object, fit_measures = "all", baseline_model = NULL, h1_model = NULL, fm_args, output = "vector", level = NULL, ...)## S4 method for signature 'INLAvaan' fitMeasures(object, fit_measures = "all", baseline_model = NULL, h1_model = NULL, fm_args, output = "vector", level = NULL, ...) ## S4 method for signature 'INLAvaan' fitmeasures(object, fit_measures = "all", baseline_model = NULL, h1_model = NULL, fm_args, output = "vector", level = NULL, ...)
object |
An object of class INLAvaan. |
fit_measures |
If |
baseline_model |
The baseline (null) model for the incremental fit
indices (BCFI, BTLI, BNFI). |
h1_model |
Ignored (included for compatibility with the lavaan generic). |
fm_args |
Ignored (included for compatibility with the lavaan generic). |
output |
Ignored (included for compatibility with the lavaan generic). |
level |
Ignored (included for compatibility with the lavaan generic). |
... |
Additional arguments. Currently supports:
|
A named numeric vector of fit measures.
bfit_indices(), compare(), diagnostics()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, verbose = FALSE) # All available fit measures fitMeasures(fit) # Specific measures fitMeasures(fit, c("npar", "dic", "p_dic", "ppp"))HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, verbose = FALSE) # All available fit measures fitMeasures(fit) # Specific measures fitMeasures(fit, c("npar", "dic", "p_dic", "ppp"))
Extract the model-implied (fitted) sample statistics from a fitted
INLAvaan model. As in lavaan and blavaan, the moments are
the model-implied covariance matrix (and mean vector, when a mean structure
is present) evaluated at the parameter estimates – here the posterior means.
## S4 method for signature 'INLAvaan' fitted(object, type = "moments", labels = TRUE, ..., per_cluster = FALSE) ## S4 method for signature 'INLAvaan' fitted.values( object, type = "moments", labels = TRUE, ..., per_cluster = FALSE )## S4 method for signature 'INLAvaan' fitted(object, type = "moments", labels = TRUE, ..., per_cluster = FALSE) ## S4 method for signature 'INLAvaan' fitted.values( object, type = "moments", labels = TRUE, ..., per_cluster = FALSE )
object |
An object of class INLAvaan. |
type |
Character. |
labels |
Logical. Attach variable names to the output. Default
|
... |
Currently unused. |
per_cluster |
Logical. For a random-slope model, return the moments
of each cluster instead of their average. Default |
This delegates to lavaan's own fitted() machinery, so the return
structure matches lavaan exactly. Because INLAvaan stores the posterior means
as the point estimates of the fitted object, the implied moments are the
posterior-mean model-implied moments (mirroring blavaan).
For a model with random slopes (lavaan's rv()), the moments are
averaged over the covariates and include the mean and the variance of each
slope. With per_cluster = TRUE, each cluster gets the expected
cluster mean and within-cluster covariance (divisor ) of its
outcomes and covariates at its own covariate values, and the mean also holds
the expected values of any between-only outcomes. This is not available for
a slope on a latent or split covariate. type = "casewise" gives the
outcomes' means given the covariates (closed-form route only).
For type = "moments", a list (or list of lists, for
multiple groups) with elements such as cov, mean, and
th. For type = "casewise", a numeric matrix of predicted
observed-variable values. With per_cluster = TRUE, a list with one
element per cluster, each holding cov and mean.
predict(), coef(), fitMeasures()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Model-implied covariance matrix (posterior means) fitted(fit) # Casewise model-predicted observed values head(fitted(fit, type = "ov"))HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Model-implied covariance matrix (posterior means) fitted(fit) # Casewise model-predicted observed values head(fitted(fit, type = "ov"))
Returns the inlavaan_internal list stored inside a fitted INLAvaan
object, optionally extracting a single named element.
get_inlavaan_internal(object, what)get_inlavaan_internal(object, what)
object |
An object of class INLAvaan. |
what |
Character. Name of the element to extract from the internal
list. If missing, the entire list is returned. Common elements include
|
The full inlavaan_internal list, or the named element when
what is supplied.
INLAvaan, diagnostics(), timing()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Full internal object int <- get_inlavaan_internal(fit) names(int) # Extract a specific element get_inlavaan_internal(fit, "coefficients")HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Full internal object int <- get_inlavaan_internal(fit) names(int) # Extract a specific element get_inlavaan_internal(fit, "coefficients")
This function fits a Bayesian latent variable model by approximating the posterior distributions of the model parameters using various methods, including skew-normal, asymmetric Gaussian, marginal Gaussian, or sampling-based approaches. It leverages the lavaan package for model specification and estimation.
inlavaan( model, data, model.type = "sem", dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, start = NULL, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )inlavaan( model, data, model.type = "sem", dp = priors_for(), test = "standard", vb_correction = TRUE, n_qmc = 64L, vb_method = c("sobol", "gauss_hermite"), marginal_method = c("skewnorm", "asymgaus", "marggaus", "sampling"), marginal_correction = c("shortcut", "shortcut_fd", "hessian", "none"), nsamp = 1000, samp_copula = TRUE, samp_norta = FALSE, cov_as_cor = FALSE, sn_fit_ngrid = 21, sn_fit_logthresh = -6, sn_fit_temp = 1, sn_fit_sample = TRUE, control = list(), verbose = TRUE, debug = FALSE, add_priors = TRUE, optim_method = c("nlminb", "ucminf", "optim"), numerical_grad = FALSE, start = NULL, cores = NULL, ppp_method = c("onestep", "em"), ppp_nsamp = 250L, ... )
model |
A description of the user-specified model. Typically, the model
is described using the lavaan model syntax. See
|
data |
An optional data frame containing the observed variables used in the model. If some variables are declared as ordered factors, lavaan will treat them as ordinal variables. |
model.type |
The lavaan entry point used to fit |
dp |
Default prior distributions for the different types of model
parameters; a named character vector as returned by |
test |
Character vector naming the post-estimation quantities to
compute and store with the fit. The atoms are |
vb_correction |
Logical indicating whether to apply a variational Bayes
correction for the posterior mean vector of estimates. Defaults to |
n_qmc |
Number of quasi-Monte Carlo nodes used by the VB mean
correction. Defaults to |
vb_method |
Integration rule for the VB mean correction. |
marginal_method |
The method for approximating the marginal posterior
distributions. Options include |
marginal_correction |
Which type of correction to use when fitting the
skew-normal or two-piece Gaussian marginals. |
nsamp |
The number of samples to draw for all sampling-based approaches (including posterior sampling for model fit indices). |
samp_copula |
Logical. When |
samp_norta |
Logical. When |
cov_as_cor |
Logical. Residual and latent-disturbance covariance
parameters ( |
sn_fit_ngrid |
Number of grid points to lay out per dimension when
fitting the skew-normal marginals. A finer grid gives a better fit at the
cost of more joint-log-posterior evaluations. Defaults to |
sn_fit_logthresh |
The log-threshold for fitting the skew-normal. Points
with log-posterior drop below this threshold (relative to the maximum) will
be excluded from the fit. Defaults to |
sn_fit_temp |
Temperature parameter for fitting the skew-normal.
Defaults to |
sn_fit_sample |
Logical. When |
control |
A list of control parameters for the optimiser. For the
default |
verbose |
Logical indicating whether to print progress messages. |
debug |
Logical indicating whether to return debug information. |
add_priors |
Logical indicating whether to include prior densities in the posterior computation. |
optim_method |
The optimisation method to use for finding the posterior
mode. Options include |
numerical_grad |
Logical indicating whether to use numerical gradients
for the optimisation. Defaults to |
start |
Optional numeric vector of starting values for the optimiser,
given as a full vector of free parameters in the internal (unconstrained)
parameterisation. Mainly for internal use by |
cores |
Integer or |
ppp_method |
How the PPP of a two-level model scores the observed and
the replicate data against the saturated model. |
ppp_nsamp |
The number of posterior draws, each with one replicate data
set, that the PPP of a two-level model uses. Defaults to |
... |
Additional arguments to be passed to the lavaan model fitting function. |
The VB mean correction integrates over n_qmc quasi-Monte Carlo
nodes, so it carries a quadrature error that falls as n_qmc rises. The
default of 64 keeps this error at roughly 0.05 posterior SDs – on par
with the Monte Carlo error of a routine MCMC run, and small against the
shifts being corrected. Users may increase n_qmc to reduce the error
further, at a proportional cost in computation time; diagnostics()
reports the realised error per fit as vb_mcse_sigma per parameter and
vb_mcse_max globally, both in posterior-SD units. Setting
vb_method = "gauss_hermite" removes the random node set altogether; see
the vb_method argument.
An S4 object of class INLAvaan which is a subclass of the
lavaan class.
Wrapping a level-1 regression coefficient in lavaan's rv() modifier
turns it into a level-2 latent variable – a random slope – which can
then be given a mean, a variance and cross-level regressions like any
other level-2 latent variable:
level: 1
fw =~ y1 + y2 + y3
fw ~ rv('s1')*x1
level: 2
fb =~ y1 + y2 + y3
s1 ~ w1
s1 ~~ s1
s1 ~ 1
The slope's variance and intercept are added automatically, so in
practice only the cross-level regression s1 ~ w1 need be written out.
summary() marks the level-1 carrier row as x1 (s1): that row is
fixed at zero, the slope itself being reported under Level 2.
The likelihood takes one of two routes. When the covariate carrying the
slope is observed and purely within-cluster, the slope integrates out in
closed form. When it is latent, or split across both levels (the same
variable entering at level 1 and at level 2), the integral is done by
Gauss-Hermite quadrature instead; that route warns at fit time and is
governed by integration.ngh, passed through to lavaan. The node count
is an accuracy setting as much as a cost setting, so give every fit that
is to be compared with another the same integration.ngh.
A model with observed exogenous covariates requires fixed.x = TRUE
(the default): the likelihood is the density of the outcomes given
those covariates, so their own means and (co)variances are unidentified
and would be reported back as their priors. A fixed.x = FALSE fit is
refused. A model whose covariates are all latent or modelled has nothing
to hold fixed, and lavaan reports fixed.x = FALSE for it of its own
accord; such a fit is accepted as it stands.
Equality constraints work as for other models, except that covariances
cannot be held equal. Composites (<~) cannot be combined with random
slopes yet.
A random-slope model implies no single within-cluster covariance matrix:
the covariance of the outcomes depends on the covariate values.
fitted() and residuals() therefore use the implied moments averaged
over the covariates, which include the mean and the variance of each
slope, or with per_cluster = TRUE the moments of each cluster at its
own covariate values (closed-form route only). standardisedsolution()
and the standardised columns and R-square of summary() are scaled by
the averaged variances. The carrier row x1 (s1) then gives the
standardised mean slope, and the slope's own rows are on the same
standardised scale. An outcome observed at level 1 only, whose slope
covariate has a non-zero mean, has no place in lavaan's two-level layout,
and these outputs abort for it.
simulate() draws each cluster's slopes and other level-2 effects and
then its outcomes, at the cluster's own covariates (closed-form route
only). sampling() gives latent, observed and implied draws. predict()
gives type = "lv" and, on the closed-form route, the cluster-specific
"yhat" and "ypred" from the empirical Bayes random effects, while
fitted(type = "casewise") gives the outcomes' means given the
covariates. bfit_indices() scales the Bayesian fit indices against the
unrestricted random-coefficient model with the same random-effects design,
because a saturated model does not exist here. This reference is
INLAvaan's own construction (closed-form route only).
The posterior predictive p-value (test = "ppp") is dropped, because a
random-slope model has no saturated model to score replicate data against.
What aborts with an explanation: the residual types scaled by standard
errors, predict(type = "ymis"), and loo(type = "loso"), which would
need a cluster's sufficient statistics downdated by one row.
The model-comparison side works throughout. compare() reports the
marginal likelihood, Bayes factors, the DIC and its , and loo()
and waic() score the fit leave-one-cluster-out on the conditional
likelihood.
To ask whether there is a random slope at all, compare the fit with the
fixed-slope model (fw ~ x1 at level 1) using compare(). On the
closed-form route, fixing the slope variance at zero (s1 ~~ 0*s1) and
dropping any cross-level regression on the slope gives the same model.
Keeping s1 ~ w1 gives a cross-level interaction model instead. The
quadrature route refuses a slope variance fixed at zero.
Typically, users will interact with the specific latent variable
model functions instead, including acfa(), asem(), and agrowth().
# The Holzinger and Swineford (1939) example HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- inlavaan( HS.model, data = HolzingerSwineford1939, auto.var = TRUE, auto.fix.first = TRUE, auto.cov.lv.x = TRUE ) summary(fit)# The Holzinger and Swineford (1939) example HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- inlavaan( HS.model, data = HolzingerSwineford1939, auto.var = TRUE, auto.fix.first = TRUE, auto.cov.lv.x = TRUE ) summary(fit)
This is a class that extends the lavaan class. Several S4 methods are available.
## S4 method for signature 'INLAvaan' coef(object, ...) ## S4 method for signature 'INLAvaan' nobs(object, ...) ## S4 method for signature 'INLAvaan' show(object) ## S4 method for signature 'INLAvaan' summary( object, header = TRUE, fit.measures = TRUE, estimates = TRUE, ci = TRUE, standardized = FALSE, standardised = standardized, rsquare = FALSE, postmedian = FALSE, postmode = FALSE, nmad = TRUE, kld = FALSE, vb_shift = FALSE, priors = TRUE, nd = 3L, ... )## S4 method for signature 'INLAvaan' coef(object, ...) ## S4 method for signature 'INLAvaan' nobs(object, ...) ## S4 method for signature 'INLAvaan' show(object) ## S4 method for signature 'INLAvaan' summary( object, header = TRUE, fit.measures = TRUE, estimates = TRUE, ci = TRUE, standardized = FALSE, standardised = standardized, rsquare = FALSE, postmedian = FALSE, postmode = FALSE, nmad = TRUE, kld = FALSE, vb_shift = FALSE, priors = TRUE, nd = 3L, ... )
object |
An object of class |
... |
Additional arguments passed to methods. |
header |
Logical; if TRUE, print model fit information header. |
fit.measures |
Logical; if TRUE, print fit measures (DIC and pD;
PPP is printed by the header regardless). Both require the fit to
have computed them, see the |
estimates |
Logical; if TRUE, print parameter estimates table. |
ci |
Logical; if TRUE (default), include 95% credible intervals (2.5% and 97.5% posterior quantiles) in the estimates table. |
standardized |
Logical; if TRUE, include standardized estimates. |
standardised |
Alias of |
rsquare |
Logical; if TRUE, include R-square values for the observed dependent variables, computed at the posterior mean point estimates. |
postmedian |
Logical; if TRUE, include posterior median in estimates. |
postmode |
Logical; if TRUE, include posterior mode in estimates. |
nmad |
Logical; if TRUE (default), include the NMAD column for skew-normal marginal fit quality. |
kld |
Logical; if FALSE (default), omit the per-parameter KLD column. Set to TRUE to show it. |
vb_shift |
Logical; if FALSE (default), omit the VB shift column (shift in units of posterior SD). Set to TRUE to show it. |
priors |
Logical; if TRUE, include prior information in estimates. |
nd |
Integer; number of decimal places to print for numeric values. |
externalA list containing an inlavaan_internal object.
lavaan, inlavaan(), acfa(), asem(), agrowth()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Print basic info fit # Detailed summary summary(fit) # Extract coefficients coef(fit)HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Print basic info fit # Detailed summary summary(fit) # Extract coefficients coef(fit)
Helper function to check if two functions are the same
is_same_function(f, g)is_same_function(f, g)
f, g
|
Functions to compare. |
Logical.
f1 <- function(x) { x^2 + 1 } f2 <- function(x) { x^2 + 1 } is_same_function(f1, f2) # TRUEf1 <- function(x) { x^2 + 1 } f2 <- function(x) { x^2 + 1 } is_same_function(f1, f2) # TRUE
Extract a log-likelihood-flavoured summary from a fitted INLAvaan
model. Two distinct quantities are available, deliberately not conflated:
the Bayesian marginal log-likelihood (the default) and the classical
log-likelihood evaluated at the posterior mean.
## S4 method for signature 'INLAvaan' logLik(object, type = c("marginal", "plugin"), ...) ## S4 method for signature 'INLAvaan' AIC(object, ..., k = 2) ## S4 method for signature 'INLAvaan' BIC(object, ...)## S4 method for signature 'INLAvaan' logLik(object, type = c("marginal", "plugin"), ...) ## S4 method for signature 'INLAvaan' AIC(object, ..., k = 2) ## S4 method for signature 'INLAvaan' BIC(object, ...)
object |
An object of class INLAvaan. |
type |
Character. |
... |
Currently unused. |
k |
Numeric penalty per parameter passed to the (disabled) |
The marginal log-likelihood already integrates over the (Laplace-
approximated) posterior, so it is not on the same scale as a classical
log-likelihood and should not be passed to AIC()/BIC() –
doing so would double-penalise model complexity that the evidence has
already accounted for. Use compare() to compare models via Bayes
factors, DIC, or LOO/WAIC. The plug-in variant exists for users who
specifically want a point-estimate-based classical comparison.
AIC()/BIC() on an INLAvaan fit are themselves
disabled (mirroring anova()): both are large-sample asymptotic
approximations to quantities INLAvaan already computes directly –
AIC approximates predictive accuracy, which loo()/waic()
already estimate more rigorously; BIC approximates times
the log marginal likelihood, which logLik() already returns
directly (up to the Laplace approximation). Point-estimate AIC/BIC remain
available for reporting-convention purposes via
AIC(logLik(object, type = "plugin")) /
BIC(logLik(object, type = "plugin")).
For type = "marginal", a length-one numeric of class
inlavaan_logLik that prints with a note on its interpretation.
For type = "plugin", a standard "logLik" object.
deviance(), compare(), loo(), waic()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "standard", verbose = FALSE) # Marginal log-likelihood (log evidence) logLik(fit) # Classical log-likelihood at the posterior mean, AIC/BIC-compatible ll <- logLik(fit, type = "plugin") AIC(ll)HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "standard", verbose = FALSE) # Marginal log-likelihood (log evidence) logLik(fit) # Classical log-likelihood at the posterior mean, AIC/BIC-compatible ll <- logLik(fit, type = "plugin") AIC(ll)
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, ...)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. For a random-slope fit the per-cluster kernels return every cluster on each call, so this trims the reported table rather than the work. |
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 (a subject for LOSO, a cluster for LOCO) with
log-likelihood contribution , score and Hessian
evaluated at the posterior summary , the
log conditional predictive ordinate is approximated to first and second order
by
The reported elpd_loo is the sum of the second-order terms (first-order
when second_order = FALSE), with standard error
and looic
. p_loo is the loo package's effective
number of parameters,
, where
is the analogous Taylor approximation of the
full-posterior pointwise log predictive density – the same definition
loo::loo() reports, and not the of the DIC.
exists exactly when
is positive definite (recorded in per_unit$ok) and
exactly when 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 (k_max < 1, k_min > -1), with k_sum
summing across units to the trace form of . p_loo (cross-product
form) and (second-derivative form) agree only in the
correct-specification limit, and printing a result reports the
first-to-second-order gap against its limit as a free check on
the Taylor truncation. Because the first-order elpd overstates the truth by
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).
For composites (<~), lavaan fixes the (co)variances of the indicators at
their sample values. Each unit is scored with them recomputed without it (to
first order under missing data), so p_loo counts them as parameters.
A random-slope fit (lavaan's rv() modifier; see inlavaan()) is scored
leave-one-cluster-out only, conditional on the exogenous covariates
(flavour is "conditional").
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 .
lpd_1, lpd_2
Pointwise log predictive density, at first and second order.
log_cpo_1, log_cpo_2
Pointwise LOO contributions, at first and second order.
det_term, the
determinant term of the second-order score.
k_max, k_min, k_sum
Leverage diagnostics (see Details).
k_ssq, consumed by
the closed-form waic() penalty.
okWhether the second-order
exists.
t_deltaComposite fits only: the drop in
when the fixed indicator (co)variances are recomputed without the
unit, already subtracted from the log CPO terms.
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_2
First- and second-order aggregates; the second-order ones are NA
when any does not exist.
elpd_gap, pd_trace
The 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_overridden
Metadata.
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. 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)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)
Generates diagnostic plots for a fitted INLAvaan model.
## S4 method for signature 'INLAvaan,ANY' plot( x, y, type = c("marg_pdf", "sn_fit", "sn_fit_log"), params = "all", nrow = NULL, ncol = NULL, use_ggplot = TRUE, points = FALSE, ... )## S4 method for signature 'INLAvaan,ANY' plot( x, y, type = c("marg_pdf", "sn_fit", "sn_fit_log"), params = "all", nrow = NULL, ncol = NULL, use_ggplot = TRUE, points = FALSE, ... )
x |
An object of class INLAvaan. |
y |
Not used. |
type |
Character. One of |
params |
Character vector of parameter names to plot, as in
|
nrow, ncol
|
Integer. Number of rows/columns for the facet grid when
|
use_ggplot |
Logical. When |
points |
Logical. When |
... |
Additional arguments (currently unused). |
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Posterior marginal densities (default) plot(fit) # Skew-normal fit diagnostic for a single parameter plot(fit, type = "sn_fit", params = "visual=~x1")HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Posterior marginal densities (default) plot(fit) # Skew-normal fit diagnostic for a single parameter plot(fit, type = "sn_fit", params = "visual=~x1")
Compute posterior predictions from a fitted INLAvaan model,
including latent variable scores, predicted observed values, and imputed
missing data.
## S4 method for signature 'INLAvaan' predict( object, type = c("lv", "yhat", "ov", "ypred", "ydist", "ymis", "ovmis"), newdata = NULL, level = 1L, nsamp = 1000, ymis_only = FALSE, summary = FALSE, ... )## S4 method for signature 'INLAvaan' predict( object, type = c("lv", "yhat", "ov", "ypred", "ydist", "ymis", "ovmis"), newdata = NULL, level = 1L, nsamp = 1000, ymis_only = FALSE, summary = FALSE, ... )
object |
An object of class INLAvaan. |
type |
Character string specifying the type of prediction:
|
newdata |
An optional data frame of new observations. If supplied,
predictions are computed for |
level |
Integer; for |
nsamp |
Integer; number of posterior samples to use for prediction.
Defaults to |
ymis_only |
Logical; only applies when |
summary |
Logical. When |
... |
Currently unused. |
A list of nsamp posterior draws, each a matrix (or data
frame, for multiple groups) with rows corresponding to cases and columns
to variables or latent factors. When summary = TRUE, instead
returns a summary.predict.inlavaan_internal object with the
posterior mean, SD, quantiles, and mode for each case/variable.
sampling(), simulate(), summary()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Posterior latent variable scores lv_scores <- predict(fit) head(lv_scores) # Predicted observed variable means yhat <- predict(fit, type = "yhat") head(yhat) # Point estimates only, skipping the manual summary() step predict(fit, type = "yhat", summary = TRUE)HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Posterior latent variable scores lv_scores <- predict(fit) head(lv_scores) # Predicted observed variable means yhat <- predict(fit, type = "yhat") head(yhat) # Point estimates only, skipping the manual summary() step predict(fit, type = "yhat", summary = TRUE)
Specify priors for a SEM, similar to how blavaan::dpriors() works.
priors_for(...)priors_for(...)
... |
Named arguments specifying prior distributions for lavaan parameter types. |
This function provides a convenient way to specify prior distributions for different types of parameters in a structural equation model (SEM). It uses a registry of default priors for common lavaan parameter types (e.g., loadings, regressions, residuals, etc.) and allows users to override these defaults by passing named arguments.
The parameter names, and default settings, are:
nu = "normal(0,32)": Observed variable intercepts
alpha = "normal(0,10)": Latent variable intercepts
lambda = "normal(0,10)": Factor loadings
beta = "normal(0,10)": Regression coefficients
wmat = "normal(0,10)": Composite weights (<~)
theta = "gamma(1,.5)[sd]": Residual precisions
psi = "gamma(1,.5)[sd]": Latent variable precisions
rho = "beta(1,1)": Correlations (both latent and observed)
tau = "normal(0,1.5)": Thresholds for ordinal variables
Note that the normal distributions are parameterised using standard
deviations, and not variances. For example, normal(0,10) means a
normal distribution with mean 0 and standard deviation 10 (not variance 10).
A named character vector of prior specifications, where names
correspond to lavaan parameter types (e.g., "lambda", "beta", "theta",
etc.) and values are character strings specifying the prior distribution
(e.g., "normal(0,10)", "gamma(1,0.5)[sd]",
"gamma(1,1)[prec]", etc.).
For variance parameters (theta, psi), the prior distribution
can be placed on a transformed scale by appending a qualifier:
[sd]: Prior is on the standard deviation .
Example: "gamma(1,0.5)[sd]" places a Gamma(1, 0.5) prior on
.
[prec]: Prior is on the precision .
Example: "gamma(1,1)[prec]" places a Gamma(1, 1) prior on
. This is the parameterisation used by
blavaan and corresponds to an Inverse-Gamma prior on the variance.
The necessary Jacobian adjustment is applied automatically in both cases.
inlavaan(), acfa(), asem(), agrowth()
priors_for(nu = "normal(0,10)", lambda = "normal(0,1)", rho = "beta(3,3)") # Precision-scale prior for residual variances (blavaan-style) priors_for(theta = "gamma(1,1)[prec]")priors_for(nu = "normal(0,10)", lambda = "normal(0,1)", rho = "beta(3,3)") # Precision-scale prior for residual variances (blavaan-style) priors_for(theta = "gamma(1,1)[prec]")
A fast approximation of skew-normal quantiles using the high-performance approximation algorithm from the INLA GMRFLib C source, and originally by Thomas Luu (see details for reference).
qsnorm_fast(p, xi = 0, omega = 1, alpha = 0)qsnorm_fast(p, xi = 0, omega = 1, alpha = 0)
p |
Vector of probabilities. |
xi |
Location parameter (numeric vector). |
omega |
Scale parameter (numeric vector). |
alpha |
Shape parameter (numeric vector). |
This function implements a high-performance approximation for the
skew-normal quantile function based on the algorithm described by Luu
(2016). The method uses a domain decomposition strategy to achieve high
accuracy ( relative error) without iterative numerical
inversion.
The domain is split into two regions:
Tail Regions: For extreme probabilities where is large, the quantile
is approximated using the Lambert W-function, , solving via
asymptotic expansion:
Central Region: For the main body of the distribution, the function uses a high-order Taylor expansion of the inverse error function around a carefully selected expansion point $x_0$:
This approach is significantly faster than standard numerical inversion
(e.g., uniroot) while maintaining sufficient precision for most
statistical applications.
Vector of quantiles.
Luu, T. (2016). Fast and accurate parallel computation of quantile functions for random number generation (Doctoral thesis). UCL (University College London). https://discovery.ucl.ac.uk/1482128/
qsnorm_fast(c(0.025, 0.5, 0.975)) qsnorm_fast(c(0.025, 0.5, 0.975), xi = 2, omega = 0.5, alpha = 1)qsnorm_fast(c(0.025, 0.5, 0.975)) qsnorm_fast(c(0.025, 0.5, 0.975), xi = 2, omega = 0.5, alpha = 1)
Extract the difference between the observed and model-implied (fitted)
sample statistics from a fitted INLAvaan model. As in lavaan
and blavaan, residuals are computed at the parameter estimates –
here the posterior means – not as a posterior distribution over residuals.
## S4 method for signature 'INLAvaan' residuals(object, type = "raw", labels = TRUE, ..., per_cluster = FALSE) ## S4 method for signature 'INLAvaan' resid(object, type = "raw", ..., per_cluster = FALSE)## S4 method for signature 'INLAvaan' residuals(object, type = "raw", labels = TRUE, ..., per_cluster = FALSE) ## S4 method for signature 'INLAvaan' resid(object, type = "raw", ..., per_cluster = FALSE)
object |
An object of class INLAvaan. |
type |
Character. |
labels |
Logical. Attach variable names to the output. Default
|
... |
Currently unused. |
per_cluster |
Logical. For a random-slope model, return the residuals
of each cluster instead of the averaged ones. Default |
This delegates to lavaan's own residuals() machinery, so the
return structure matches lavaan exactly. Because INLAvaan stores the
posterior means as the point estimates of the fitted object, the residuals
are the observed statistics minus the posterior-mean model-implied
statistics (mirroring blavaan, which likewise inherits lavaan's
residuals() without overriding it).
For a model with random slopes (lavaan's rv()), the residuals
compare the sample moments with the averaged moments of fitted(). With
per_cluster = TRUE, they compare each cluster's sample mean and
within-cluster covariance with its expected ones. A covariance of two
variables observed together in fewer than two rows of a cluster is
NA. The raw and correlation types are available, and
type = "casewise" gives the observed values minus the outcomes'
means given the covariates (closed-form route only).
For moment-based types, a list with elements
type, cov, and (when relevant) mean. For
type = "casewise", a numeric matrix of observed-minus-fitted
values.
fitted(), predict(), fitMeasures()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Raw residual covariance matrix (posterior means) residuals(fit) # SRMR-basis residuals residuals(fit, type = "cor.bentler") # Casewise observed-minus-fitted values head(residuals(fit, type = "casewise"))HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Raw residual covariance matrix (posterior means) residuals(fit) # SRMR-basis residuals residuals(fit, type = "cor.bentler") # Casewise observed-minus-fitted values head(residuals(fit, type = "casewise"))
Sample model parameters, latent variables, or observed variables from the
generative model underlying a fitted INLAvaan model. By default, parameters
are drawn from the posterior distribution; set prior = TRUE to draw
from the prior instead (useful for prior predictive checks).
sampling(object, ...) ## S4 method for signature 'INLAvaan' sampling( object, type = c("lavaan", "theta", "latent", "observed", "implied", "all"), nsamp = 1000L, samp_copula = TRUE, prior = FALSE, silent = FALSE, ... )sampling(object, ...) ## S4 method for signature 'INLAvaan' sampling( object, type = c("lavaan", "theta", "latent", "observed", "implied", "all"), nsamp = 1000L, samp_copula = TRUE, prior = FALSE, silent = FALSE, ... )
object |
An object of class INLAvaan (or |
... |
Additional arguments (currently unused). |
type |
Character string specifying what to sample:
|
nsamp |
Number of samples to draw. |
samp_copula |
Logical. When |
prior |
Logical. When |
silent |
Logical. When |
Each row of the output corresponds to a fresh parameter draw: a new
is sampled and then propagated through the
generative chain to produce one latent vector and one observed vector. This
makes sampling() ideal for prior and posterior predictive checks
(e.g., density overlays, test statistic distributions).
The generative chain is:
If you need complete replicate datasets (many observations from a single
parameter draw) — for example, for simulation-based calibration (SBC) — use
simulate() instead.
This is distinct from predict(), which computes individual-specific
factor scores
conditional on observed data.
For a random-slope model, each draw takes the level-2 variables, slopes
included, first and puts the drawn slopes into their level-1 paths, and
type = "implied" gives the moments averaged over the covariates (see
fitted()). As for other models, the covariates of a latent or observed
draw come from their fitted moments, whereas simulate() keeps the
observed covariates of a random-slope model.
A matrix or named list, depending on type.
simulate() for generating complete replicate datasets (e.g.,
for SBC); predict() for individual-specific factor scores;
bfit_indices() for Bayesian fit indices.
utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa("visual =~ x1 + x2 + x3", HolzingerSwineford1939) # Posterior samples of lavaan-side parameters samps <- sampling(fit, nsamp = 500) head(samps) # Compare copula vs Gaussian sampling s_cop <- sampling(fit, nsamp = 500, samp_copula = TRUE) s_gaus <- sampling(fit, nsamp = 500, samp_copula = FALSE) # Prior predictive samples y_prior <- sampling(fit, type = "observed", nsamp = 500, prior = TRUE)utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa("visual =~ x1 + x2 + x3", HolzingerSwineford1939) # Posterior samples of lavaan-side parameters samps <- sampling(fit, nsamp = 500) head(samps) # Compare copula vs Gaussian sampling s_cop <- sampling(fit, nsamp = 500, samp_copula = TRUE) s_gaus <- sampling(fit, nsamp = 500, samp_copula = FALSE) # Prior predictive samples y_prior <- sampling(fit, type = "observed", nsamp = 500, prior = TRUE)
Generate complete synthetic datasets from a fitted INLAvaan model. For each
simulation, a single parameter vector is drawn (from the posterior or prior),
and then sample.nobs observations are generated from the model-implied
distribution at that parameter value.
## S4 method for signature 'INLAvaan' simulate( object, nsim = 1L, seed = NULL, sample.nobs = NULL, prior = FALSE, samp_copula = TRUE, silent = FALSE, ... )## S4 method for signature 'INLAvaan' simulate( object, nsim = 1L, seed = NULL, sample.nobs = NULL, prior = FALSE, samp_copula = TRUE, silent = FALSE, ... )
object |
An object of class INLAvaan. |
nsim |
Number of replicate datasets to generate (default 1). |
seed |
Optional random seed (passed to |
sample.nobs |
Number of observations per dataset, per group for multigroup models (a single number is used for every group). Defaults to the sample size of the original data. |
prior |
Logical. When |
samp_copula |
Logical. When |
silent |
Logical. When |
... |
Additional arguments (currently unused). |
This function is designed for tasks that require full replicate datasets
from a single parameter draw, such as simulation-based calibration (SBC) and
posterior predictive p-values. It differs from sampling() which generates
one observation per parameter draw (useful for prior/posterior predictive
density overlays).
For each simulation :
Draw from the posterior (or prior).
Compute the model-implied covariance
.
If it is not positive-definite, reject and redraw.
Generate a dataset of sample.nobs rows from
.
Parameter draws reuse the same internal machinery as sampling()
(sample_params_prior / sample_params_posterior), so the prior
specification is consistent.
A two-level dataset keeps the clusters of the data, with their labels and
sizes, unless sample.nobs is given. For a random-slope model (closed-form route only),
each cluster keeps its own covariates and size, its slopes and other
level-2 effects are drawn first, and then its outcomes. sample.nobs is
not available. Other models draw their covariates from their fitted
moments.
A list of length nsim. Each element is a data frame with
sample.nobs rows and two attributes:
"truth" — named numeric vector of lavaan-side (x-space, constrained)
parameter values used to generate the dataset.
"truth_theta" — named numeric vector of the corresponding unconstrained
(theta-space) parameter values.
sampling() for single-observation draws from the predictive
distribution (prior/posterior predictive checks).
utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa("visual =~ x1 + x2 + x3", HolzingerSwineford1939) # Simulate one replicate dataset from the posterior sims <- simulate(fit, nsim = 1) head(sims[[1]]) # data frame attr(sims[[1]], "truth") # true lavaan-side (x-space) parameters attr(sims[[1]], "truth_theta") # corresponding unconstrained (theta-space) parameters # Simulate from the prior (e.g., for SBC) sims_prior <- simulate(fit, nsim = 5, prior = TRUE) lapply(sims_prior, nrow)utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa("visual =~ x1 + x2 + x3", HolzingerSwineford1939) # Simulate one replicate dataset from the posterior sims <- simulate(fit, nsim = 1) head(sims[[1]]) # data frame attr(sims[[1]], "truth") # true lavaan-side (x-space) parameters attr(sims[[1]], "truth_theta") # corresponding unconstrained (theta-space) parameters # Simulate from the prior (e.g., for SBC) sims_prior <- simulate(fit, nsim = 5, prior = TRUE) lapply(sims_prior, nrow)
Standardised solution of a latent variable model
standardisedsolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardisedSolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardizedsolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardizedSolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... )standardisedsolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardisedSolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardizedsolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... ) standardizedSolution( object, type = "std.all", se = TRUE, ci = TRUE, level = 0.95, postmedian = FALSE, postmode = FALSE, cov.std = TRUE, remove.eq = TRUE, remove.ineq = TRUE, remove.def = FALSE, nsamp = 250, ... )
object |
An object of class INLAvaan. |
type |
Character (dots and underscores are interchangeable:
|
se |
Logical. If TRUE, standard errors for the standardized parameters will be computed, together with a z-statistic and a p-value. |
ci |
If |
level |
The confidence level required. |
postmedian |
Logical; if TRUE, include posterior median in estimates. |
postmode |
Logical; if TRUE, include posterior mode in estimates. |
cov.std |
Logical. If |
remove.eq |
Logical. If |
remove.ineq |
Logical. If |
remove.def |
Logical. If |
nsamp |
The number of samples to draw from the approximate posterior distribution for the calculation of standardised estimates. |
... |
Additional arguments sent to |
For a model with random slopes (lavaan's rv()), the estimates are scaled
by the implied variances averaged over the covariates, which include the
mean and the variance of each slope. The level-1 row that carries a slope
gives the standardised mean slope. The slope's own rows are on the same
standardised-slope scale: its intercept is a standardised slope, and its
(residual) variance is the share of the outcome's level-1 variance that the
slope's (residual) variation adds.
A data.frame containing standardised model parameters.
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 " utils::data("HolzingerSwineford1939", package = "lavaan") # Fit a CFA model with standardised latent variables fit <- acfa( HS.model, data = HolzingerSwineford1939, test = "none", nsamp = 10, vb_correction = FALSE, verbose = FALSE ) standardisedsolution(fit, nsamp = 10, se = FALSE, ci = FALSE)HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 " utils::data("HolzingerSwineford1939", package = "lavaan") # Fit a CFA model with standardised latent variables fit <- acfa( HS.model, data = HolzingerSwineford1939, test = "none", nsamp = 10, vb_correction = FALSE, verbose = FALSE ) standardisedsolution(fit, nsamp = 10, se = FALSE, ci = FALSE)
Extract wall-clock timings for individual computation stages of a fitted
INLAvaan model.
timing(object, ...) ## S4 method for signature 'INLAvaan' timing(object, what = "total", ...)timing(object, ...) ## S4 method for signature 'INLAvaan' timing(object, what = "total", ...)
object |
An object of class INLAvaan. |
... |
Currently unused. |
what |
Character vector of timing segment names to return, or
|
A named numeric vector (class c("timing.INLAvaan",
"numeric")) of elapsed times in seconds. Printing formats short
durations as seconds, longer ones as minutes or hours.
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Total elapsed time timing(fit) # All stages timing(fit, what = "all") # Specific stages timing(fit, what = c("optim", "marginals"))HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Total elapsed time timing(fit) # All stages timing(fit, what = "all") # Specific stages timing(fit, what = c("optim", "marginals"))
Re-fit an INLAvaan model with modified arguments, in the style of
stats::update(). This is convenient for prior-sensitivity analyses
(vary dp or the prior() modifiers), iterative respecification (swap the
model or add syntax), and swapping approximation settings (e.g.
marginal_method, nsamp) without retyping the whole call.
## S4 method for signature 'INLAvaan' update(object, model, add, ..., evaluate = TRUE)## S4 method for signature 'INLAvaan' update(object, model, add, ..., evaluate = TRUE)
object |
An object of class INLAvaan. |
model |
Optional replacement model, in the same form accepted by
|
add |
Optional lavaan syntax appended to the original model. Requires
the original model to have been specified as a syntax string, and is
ignored when |
... |
Further arguments passed to |
evaluate |
Logical. If |
The method edits the recorded inlavaan() call, replacing model (or
extending it via add) and overriding any argument supplied through ...,
then re-evaluates it. Because INLAvaan extends the lavaan class, a
dedicated method is required: without it, update() would dispatch to
lavaan's method and silently return a frequentist lavaan fit
instead of a Bayesian one.
When the parameter structure is preserved (no change to model, add, or
model.type), the previous posterior mode is passed as the optimiser's
starting value (a warm start), so mode-finding is typically much faster than
a cold fit. This makes prior sweeps particularly cheap. The warm start only
changes the optimiser's starting point, not the fitted result. Supply
start explicitly to override it.
Fits produced by INLAvaan versions that did not record their call cannot be
updated; re-fit once with inlavaan() (or acfa(), asem(), agrowth())
and update() will work on the new object.
A re-fitted INLAvaan object, or the updated call when
evaluate = FALSE.
## Not run: model <- "visual =~ x1 + x2 + x3" fit <- acfa(model, data = lavaan::HolzingerSwineford1939) # Prior sensitivity: refit under a tighter loading prior (warm-started) fit2 <- update(fit, dp = priors_for(lambda = "normal(0,1)")) # Respecify: add a residual covariance fit3 <- update(fit, add = "x1 ~~ x2") compare(fit, fit3) # Swap approximation settings only fit4 <- update(fit, marginal_method = "sampling", nsamp = 4000) ## End(Not run)## Not run: model <- "visual =~ x1 + x2 + x3" fit <- acfa(model, data = lavaan::HolzingerSwineford1939) # Prior sensitivity: refit under a tighter loading prior (warm-started) fit2 <- update(fit, dp = priors_for(lambda = "normal(0,1)")) # Respecify: add a residual covariance fit3 <- update(fit, add = "x1 ~~ x2") compare(fit, fit3) # Swap approximation settings only fit4 <- update(fit, marginal_method = "sampling", nsamp = 4000) ## End(Not run)
Extract the posterior variance-covariance matrix of model parameters from a
fitted INLAvaan model.
## S4 method for signature 'INLAvaan' vcov(object, type = c("lavaan", "theta"), ...)## S4 method for signature 'INLAvaan' vcov(object, type = c("lavaan", "theta"), ...)
object |
An object of class INLAvaan. |
type |
Character. |
... |
Currently unused. |
A square numeric matrix.
summary(), coef(), standardisedsolution()
HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Default: posterior covariance of lavaan parameters vcov(fit) # Internal parameterisation (Laplace approximation) vcov(fit, type = "theta")HS.model <- " visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 " utils::data("HolzingerSwineford1939", package = "lavaan") fit <- acfa(HS.model, HolzingerSwineford1939, std.lv = TRUE, nsamp = 100, test = "none", verbose = FALSE) # Default: posterior covariance of lavaan parameters vcov(fit) # Internal parameterisation (Laplace approximation) vcov(fit, type = "theta")
Computes the WAIC of a fitted INLAvaan model in closed form from the
fit's Laplace summary – the same per-unit Taylor quantities behind
loo(), with no posterior draws and no Monte Carlo error. Single-level
models are scored per subject; two-level models are scored per cluster by
default, matching the units used by loo(). For a two-level model
type = "loso" instead scores the conditional (leave-one-unit-out)
WAIC; see Details.
waic(x, ...) ## S3 method for class 'INLAvaan' waic( x, type = c("auto", "loso", "loco"), units = NULL, second_order = TRUE, cores = NULL, verbose = FALSE, ... ) ## S3 method for class 'inlavaan_internal' waic( x, type = c("auto", "loso", "loco"), units = NULL, second_order = TRUE, cores = NULL, verbose = FALSE, ... ) ## S3 method for class 'inlavaan_waic' print(x, ...) ## S3 method for class 'inlavaan_waic' summary(object, ...)waic(x, ...) ## S3 method for class 'INLAvaan' waic( x, type = c("auto", "loso", "loco"), units = NULL, second_order = TRUE, cores = NULL, verbose = FALSE, ... ) ## S3 method for class 'inlavaan_internal' waic( x, type = c("auto", "loso", "loco"), units = NULL, second_order = TRUE, cores = NULL, verbose = FALSE, ... ) ## S3 method for class 'inlavaan_waic' print(x, ...) ## S3 method for class 'inlavaan_waic' summary(object, ...)
x |
A fitted INLAvaan object (or its |
... |
Not used. |
type |
Unit type: |
units |
Optional integer vector of unit indices to score; defaults to all units. |
second_order |
Logical; include the second-order (Hessian) terms
(default |
cores |
Number of cores for differentiating the unit scores. The
default |
verbose |
Logical; print progress (default |
object |
A fitted INLAvaan object, or an |
Writing and expanding it
to second order about the posterior mode, with the posterior taken as
, both WAIC terms are available in closed form:
the pointwise log predictive density is the same
Gaussian integral loo() computes, and the penalty is the polynomial
with and the unit's score and Hessian. Then
and .
Existence. is a polynomial in the posterior
moments, so unlike the lpd and log CPO integrals of loo() it is finite
for every unit and carries no condition of its own. The second-order WAIC
therefore exists exactly where its lpd term does: where
is positive definite, equivalently
for the spectrum of . The
log CPO condition is irrelevant here, since the WAIC
reads no case-deletion term: a unit whose deleted posterior is improper
can still carry an exact second-order WAIC. Where the lpd term fails,
every estimate is reported at first order over all units and warns –
is a headline predictive score, and
mixing two Taylor orders within one reported number is exactly what
loo() refuses for (the mixed
alternative is reserved for p_loo, a secondary diagnostic). That
fallback is exact rather than merely lower-order: the identity
holds pointwise, so the first-order WAIC is the
first-order LOO score. No other threshold is applied: as in loo(),
existence is the only condition the package acts on.
The same model restrictions as loo() apply, and so does the flavour
rule: fits with fixed.x = TRUE are scored conditionally on the
exogenous covariates, fits with fixed.x = FALSE jointly (see loo()).
For composites, also counts the indicator
(co)variances that lavaan fixes at their sample values, as p_loo does.
Marginal vs conditional WAIC (two-level models). The default
per-cluster scoring is the marginal WAIC, which corresponds to
leave-one-cluster-out cross-validation – prediction for a new cluster.
Setting type = "loso" scores the conditional WAIC, corresponding to
leave-one-unit-out – prediction for a new observation within an
observed cluster. The two answer different questions and are easily
conflated (Merkle, Furr & Rabe-Hesketh, 2019); the per-cluster marginal
is the usual model-comparison target, so it is the default, and
type = "loso" warns. This matches loo(type = "loso") – the two read
the same estimand off the same expansion.
When inlavaan()'s test includes "loo" or "waic" (e.g.
test = "full"), the WAIC is derived at fit time from the same Taylor
pass as the LOO at no extra cost and stored with the fit: waic(fit)
then returns the stored result when called with default arguments, and
fitmeasures() reports waic, p_waic, se_waic as part of "all"
for free. Under the default test = "standard" nothing is stored and
waic(fit) computes it on demand. If the loo package is attached it
masks this generic, but dispatch on INLAvaan objects continues to work.
An object of class inlavaan_waic: a list with per_unit
(pointwise lpd, p_waic, elpd_waic, with the same unit/group
identification as loo()), estimates (matrix with rows
elpd_waic, p_waic, waic and columns Estimate, SE), type,
flavour, n_units, n_groups, n_lpd_ok (units whose second-order
lpd exists), second_order (whether it was requested) and
use_second (whether it was used). summary() is an alias for
print(): it prints the same output and returns the result invisibly.
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) waic(fit)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) waic(fit)