| Type: | Package |
| Title: | Robust Test Statistics for Structural Equation Models |
| Description: | Supports penalized eigenvalue block-averaging and penalized regression p-values (Foldnes, Moss, Grønneberg, 2024) <doi:10.1080/10705511.2024.2372028>, including their extension to nested model comparison (Foldnes, Grønneberg, Moss, 2026) <doi:10.3758/s13428-026-02968-4>, as well as traditional p-values such as Satorra-Bentler. All p-values can be calculated using unbiased or biased gamma estimates (Du, Bentler, 2022) <doi:10.1080/10705511.2022.2063870> and two choices of chi square statistics. The tests apply to any minimum-discrepancy estimator – ML, GLS, ULS, and categorical WLSMV/DWLS – with experimental support for full-information maximum-likelihood (FIML) fits under missing data. |
| Version: | 0.9.0 |
| License: | GPL (≥ 3) |
| Encoding: | UTF-8 |
| URL: | https://github.com/JonasMoss/semTests |
| Depends: | R (≥ 3.5.0) |
| Imports: | lavaan (≥ 0.6-16), methods |
| Suggests: | testthat (≥ 3.0.0), covr, xml2, knitr, rmarkdown |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| Config/roxygen2/version: | 8.0.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-06-09 12:18:33 UTC; jonas |
| Author: | Jonas Moss |
| Maintainer: | Jonas Moss <jonas.moss.statistics@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-06-09 14:00:02 UTC |
Reject anything that is not a fitted lavaan object.
Description
The class gate for pvalues() / pvalues_nested(). It must run before any
@-slot access so a NULL / data.frame / list argument fails with a readable
message instead of a cryptic S4 "no slot of name ..." error – the nested
entry point in particular reads m0@test to compute the degrees of freedom
before the support gate runs.
Usage
check_lavaan(x, arg = "object")
Arguments
x |
The object passed by the user. |
arg |
The argument name, used in the message (e.g. |
Value
x, invisibly.
Reject a fit whose configuration is outside the supported surface.
Description
The single entry-level gate for pvalues(). It admits exactly the
configurations documented in semTests-support – the supported continuous
and categorical estimators, complete data, and single-group FIML – and stops
with a pointer to ?semTests-support otherwise. Statistic- and gamma-specific
rejections (the normal-theory-only RLS statistic and UG gamma) depend on the
parsed test string and stay with the code that consumes them (make_chisqs(),
gamma()); this gate is purely fit-shape.
Usage
check_supported(fit)
Arguments
fit |
A fitted |
Value
fit, invisibly.
Reject a nested pair whose configuration is outside the supported surface.
Description
The single entry-level gate for pvalues_nested(): categorical nesting is
deferred, missing-data nesting requires both fits to be FIML, and FIML nesting
supports method = "2000" only. Each fit is also run through
check_supported(). The UG-gamma rejection for FIML stays in the p-value
engine because it depends on the parsed test string.
Usage
check_supported_nested(m0, m1, method, A.method = "exact")
Arguments
m0, m1 |
Two nested |
method |
The nested reduction method, |
A.method |
The FIML restriction map, |
Value
TRUE, invisibly.
Common default value of 2.
Description
Common default value of 2.
Usage
default(x)
Calculate the jth eba pvalue.
Description
Calculate the jth eba pvalue.
Usage
eba_pvalue(chisq, lambdas, j)
FIML goodness-of-fit eigenvalues in saturated eta-space.
Description
FIML goodness-of-fit eigenvalues in saturated eta-space.
Usage
fiml_lambdas(fit, df)
FIML Satorra-2000 nested restriction eigenvalues.
Description
FIML Satorra-2000 nested restriction eigenvalues.
Usage
fiml_lambdas_nested(m0, m1, df, A.method = c("exact", "delta"))
Saturated observed-data FIML eta-space pieces.
Description
Saturated observed-data FIML eta-space pieces.
Usage
fiml_saturated_moments(fit)
Provenance of a semTests_pvalues object.
Description
Records the fit-level options actually used to compute the p-values, so the returned object is self-describing across estimators and data types.
Usage
fit_provenance(fit, nested, method = NA, A.method = NA, df = NULL)
Get gamma from a model.
Description
Get gamma from a model.
Usage
gamma(m1, unbiased = 1, m0 = NULL)
Arguments
m1 |
Model to extract gamma from. |
unbiased |
Biased (1), unbiased (2), or both (3). |
m0 |
Optional second model, used if |
Value
List of (un)biased gammas.
Calculate unbiased gamma from gamma and object.
Description
Calculate unbiased gamma from gamma and object.
Usage
gamma_to_gamma_unbiased(gammas, object)
Arguments
gammas |
List of gammas for each group. |
object |
|
Value
List of unbiased gammas.
Upper tail of a linear combination of chi-square_1 variables
Description
Computes P(Q > q) for Q = \sum_j \lambda_j Z_j^2 with
independent Z_j \sim N(0,1). Drop-in replacement for
CompQuadForm::imhof(q, lambda)$Qq: the Imhof integral in the body of the
distribution, a Lugannani-Rice saddlepoint in the far tail where the integral
degrades.
Usage
imhof_pvalue(q, lambda)
Arguments
q |
Numeric scalar (or vector) of thresholds; the observed chi-square. |
lambda |
Numeric vector of eigenvalues (may be mixed sign). |
Value
Numeric vector of upper-tail probabilities, length length(q).
Is this the classical normal-theory, complete-data, ML case?
Description
The Du-Bentler unbiased gamma and the RLS (browne.residual.nt.model)
statistic are only defined here; off this case the eigenvalue machinery uses
the biased gamma and the estimator's own (uncorrected) statistic.
Usage
is_classic_nt(fit)
Is this a continuous FIML/missing-data lavaan fit?
Description
Is this a continuous FIML/missing-data lavaan fit?
Usage
is_fiml(fit)
Reference spectrum of the nested test, without forming the full UGamma.
Description
For Satorra's (2000) method the nonzero eigenvalues are those of the m x m
matrix C^{-1} D' Gamma D (see nested_factor_2000); the q x q UGamma and
its eigendecomposition are avoided. The (2001) method has no such reduction,
so the top-df eigenvalues of the full (U0 - U1) Gamma are returned.
Usage
lambdas_nested(m0, m1, method = c("2000", "2001"), unbiased = 1, df)
Arguments
m0, m1 |
Two nested |
method |
Either |
unbiased |
Biased (1), unbiased (2), or both (3) gamma. |
df |
Number of restrictions (degrees-of-freedom difference). |
Value
A list of eigenvalue vectors, one per gamma estimate.
Calculate nested ugamma.
Description
This can also be used with restrictions.
Usage
lav_ugamma_nested_2000(m0, m1, gamma, a = NULL, method = "delta")
Arguments
m0, m1 |
Two nested |
gamma |
Gamma weighted by groups. |
a |
The |
method |
Method passed to |
Value
Ugamma for nested object.
Calculate the scaled and shifted / the mean-variance adjusted p-value
Description
Calculate the scaled and shifted / the mean-variance adjusted p-value
Usage
scaled_and_shifted(chisq, lambdas)
Arguments
chisq |
Chi-square fit value from a lavaan object. |
lambdas |
Eigenvalues of UG matrix. |
Value
The scaled and shifted p-value or the mean-variance adjusted p-value.
Returns if not NA; else converts NA to NULL.
Description
Returns if not NA; else converts NA to NULL.
Usage
nanull(x)
Gamma-free factors of the reduced nested spectrum (Satorra 2000).
Description
Returns the restriction-space factor D and companion C such that the m
nonzero eigenvalues of the full q x q UGamma equal those of
C^{-1} D' Gamma D, an m x m problem with m the number of restrictions.
This is the materialised reduction of Moss (2026): the full q x q U matrix
and its eigendecomposition are never formed. With U = D C^+ D' one has
D = V Delta P^+ A' and C = A P^+ A', where V is the (group-weighted)
weight, Delta the Jacobian, P^+ the inverted information, and A the
restriction matrix.
Usage
nested_factor_2000(m0, m1, a = NULL)
Calculate the jth pall pvalue.
Description
Calculate the jth pall pvalue.
Usage
pall(chisq, lambdas)
Calculate the jth eba pvalue.
Description
Calculate the jth eba pvalue.
Usage
peba_pvalue(chisq, lambdas, j)
Calculate penalized OLS pvalue.
Description
Calculate penalized OLS pvalue.
Usage
pols_pvalue(chisq, lambdas, gamma)
Print method for p-values from pvalues() / pvalues_nested().
Description
Prints the p-values, then a one-line provenance footer (estimator, data type, information, df) recording the options used.
Usage
## S3 method for class 'semTests_pvalues'
print(x, ...)
Arguments
x |
A |
... |
Passed to the default print method. |
Value
x, invisibly.
Calculate the jth all pvalue.
Description
Calculate the jth all pvalue.
Usage
pvalue_all(chisq, lambdas)
P value function for one and two arguments.
Description
P value function for one and two arguments.
Usage
pvalues_(
m0,
m1,
unbiased,
trad,
eba,
peba,
pols,
chisq = c("ml", "rls"),
extras = FALSE,
method,
A.method = "exact"
)
Value
pvalues.
Calculate p-values for one or two lavaan objects.
Description
Calculate p-values for a lavaan object using several methods,
including penalized eigenvalue block-averaging and penalized regression
estimators. The recommended choices of p-values are included as default
values. Multiple p-values can be returned simultaneously.
Usage
pvalues(object, tests = if (is_classic_nt(object)) "pEBA4_RLS" else "pEBA4")
pvalues_nested(
m0,
m1,
method = c("2000", "2001"),
tests = if (is_classic_nt(m0)) "PALL_UG_ML" else "PALL",
A.method = c("exact", "delta")
)
Arguments
object, m0, m1 |
One or two |
tests |
A list of tests to evaluate on the
form |
method |
For nested models, choose between |
A.method |
For nested FIML models, choose |
Details
The test argument is a list of character strings on the form
(test)(ug?)(ml?), for instance, SB_UG_RLS.
The first part of the string specifies the desired test. The supported tests are listed below.
If
UGis included in the string the unbiased estimator of the fourth order moment matrix (Du, Bentler, 2022) is used. If not, the standard biased matrix is used. There is no simple relationship between p-value performance and the choice ofunbiased.The final part specifies the chi square statistic. The
MLchoice uses the chi square based on the normal discrepancy function (Bollen, 2014). TheRLSchoice (default) uses the reweighted least squares statistic of Browne (1974).
The peba method is the recommended default. It partitions the eigenvalues
into j equally sized sets (if not possible, the smallest set is incomplete),
shrinks them towards their common mean, and averages within each set. Provide
a list of integers j to partition with respect to; the best choices are
typically about 2–6. It was introduced by Foldnes, Moss, & Grønneberg
(2024).
pols is a penalized regression method with a penalization term ranging from
0 to infinity. Foldnes, Moss, & Grønneberg (2024) studied pols=2, which has
good performance in a variety of contexts.
pall penalizes all eigenvalues in ugamma, while all uses all eigenvalues
without penalization. pall is the recommended option for nested models, for
which the penalized methods were extended and evaluated by Foldnes,
Grønneberg, & Moss (2026).
The eba method is the unpenalized predecessor of peba (Foldnes &
Grønneberg, 2018): it averages within the eigenvalue blocks without shrinkage.
It is generally outperformed by peba and is kept mainly for comparison;
eba with j=2 – j=4 tends to work best.
In addition, you may specify a
-
stdthe standard p-value where the choice ofchisqis approximated by a chi square distribution. -
sbSatorra-Bentler p-value. The p-value proposed by Satorra and Bentler (1994). -
ssThe scaled and shifted p-value proposed by Asparouhov & Muthén (2010). -
sfThe scaled F p-value proposed by Wu and Lin (2016).
The unbiased argument is TRUE if the unbiased estimator of the
fourth order moment matrix (Du, Bentler, 2022) is used. If FALSE, the
standard biased matrix is used. There is no simple relationship between
p-value performance and the choice of unbiased.
The chisq argument controls which basic test statistic is used. The ml
choice uses the chi square based on the normal discrepancy function (Bollen, 2014).
The rls choice uses the reweighted least squares statistic of Browne (1974).
Estimators and data types
The authoritative list of supported estimators, data types, and
configurations – the matrix this function is validated against – is
semTests-support (?semTests-support). In brief:
The limiting null law of the test statistic is a weighted sum of
chi-squares for any minimum-discrepancy estimator, so these tests are not
specific to normal-theory ML. pvalues() supports ML/MLM/MLR, GLS, ULS,
FIML (missing data), and categorical WLSMV/DWLS, with single- and
multi-group continuous and categorical fits; pvalues_nested() supports
the continuous estimators (nested categorical is not yet implemented). The
model must be fit so that lavaan exposes the asymptotic moment covariance –
fit with a robust test such as test = "satorra.bentler", or with
estimator = "MLM"/"MLR"/"DWLS". Off the classical continuous-complete-data
ML case, the RLS statistic (browne.residual.nt.model) and the unbiased
(UG) Du-Bentler gamma are undefined and are refused; the standard statistic
and the biased gamma are used instead. ADF/WLS is the degenerate exception,
where the test equals the ordinary chi-square and the correction adds nothing.
Support beyond classical normal-theory ML – GLS, ULS, categorical WLSMV/DWLS, FIML missing data, and nested FIML comparison – is experimental as of 0.9.0; see the Stability note in semTests-support.
The information matrix (expected vs observed) is taken from the fit; to
control it, fit the lavaan model with information = "expected" or
"observed". The returned object records the estimator, statistic,
information type, data type and degrees of freedom actually used; see its
printed footer and attr(x, "semtests").
Value
A named numeric vector of p-values, of class semTests_pvalues,
carrying an "semtests" attribute that records the options used (estimator,
statistic, information type, gamma type, data type, and degrees of freedom).
References
Foldnes, N., Moss, J., & Grønneberg, S. (2024). Improved goodness of fit procedures for structural equation models. Structural Equation Modeling: A Multidisciplinary Journal, 1-13. https://doi.org/10.1080/10705511.2024.2372028
Foldnes, N., Grønneberg, S., & Moss, J. (2026). Penalized eigenvalue block averaging: Extension to nested model comparison and Monte Carlo evaluations. Behavior Research Methods. https://doi.org/10.3758/s13428-026-02968-4
Satorra, A., & Bentler, P. M. (1994). Corrections to test statistics and standard errors in covariance structure analysis. https://psycnet.apa.org/record/1996-97111-016
Asparouhov, & Muthén. (2010). Simple second order chi-square correction. Mplus Technical Appendix. https://www.statmodel.com/download/WLSMV_new_chi21.pdf
Wu, H., & Lin, J. (2016). A Scaled F Distribution as an Approximation to the Distribution of Test Statistics in Covariance Structure Analysis. Structural Equation Modeling. https://doi.org/10.1080/10705511.2015.1057733
Foldnes, N., & Grønneberg, S. (2018). Approximating Test Statistics Using Eigenvalue Block Averaging. Structural Equation Modeling, 25(1), 101-114. https://doi.org/10.1080/10705511.2017.1373021
Du, H., & Bentler, P. M. (2022). 40-Year Old Unbiased Distribution Free Estimator Reliably Improves SEM Statistics for Nonnormal Data. Structural Equation Modeling: A Multidisciplinary Journal, 29(6), 872-887. https://doi.org/10.1080/10705511.2022.2063870
Bollen, K. A. (2014). Structural Equations with Latent Variables (Vol. 210). John Wiley & Sons. https://doi.org/10.1002/9781118619179
Browne. (1974). Generalized least squares estimators in the analysis of covariance structures. South African Statistical Journal. https://doi.org/10.10520/aja0038271x_175
See Also
semTests-support for the full list of supported configurations.
Examples
library("semTests")
library("lavaan")
model <- "visual =~ x1 + x2 + x3
textual =~ x4 + x5 + x6
speed =~ x7 + x8 + x9"
object <- cfa(model, HolzingerSwineford1939, estimator = "MLM")
pvalues(object)
# For the pEBA6 method with biased gamma and ML chisq statistic:
pvalues(object, "pEBA6_ML")
# Nested model comparison (constrain the textual loadings to be equal):
constrained <- "visual =~ x1 + x2 + x3
textual =~ a*x4 + a*x5 + a*x6
speed =~ x7 + x8 + x9"
m1 <- cfa(model, HolzingerSwineford1939, estimator = "MLM")
m0 <- cfa(constrained, HolzingerSwineford1939, estimator = "MLM")
pvalues_nested(m0, m1)
Renormalise the eigenvalue spectrum under missing data.
Description
For complete data, lavaan's UGamma spectrum has mean equal to the robust
scaling factor, so the reference law sum lambda_j chi^2_1 has the right
first moment. Under missing data (FIML) lavaan's UGamma is mis-scaled (its
mean is off by a constant, ~1.8x in practice), which makes the eigenvalue
p-values badly anti-conservative. The spectrum shape is correct, so we
rescale it to mean = chisq.scaling.factor. This is a no-op for complete
data (where the mean already equals the scaling factor) and requires a robust
test, which FIML fits always have.
Usage
rescale_missing(lambdas_list, fit, df)
Calculate the scaled_f p-value.
Description
Calculate the scaled_f p-value.
Usage
scaled_f(chisq, eig)
Arguments
chisq |
Chi-square fit value from a lavaan object. |
eig |
eig of UG matrix. |
Value
scaled f p-value.
Supported estimators, data types, and configurations
Description
The authoritative list of what pvalues() and pvalues_nested() support.
The eigenvalue-based p-values target the limiting null law of the test
statistic – a weighted sum of chi-squares – which holds for any
minimum-discrepancy estimator, so the tests are not specific to normal-theory
ML. This page is the single source of truth: anything not listed here is
refused at the entry point (see check_supported()), and the test suite is
written against it.
Details
Single-model (pvalues())
| Estimator | Data | Groups | Missing | Status |
| ML / MLM / MLR | continuous | single or multi | complete | supported |
| GLS | continuous | single or multi | complete | supported |
| ULS | continuous | single or multi | complete | supported |
| ML / MLR (FIML) | continuous | single only | FIML | supported |
| WLSMV / DWLS | categorical | single or multi | complete | supported |
| WLS (ADF) | continuous | single | complete | degenerate (no-op) |
Nested (pvalues_nested())
| Estimators | Data | Groups | Missing | Method | A.method | Status |
| ML/MLM/MLR, GLS, ULS | continuous | single or multi | complete | 2000 or 2001 | -- | supported |
| ML / MLR (FIML) | continuous | single only | FIML (both fits) | 2000 only | exact or delta | supported |
| WLSMV / DWLS | categorical | any | any | -- | -- | rejected |
| any | continuous | -- | mixed / non-FIML | -- | -- | rejected |
Stability
The classical normal-theory ML path (continuous, complete data) is the mature, paper-backed core and is stable. Support for the other estimators (GLS, ULS, categorical WLSMV/DWLS), for FIML missing-data fits, and for nested comparison under FIML is experimental as of 0.9.0: the methodology rests on the references but the implementation surface is newer and less Monte-Carlo-vetted, so the API and numerical details may change. Everything in the tables above is validated and tested; configurations outside them are refused at the entry point.
Statistic and gamma options
Test names have the form (test)_(ug?)_(ml|rls?) (e.g. "SB_UG_RLS"); see
pvalues() for the test families. Two of the options are defined only for the
classical case:
The RLS statistic (
browne.residual.nt.model, Browne 1974) and theUG(Du-Bentler) unbiased gamma are available only for classical normal-theory ML: continuous, complete data,estimator = "ML". Off that case they are undefined – lavaan silently degrades RLS to ADF, and the Du-Bentler correction has no derivation – so requesting them is refused. The standard statistic and the biased gamma are used instead.-
FIML (missing data) uses the biased gamma and the standard statistic only;
UGandRLSare refused. The missing-data spectrum is renormalised (seerescale_missing()). FIML requires a single group, continuous data, and no fixed exogenous covariates, and – because the inference rests on the observed information under MAR – a fit withinformation = "expected"triggers a warning (seewarn_fiml_information()).
Why ADF/WLS is degenerate
For full WLS (ADF) the model test statistic already uses the
asymptotically-correct weight matrix, so its null distribution is the exact
chi-square and the eigenvalue correction collapses to the identity: every
p-value equals the ordinary 1 - pchisq(chisq, df). It is accepted but adds
nothing.
See Also
Split string into options.
Description
Split string into options.
Usage
split_input(string)
Arguments
string |
Input string |
Calculate traditional pvalues.
Description
Calculate traditional pvalues.
Usage
trad_pvalue(
df,
chisq,
lambdas,
type = c("std", "sf", "ss", "sb", "pall", "all")
)
Arguments
df, chisq, lambdas, type |
Parameters needed to calculate the p-values. |
Value
Traditional p-values.
Calculate non-nested gamma
Description
Calculate non-nested gamma
Usage
ugamma(object, unbiased = 1)
Calculate nested gamma
Description
Calculate nested gamma
Usage
ugamma_nested(m0, m1, method = c("2000", "2001"), unbiased = 1)
Warn when a FIML fit uses expected information.
Description
Under missing data the expected (Fisher) information is consistent only under
MCAR; under MAR – the regime FIML is adopted for – the observed information
is required for valid inference (Kenward & Molenberghs, 1998). lavaan defaults
to observed information for missing = "ml", but it silently accepts
information = "expected", which would make the eigenvalue p-values rest on
the stronger MCAR assumption. This emits a warning (not an error: expected is
still valid under MCAR).
Usage
warn_fiml_information(fit)
Arguments
fit |
A fitted |
Details
lavaan stores information as a length-2 vector, so we test its first entry.
Value
fit, invisibly.