Metadata-Version: 2.4
Name: seminr
Version: 0.1.0
Summary: Python port of the seminr R package: PLS-SEM and covariance-based SEM (CBSEM/CFA)
Project-URL: Homepage, https://github.com/sem-in-r/seminr-py
Project-URL: Repository, https://github.com/sem-in-r/seminr-py
Project-URL: Issues, https://github.com/sem-in-r/seminr-py/issues
Author: Soumya Ray
License-Expression: GPL-3.0-only
License-File: LICENSE
Keywords: bootstrap,cbsem,cfa,factor-analysis,lavaan,partial-least-squares,pls-sem,sem,seminr,statistics,structural-equation-modeling
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == 'pandas'
Description-Content-Type: text/markdown

# seminr (Python)

A Python port of the [seminr](https://github.com/sem-in-r/seminr) R package for
structural equation modeling: **PLS-SEM** (partial least squares) and
**CBSEM/CFA** (covariance-based SEM / confirmatory factor analysis).

The goal is numerical parity with seminr on the bundled `mobi` dataset —
1e-5 against R-generated golden fixtures for PLS quantities, and tiered
tolerances for CBSEM (which reimplements lavaan's ML/MLR estimator from
scratch, matching lavaan 0.6-21). The public API mirrors seminr's R names.

Built one vertical slice at a time; the port is now functionally complete.

## Installation

```bash
pip install seminr          # or: uv add seminr
pip install "seminr[pandas]"  # optional: pandas DataFrame input + .to_dataframe()
```

Requires Python 3.11+. Runtime dependencies are NumPy and SciPy only.

## Status

**Functionally complete.** The full non-plotting seminr surface is implemented
and matches seminr on the bundled `mobi` dataset (1e-5 for PLS quantities;
tiered tolerances for CBSEM):

- **Specification DSL** — constructs, composite/reflective/higher-order
  composites, all three interaction methods (product-indicator, orthogonal,
  two-stage) and quadratic terms, item-error associations, `as_reflective` /
  `higher_reflective`, and `csem2seminr` / `lavaan2seminr` syntax import.
- **PLS-SEM** — `estimate_pls` (PLSc for reflective constructs, two-stage
  higher-order constructs, interactions), `bootstrap_model` (with t-values,
  percentile CIs, mediation helpers), and `rerun`.
- **Assessment** — `summarize()` for every model kind, reliability
  (alpha / rhoA / rhoC / AVE), validity (HTMT, Fornell-Larcker, cross-loadings,
  VIFs), f², AIC/BIC, and descriptives.
- **PLSpredict** (`predict_pls`, direct `predict`) and **PLS-MGA**
  (`estimate_pls_mga`).
- **CBSEM / CFA** — `estimate_cbsem` / `estimate_cfa`, a from-scratch ML/MLR
  estimator matching `lavaan::sem/cfa(std.lv=TRUE)`: LISREL model, analytic
  gradient, ~21 fit measures, Huber-White robust SEs, Yuan-Bentler-Mplus scaled
  test, and ten Berge factor scores.

Bootstrap, PLSpredict, and MGA accept an opt-in `cores=` argument for
multiprocess parallelism. Out of scope: the plotting/presentation layer. See
[`.claude/plans/PLAN.port-seminr.md`](.claude/plans/PLAN.port-seminr.md) for the
full slice-by-slice history and [`.claude/FUTURE.md`](.claude/FUTURE.md) for
deferred items.

## Usage

```python
from seminr import (
    constructs, composite, multi_items,
    relationships, paths, interaction_term,
    estimate_pls, bootstrap_model,
)

# The ECSI mobi dataset ships with the package:
from seminr.datasets import load_mobi
mobi = load_mobi()
# ...or bring your own as a pandas DataFrame or a (column_names, 2-D array) pair.

measurement_model = constructs(
    composite("Image", multi_items("IMAG", [1, 2, 3, 4, 5])),
    composite("Expectation", multi_items("CUEX", [1, 2, 3])),
    composite("Satisfaction", multi_items("CUSA", [1, 2, 3])),
    interaction_term("Image", "Expectation"),  # a product-indicator moderation
)

structural_model = relationships(
    paths(["Image", "Expectation", "Image*Expectation"], "Satisfaction"),
)

model = estimate_pls(mobi, measurement_model, structural_model)
image_path = model.path_coef.get("Image", "Satisfaction")   # path coefficient
r2 = model.r_squared.get("Rsq", "Satisfaction")             # structural R-squared

# Bootstrap for standard errors and t-values (see the reproducibility note below).
boot = bootstrap_model(model, nboot=200, seed=123)
boot_sd = boot.paths_descriptives.get("Image", "Satisfaction Boot SD")
t_value = image_path / boot_sd
```

Result matrices are `NamedMatrix` values: index them by row/column name with
`.get(row, col)`, or reach the underlying float64 array via `.values`.

## Attribution

- **[seminr](https://github.com/sem-in-r/seminr)** (R, GPL-3.0) by Soumya Ray,
  Nicholas Danks, and contributors — the authoritative reference for behavior
  and the source of every golden fixture in this repository.
- **[seminr-ts](https://github.com/sem-in-r/seminr-ts)** (TypeScript, GPL-3.0) — a
  completed, fixture-verified port of the same package. Its module layout,
  algorithm digests, and fixtures are reused directly here.

## Bootstrap reproducibility

`bootstrap_model(model, nboot=..., seed=...)` uses a default resampler backed by
NumPy's `Generator(PCG64(seed))`. It is deterministic within Python for a given
`seed`, but it is **not** identical to seminr's R Mersenne-Twister resampling, so
its replication indices — and therefore the resulting bootstrap descriptives —
differ from R even at the same nominal seed. For exact parity with a specific R
run, inject the resample indices directly: `bootstrap_model(model, nboot=n,
indices=matrix)`, where `matrix` is an `nboot x n` array of 0-based row indices
(the parity tests feed R's exported index matrix this way). A custom `resampler`
can also be supplied. This mirrors the seminr-ts port's decision (its plan Q3/Q5).

## Performance

The numerical kernel is backed by NumPy/SciPy (BLAS), so estimation, bootstrap,
PLSpredict, and MGA run faster than seminr's R implementation on the `mobi`
benchmarks — including seminr's own optimized branch — with no hand-tuned
Python. `bootstrap_model`, `predict_pls`, and `estimate_pls_mga` accept an
opt-in `cores=N` argument that fans replications/folds/groups out across
processes; results are bit-identical to the sequential path. Note that on small
models (like `mobi`, where a single estimation is sub-millisecond) the process
pool's start-up and pickling overhead can make `cores=` *slower* than the
default sequential path — reach for it when `nboot`, the sample size, or the
per-replication cost is large enough to amortize that overhead. The measurement
harness lives in [`benchmark/`](benchmark/).

## License

[GPL-3.0-only](LICENSE), as a derivative work of the GPL-3 seminr package.
