Building and Estimating Structural Equation Models
Overview
A powerful, easy to use syntax for specifying and estimating complex Structural Equation Models. Models can be estimated using Partial Least Squares Path Modeling or Covariance-Based Structural Equation Modeling or covariance based Confirmatory Factor Analysis (Ray, Danks, and Valdez 2021 doi:10.2139/ssrn.3900621).
New features
predict()now works for CB-SEM models fromestimate_cbsem(), using the model-implied conditional expectation of de Rooij et al. (2023), the rule oflavaan::lavPredictY().predict_DAandpredict_EAchoose the predictor items as for PLS and PLSc, so the three estimators' predictions are comparable. Models with interaction terms, higher-order constructs, ordinal indicators or multiple groups, and non-converged or inadmissible fits, stop with the reason (#426).
Fixed
predict()andpredict_pls()pushed composite scores, built from the uncorrected outer weights, through the PLSc-corrected path coefficients and loadings whenever a model hadreflective()constructs. PLSc coefficients describe the common factors, not the composites, so the predictions were over-dispersed (by 1/sqrt(rho_A) with a single antecedent) or reweighted across correlated antecedents. PLSc models are now predicted with the model-implied conditional expectation of de Rooij et al. (2023), using the indicator correlations implied by the PLSc estimates;predict_DAandpredict_EAset the predictor items. Inadmissible PLSc solutions stop with the reason (the checks are listed in?predict.seminr_model), and PLSc models with interaction terms are no longer predicted (#427). Models without reflective constructs are unchanged (#425).
Show all changes in 2.6.0Hide the rest of the changes in 2.6.0
- In
predict_pls(), a PLSc solution can be inadmissible in a training fold even when the full-sample solution is admissible, especially with few folds (for the mobi ECSI model, in about 70% of 5-fold and 30% of 10-fold runs). Such folds are skipped with one warning that names them: their test rows areNAfor both PLS and the LM benchmark, and the prediction metrics use the remaining rows. If every fold is inadmissible,predict_pls()stops. The PLS chain is never used as a fallback. Cross-validation also no longer turns any error or warning inside a fold into a failure with an unrelated message ("row names supplied are of the wrong length"); they now reach the caller. - Models estimated by seminr < 2.6.0 and saved (e.g. with
saveRDS()) keep their old PLSc estimates.predict()on such a model that mixesreflective()constructs with Mode A or unit-weighted composites now warns to re-estimate it.estimate_pls()stamps models withseminr_versionfor this purpose. - PLSc (
estimate_pls()withreflective()constructs) corrected the paths into and out of Mode A and unit-weighted composites as if they were common factors, by dividing their correlations by sqrt(rho_A). Only Mode B composites were left uncorrected. Consistent PLS corrects only common factors and treats composites as fully reliable (Dijkstra & Henseler, 2015), as cSEM does. All composites now use rho_A = 1 in PLSc. **Path coefficients and R² change for models that mixreflective()constructs with Mode A or unit-weightedcomposite()constructs.** Loadings, all-reflective models, models with only Mode B composites next to reflective constructs, and models without reflective constructs are unchanged. Therho_Areported bysummary()is unchanged. CallingPLSc()directly on a model without reflective constructs now leaves it unchanged (it used to disattenuate its composites) (#430). - PLSc corrected a two-stage
higher_composite()with rho_A computed on its lower-order construct (LOC) scores, as if the HOC were a common factor of them. A HOC of composite LOCs is now treated as fully reliable (rho = 1). A HOC of common-factor LOCs is corrected with the reliability of its stage-2 proxy, a weighted sum of error-laden LOC scores: w'S*w, with the LOCs' stage-1 reliabilities on the diagonal of S* (van Riel et al., 2017), as cSEM's two-stage approach does. **Path coefficients and R² change for PLSc models withhigher_composite()constructs** (on mobi, IE -> Satisfaction 0.953 -> 0.920 with reflective LOCs; cSEM 0.918). A HOC of common factors is now corrected even when no stage-2 construct is reflective, and areflective()construct that is declared but not used in the structural model no longer makesestimate_pls()run PLSc. predict_pls()on a model fromestimate_cbsem()failed with an unrelated low-level error (the CB-SEM object inheritsseminr_modelbut has no weights or loadings). It now stops with a message pointing topredict().predict()andpredict_pls()on a model fromestimate_cfa()failed the same way; they now stop and say that a CFA has no structural model to predict from (#426).plot.reliability_table()drew its reference line at 0.708, which is the indicator **loading** threshold (0.708² ≈ 0.50 explained variance). The metrics that plot shows — Cronbach's alpha, rhoA and rhoC — are construct-level internal consistency reliabilities, judged against **0.70**. The line is now drawn at 0.70, and the value is exposed as athresholdargument so it can be overridden and so the intended value is documented rather than buried (#421). Reported by Marko Sarstedt.predict_pls(reps = k)repeated the cross-validation on the same folds, so every repetition gave the same predictions andrepshad no effect. Each repetition now draws new random folds, and the predictions are averaged over the repetitions. **Results change for any call withrepsgreater than 1**, includingassess_cvpat(),assess_cvpat_compare(),assess_pcm()andassess_coa()in seminrExtras, which passrepson topredict_pls().reps = NULLandreps = 1give the same results as before. A fold skipped as inadmissible (PLSc) in one repetition is averaged over the others.predict()on test data with a missing value returnedNAfor every prediction in that case's row, because the matrix products spread theNAthrough zero weights, loadings and paths (NA * 0isNA). Only the predictions that use the missing value areNAnow: a missing item makes its own construct's scoreNAand the predictions that depend on it, and leaves the others. Predictions for complete data are unchanged. PLSc models already behaved this way.estimate_pls()returned inadmissible PLSc solutions without notice. It now warns, once, with every problem found: a rho_A above one, a standardized loading above one, a disattenuated construct correlation of 1 or more in absolute value, a structural-model-implied construct correlation matrix that is not positive definite, or an R-squared outside [0, 1]. The estimates are still returned. Such solutions are common in small samples.bootstrap_model()drops resamples whose estimation fails or warns, so inadmissible PLSc resamples are now excluded from the bootstrap statistics. The number dropped is reported in the final message, stored in the model asboots_dropped(withboots_requested), and printed bysummary(). **Bootstrap standard errors, t values, p values and confidence intervals change for PLSc models with inadmissible resamples**: for a mobi model with a weak reflective block, 69 of 500 resamples were dropped, and the t value of its weakest discriminant validity estimate rose from 0.95 to 1.52.- PLSc divides by sqrt(rho_A). A rho_A of zero or below gave
NaNpaths, R-squared and loadings with only R's "NaNs produced" warnings;estimate_pls()now stops and names the constructs. A cross-validation fold or bootstrap resample with such a rho_A is skipped. construct_order()looped forever on structural models in which an outcome construct's antecedents are ordered before the rest of the model (e.g.Image -> Loyaltynext toImage -> Expectation -> Quality), sopredict(technique = predict_EA)hung on them. A structural model with a cycle now stops with an error.- The note that PLSc estimates of interaction models are subject to some bias (Becker et al., 2018) never showed, because
estimate_pls()checked a field that is never set. It now shows for PLSc models with interaction terms. estimate_pls()with ahigher_reflective()construct, which is supported byestimate_cbsem()only, failed with "undefined columns selected". It now stops and points toestimate_cbsem()orhigher_composite().PLSc()on a model with a higher-order construct, already corrected byestimate_pls(), failed with "subscript out of bounds". It now returns the model unchanged with a message.- Parallel processing failed to start ("creation of server socket failed") when its port was in use, e.g. by another R session started at the same time. It now retries on other ports.
Changed
- The package maintainer address is now
seminrgroup@gmail.com(#420). bootstrap_model()with the defaultcores = NULLstill uses every core. During R CMD check with CRAN's core limit set (_R_CHECK_LIMIT_CORES_, as with--as-cranand on CRAN), it uses at most two, so the package's tests and vignettes stay within CRAN policy. Results do not depend on the number of cores.
Performance
Substantial internal speedups across the heavy routines. **These are refactors
only — no result changes.** Verified against the PLS-SEM R book code: with seeds
pinned, 2.5.0 and 2.6.0 agree to max|diff| = 0 on every deterministic quantity
(path coefficients, loadings, weights, R², reliability, HTMT, AVE, VIF) and every
bootstrap quantity, for both cores = 1 and cores = 2.
simplePLS()skips redundant matrix work in the estimation loop.HTMT()reuses one correlation block per construct instead of recomputing.predict_pls()avoids redundant model refits and per-itemlmfits.- Cross-validation reuses the full-model reference scores.
- PLS-MGA p-values are computed without
expand.grid. bootstrap_model(cores = 1)now runs replicates in-process rather than shipping them to a one-worker PSOCK cluster, and saves and restores the caller's RNG state. Replicates continue to useset.seed(seed + i), so bootstrap results remain identical across core counts and across versions.- Added a benchmark harness covering the heavy PLS routines.
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-04
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-06-09
- ERROR12 OK, 1 ERROR2026-06-08
- NOTE12 OK, 1 NOTE2026-06-07
Show 2 earlier changesHide 2 earlier changes
- OK12 OK2026-04-25
- NOTE13 OK, 1 NOTE2026-03-10
Documentation
- Help pages
- 117 (10 internal)includes a package overview page
- Examples that run
- 98%46 run in a plain check · 1 never run
- Exported functions with examples
- 49%
- Documented parameters
- 98%
- Return-value docs
- 84%
- Help pages citing references
- 17%
Also includes 10 demo scripts (run with demo())
Downloads
Repository
- PRs merged (all time)
- 87%
- Median days to close, 50 most recently updated closed issues
- 546
- Median days to close, 50 most recently updated closed PRs
- 1
Stars over time
Forks over time
Releases over time
Issues over time
PRs over time
Commits over time
9 development-tooling and community-health practices detected across 5 families in the upstream repository
Checks run against github.com/sem-in-r/seminr on 2026-10-18.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2021-10-18
Show all practicesHide 1 practice
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-02-01Seen in the repository: CLAUDE.mdNamed in .Rbuildignore: .claude, CLAUDE.local.md, CLAUDE.mdNamed in .gitignore: .claude, CLAUDE.local.mdPull request it wrote: opened by a project memberCommits: at least 17 crediting it in the message
- Claude Opus 4.516 commits · Feb 2026
- Claude Opus 4.61 commit · Feb 2026
Dependencies
Installing seminr adds 70 packages (37 with compiled code, 55 MB of source tarballs). Heaviest: stringi 11 MB, bslib 5.6 MB, igraph 4.8 MB.
Code & Tests
Datasets
People & History
Maintainer since 0.3.1 (2018)
Pick two releases to compare their code metrics.
- 2.6.0Latest2026-10-13 · current release · diff ↗ ·
5 hours in CRAN's queue
Listed in CRAN's incoming queue from 2026-10-13 16:37 to 2026-10-13 21:40 UTC, in 2 readings. - 2.5.02026-05-25 · diff ↗ ·
removed 1 export
is_sinkadded 10 exports
all_composites, all_factors, all_non_interactions, construct_items, construct_mode, construct_name, construct_names, construct_type, is_only_endogenous, quadratic_term - RR 4.6.0 released · 2026-04-24
- 2.4.2
- 2.4.02026-01-27 · diff ↗ ·
removed 1 export
seminr_theme_oldadded 2 exports
mode_plsc, seminr_theme_academic - unarchivedReturned to CRAN2025-09-03
- 2.3.7
- archivedRemoved from CRAN2025-08-25errors were not corrected in time
- 2.3.6
- RR 4.5.0 released · 2025-04-11
- 2.3.42024-10-13 · diff ↗
- 2.3.32024-06-17 · diff ↗
- RR 4.4.0 released · 2024-04-24
- RR 4.3.0 released · 2023-04-21
- 2.3.22022-10-12 · diff ↗
- 2.3.1
Show 27 earlier eventsHide 27 earlier events
- RR 4.2.0 released · 2022-04-22
- 2.3.0
- 2.2.12021-09-02 · diff ↗
- 2.2.0
- RR 4.1.0 released · 2021-05-18
- 2.1.02021-05-13 · diff ↗ ·
removed 1 export
confidence_intervaladded 3 exports
higher_reflective, is_sink, specific_effect_significance - unarchivedReturned to CRAN2021-04-25
- 2.0.22021-04-25 · diff ↗
- archivedRemoved from CRAN2021-03-31policy violation Harassing CRAN team with HTML, repeatedly
- 2.0.12021-03-22 · diff ↗
- 2.0.02021-03-10 · diff ↗ ·
added 29 exports
browse_plot, check_test_plot, compute_itcriteria_weights, csem2seminr, dot_graph, dot_graph_htmt, edge_template_default, edge_template_minimal, get_theme_doc, last_seminr_plot, mean_replacement, node_endo_template_default, node_exo_template_default, plot_htmt, predict_DA, predict_EA, predict_pls, save_plot, seminr_theme_create, seminr_theme_dark, seminr_theme_default, seminr_theme_get, seminr_theme_old, seminr_theme_set, seminr_theme_smart, set_last_seminr_plot, slope_analysis, specify_model, total_indirect_ci - 1.1.02020-07-23 · diff ↗ ·
added 6 exports
as.reflective, associations, estimate_cbsem, estimate_cfa, estimate_lavaan_ten_berge, item_errors - 1.0.22020-05-08 · diff ↗
- RR 4.0.0 released · 2020-04-24
- 1.0.12019-12-12 · diff ↗
- 1.0.02019-09-27 · diff ↗ ·
removed 4 exports
SRMR, interaction_ortho, interaction_scaled, interactionsadded 6 exports
fSquared, higher_composite, interaction_term, orthogonal, product_indicator, two_stage - 0.7.02019-05-07 · diff ↗
- RR 3.6.0 released · 2019-04-26
- 0.6.0
- 0.5.32018-10-18 · diff ↗
- 0.5.22018-09-19 · diff ↗
- 0.5.12018-09-10 · diff ↗
- 0.5.0
- RR 3.5.0 released · 2018-04-23
- 0.4.02018-01-18 · diff ↗
- 0.3.12018-01-05
- RR 3.4.0 released · 2017-04-21
Package metadata
- First published
- 2018-01-05
- Total releases
- 29 / 8 yrs
- License
- GPL-3 OSI
- Minimum R
- ≥ 4.1.0
- Bundled data
- 21 KB / 4 files
- Download size
- 1.7 MB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("seminr")This package has no citation file, so citation() returns this entry built from DESCRIPTION.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-22, which the citation names so these numbers can be found later. More on citing and the projects behind them.