Bifactor ESEM, ESEM, and CFA — with multi-group measurement invariance — for continuous and ordered data in R.
bifactory fits Exploratory Structural Equation Models (ESEM), bifactor ESEM
(B-ESEM), and Confirmatory Factor Analysis (CFA), and runs multi-group
measurement invariance. It combines EFA-style rotation (via GPArotation /
psych) with structural estimation (via lavaan), and handles both
continuous data (MLR) and ordered/Likert data (WLSMV).
For the CFA, ESEM and B-ESEM fits, loadings, standard errors and fit indices follow Mplus conventions, so a model can be checked against an established reference implementation.
The distinctive piece is the ordered bifactor ESEM path: a custom diagonally weighted least squares (DWLS) routine over polychoric correlations, with an orthogonal target rotation, a mean-and-variance-adjusted scaled chi-square, and rotation-aware delta-method standard errors — the combination an orthogonal bifactor target rotation requires under WLSMV.
# install.packages("remotes")
# remotes::install_github("leondebeer/bifactory")
install.packages("bifactory", repos = c('https://leondebeer.r-universe.dev', 'https://cloud.r-project.org'))
Requires R (>= 4.1.0) and lavaan (>= 0.7-2), whose polychoric correlations are the exact two-step values that multi-group comparisons with Mplus depend on.
library(bifactory)
# Continuous ESEM on the classic Holzinger-Swineford data
data("HolzingerSwineford1939", package = "lavaan")
d <- HolzingerSwineford1939[, paste0("x", 1:9)]
fit <- esem(d, nfactors = 3, factor_names = c("Visual", "Textual", "Speed"))
std_loadings(fit) # standardized loadings (with cross-loadings)
factor_correlations(fit) # factor correlation matrix
fitMeasures(fit, c("cfi", "tli", "rmsea", "srmr")) # fit indices
A general factor G loading on all items, plus orthogonal domain-specific factors. For ordered indicators this uses the custom DWLS/WLSMV path:
fit_b <- besem_ordered(
data = my_items,
specific_factors = list(
EX = c("ex1", "ex2", "ex3", "ex4"),
MD = c("md1", "md2", "md3"),
CI = c("ci1", "ci2", "ci3")
)
)
summary(fit_b, fit.measures = TRUE, standardized = TRUE)
# Reliability and dimensionality indices come from the comparison pipeline,
# not from a single fit:
spec <- specify_model(EX = ex_items, MD = md_items, CI = ci_items,
data = my_items, ordered = TRUE)
results <- run_comparison(spec)
compute_indices(results) # omega, omega_H, ECV, PUC, H, alpha
spec <- specify_model(
EX = ex_items, MD = md_items, CI = ci_items,
data = df, group = "sex", ordered = TRUE
)
inv <- esem_invariance(spec) # configural -> weak -> strong -> strict
print(inv) # comparison table with delta-CFI / delta-RMSEA
Multi-group B-ESEM invariance (model = "besem") is validated against Mplus for
small numbers of groups (the function warns at larger group counts, where the
optimiser can stall on the bifactor target rotation).
| Function | Purpose |
|---|---|
specify_model() |
Define a model (factors, data, group, ordered) |
esem() / besem()
|
ESEM / bifactor ESEM for continuous data (MLR) |
esem_ordered() / besem_ordered()
|
ESEM / bifactor ESEM for ordered data (WLSMV) |
run_comparison() |
Fit CFA, ESEM, and B-ESEM together and compare |
esem_invariance() |
Multi-group configural / weak / strong / strict invariance |
compute_indices() |
Reliability and dimensionality indices (omega suite, ECV, PUC, H, alpha) |
make_target() / make_bifactor_target()
|
Target rotation matrices |
alignment_check() |
Pre-fit ICM-CFA misspecification diagnostic |
std_loadings(), factor_correlations(), parameters()
|
Tidy extractors |
If you have Mplus and MplusAutomation installed, run_comparison() can
generate the .inp files, run them, and report R-vs-Mplus deltas. This is
entirely optional — every estimator runs in R alone — and engages only when you
supply an Mplus output folder.
results <- run_comparison(spec, mplus_folder = "path/to/mplus_output")
citation("bifactory")
Issues and pull requests are welcome. Please open an issue to discuss larger changes before submitting a PR.
AGPL-3. You may use bifactory (including commercially), but modified
or network-served versions must make their source available under the same
license.