Documentation
NAVAL-SEM v0.9
Open-source structural equation modelling desktop application. PLS-SEM and CB-SEM
in a self-contained GUI — no R, no SPSS licence required.
v0.9.0
FastAPI · Python
semopy · NumPy · pandas
CC BY-NC-ND 4.0
Windows · macOS · Linux
- Content Validity Index (CVI) — item-level I-CVI, S-CVI/Ave, S-CVI/UA, and modified kappa (κ*) for expert-panel scale validation (
POST /cvi)
- Exploratory Factor Analysis (EFA) — KMO, Bartlett's sphericity, Kaiser criterion, varimax/other rotations, cross-loading and weak-loading flags (
POST /efa)
- Nomological Validity — per-construct R² checked against Hair et al. (2019) benchmarks, Substantial / Moderate / Weak / Not-supported interpretation (
POST /nomological)
- Measurement Invariance — configural → metric → scalar sequence, ΔCFI criterion (Cheung & Rensvold 2002), partial-invariance release list (
POST /invariance)
- NCA-ESSE — NCA Effect Size Sensitivity Extension (Becker et al. 2026, JBR): ECDF threshold sweep 0–5%, joint-uniform benchmark, permutation tests, recommended threshold (
POST /nca-esse) — unscheduled addition
Quick Start
1 · Launch the server
Shell
# From the extracted release folder
uvicorn app.main:app --reload --port 8000
# Or use the bundled launcher
python launcher.py
2 · POST your first model
All analysis endpoints accept multipart/form-data with a file upload (CSV / XLSX / SAV) and a model string in lavaan syntax.
cURL — minimal PLS-SEM run
curl -X POST http://localhost:8000/run \
-F "file=@data.csv" \
-F "model=Y =~ y1 + y2 + y3
X =~ x1 + x2 + x3
Y ~ X" \
-F "algorithm=pls" \
-F "bootstrap_n=500"
3 · Stream live logs (SSE)
JavaScript
const runId = crypto.randomUUID();
const es = new EventSource(`/logs/${runId}`);
es.onmessage = (e) => {
const d = JSON.parse(e.data);
if (d.done) { es.close(); return; }
console.log(d.level, d.msg);
};
// Then POST /run with run_id in the form body
formData.append("run_id", runId);
Algorithms
All analysis endpoints that accept an algorithm parameter support three estimators:
pls
Partial Least Squares SEM
- Default for all endpoints
- Small-to-medium samples
- Non-normal data
- Exploratory research
- Composite-based fit
cb
Covariance-Based SEM
- Theory-confirming
- Normal distribution assumed
- Full CFI / TLI / RMSEA
- n ≥ 200 recommended
- ML estimator via semopy
wls
Weighted Least Squares
- Ordinal / Likert data
- Robust to non-normality
- DWLS / WLSMV variant
- Categorical indicators
lavaan Syntax
NAVAL-SEM parses a lavaan-compatible model string. Three operator types are supported:
| Operator | Meaning | Example |
=~ | Measurement (reflective loading) | EXO =~ x1 + x2 + x3 |
~ | Structural regression path | ENO ~ EXO + MOD |
~~ | Residual covariance / variance | y2 ~~ y4 |
Interaction terms
Use the * operator inside a structural path to specify moderation. The engine auto-creates the mean-centred product column.
Moderation syntax
# Standard moderation: X*M on outcome Y
Y ~ X + M + X*M
X =~ x1 + x2 + x3
M =~ m1 + m2 + m3
Y =~ y1 + y2 + y3
Auto reverse scoring
Prefix any indicator name with r (e.g. rx3) when the column x3 exists in the dataset but rx3 does not. The server applies (max + min) − value automatically before fitting.
Reverse-scored indicator
SatisfactionLV =~ sat1 + sat2 + rsat3 + sat4
# rsat3 will be auto-reverse-scored from sat3
⚠
Multi-target residual covariance shorthand (y2 ~~ y4 + y6) is automatically expanded to individual pairs by the engine. However, write them individually for clarity in production models.
Core API
POST /run
Fit a full SEM model. Returns path coefficients, loadings, fit indices, validity metrics, and optionally bootstrap CIs. Generates a SHA-256 reproducibility fingerprint.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV, XLSX, or SAV dataset |
| modelrequired | string | — | lavaan syntax string |
| algorithmoptional | string | pls | pls | cb | wls |
| bootstrap_noptional | int | 0 | Bootstrap resamples (0 = no bootstrap, max 20 000) |
| missingoptional | string | listwise | listwise | mean |
| run_idoptional | string | auto UUID | Client-supplied ID for SSE log streaming |
Returns → ModelResult
Key fields in the response JSON:
| Field | Type | Notes |
| algorithm | string | Estimator used |
| n_obs | int | Rows used after missing-data handling |
| converged | bool | Optimisation converged |
| parameters | PathParameter[] | All =~, ~, ~~ rows with estimates, SE, z, p, CI |
| fit | FitIndices | CFI, TLI, RMSEA, SRMR, χ², AIC, BIC, AVE, CR, α |
| vif | VIFEntry[] | null | Collinearity — VIF < 5 acceptable, < 3.3 strict |
| f2 | F2Entry[] | null | Cohen's f² for each predictor |
| indirect | IndirectResult | null | Populated when bootstrap_n > 0 |
| outer_weights | OuterWeightEntry[] | null | Bootstrap outer weights |
| fingerprint | string | null | SHA-256 reproducibility hash |
| summary | ModelSummary | null | High-level digest for UI panels (v0.7) |
| warnings | string[] | Non-fatal notices |
POST /bootstrap
Run standalone bootstrap without re-fitting the full model. Useful for extending an existing /run result with CIs post-hoc.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV, XLSX, or SAV |
| modelrequired | string | — | lavaan syntax |
| bootstrap_noptional | int | 500 | Resamples (max 20 000) |
| algorithmoptional | string | pls | Estimator |
| missingoptional | string | listwise | Missing-data strategy |
POST /indirect
Decompose indirect (mediation) effects for all variable pairs connected via paths of length ≥ 2. Returns bootstrap 95 % CIs and a total-effects matrix.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset |
| modelrequired | string | — | lavaan syntax |
| bootstrap_noptional | int | 500 | Bootstrap resamples for CIs |
| missingoptional | string | listwise | |
Returns → IndirectResult
| Field | Type | Notes |
| effects | IndirectEffect[] | One row per from→through→to chain with bootstrapped SE, CI, significance |
| total_effects | Dict[str, Dict[str, float]] | Matrix: total_effects[from_var][to_var] |
POST /predict
Predictive relevance suite: Stone-Geisser Q² (blindfolding), PLSpredict k-fold vs linear baseline, and CVPAT.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset |
| modelrequired | string | — | lavaan syntax |
| omission_distanceoptional | int | 7 | Blindfolding omission distance D |
| k_foldsoptional | int | 10 | Cross-validation folds for PLSpredict |
| missingoptional | string | listwise | |
POST /cmb
Common Method Bias marker variable analysis (Lindell & Whitney 2001). Requires a marker variable theoretically unrelated to your constructs.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset |
| modelrequired | string | — | lavaan syntax |
| marker_variablerequired | string | — | Column name of the marker variable |
| missingoptional | string | listwise | |
POST /mga
Multi-Group Analysis: per-group model fit, pairwise bootstrap path-difference CIs, and (for 2-group analyses) MICOM measurement invariance.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset (must contain grouping column) |
| modelrequired | string | — | lavaan syntax |
| grouping_variablerequired | string | — | Column name for group splits |
| algorithmoptional | string | pls | |
| bootstrap_noptional | int | 500 | Resamples for path-difference CIs |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | SSE tracking ID |
v0.7
POST /moderation
Moderation analysis via the product-of-composites approach.
Detects X*M interaction terms in the lavaan structural syntax and tests each one
by comparing R² with vs. without the interaction term (Δ R², Cohen's f²)
and computing bootstrap simple slopes at moderator = −1 SD, mean (0), +1 SD.
✦
Manifest-variable fallback: when the model contains no =~ measurement lines, NAVAL-SEM automatically switches to an OLS path for observed-variable moderation (no latent composites needed).
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset (CSV, XLSX, SAV) |
| modelrequired | string | — | lavaan syntax; must include at least one X*M term |
| algorithmoptional | string | pls | pls | cb | wls |
| bootstrap_noptional | int | 500 | Bootstrap resamples for β_interaction CIs and simple slopes |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | SSE tracking ID |
Returns → ModerationResult
| Field | Type | Notes |
| moderation_terms | ModerationTerm[] | One entry per detected X×M interaction |
| parameters | PathParameter[] | Full model parameters including interaction term |
| fit | FitIndices | Fit of the full (interaction-included) model |
| n_obs | int | Effective sample size |
| bootstrap_n | int | Actual resamples used |
| warnings | string[] | |
ModerationTerm fields
| Field | Type | Notes |
| iv / moderator / outcome | string | Variable names |
| interaction_col | string | Auto-created product column name (e.g. X_x_M) |
| beta_iv / beta_moderator / beta_interaction | float | Standardised path coefficients |
| ci_lower_95 / ci_upper_95 | float | null | Bootstrap percentile CI for β_interaction |
| significant | bool | True when CI excludes 0 |
| r2_with / r2_without / delta_r2 | float | R² comparison |
| f2_interaction | float | Cohen's f² = Δ R² / (1 − R²_with) |
| simple_slopes | SimpleSlope[] | At W = −1 SD, 0, +1 SD |
Example syntax
# Test whether trust (TR) moderates innovation (IN) → satisfaction (SA)
SA ~ IN + TR + IN*TR
IN =~ in1 + in2 + in3
TR =~ tr1 + tr2 + tr3
SA =~ sa1 + sa2 + sa3
POST /ipma
Importance-Performance Map Analysis (Ringle & Sarstedt 2016; Hair et al. 2022 Ch. 7).
Computes for each predictor of a target latent variable:
Importance = total effect (direct + all indirect paths) and
Performance = mean composite score rescaled to 0–100.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset |
| modelrequired | string | — | lavaan syntax |
| target_lvrequired | string | — | Name of the outcome LV for which importance is computed |
| algorithmoptional | string | pls | |
| scale_minoptional | float | observed min | Theoretical scale minimum (e.g. 1 for Likert 1–5) |
| scale_maxoptional | float | observed max | Theoretical scale maximum (e.g. 5 for Likert 1–5) |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → IPMAResult
| Field | Type | Notes |
| target_lv | string | The outcome LV specified |
| entries | IPMAEntry[] | Sorted by importance descending |
| scale_min / scale_max | float | Scale range actually used for 0–100 rescaling |
| algorithm | string | |
| warnings | string[] | Emitted when scale range is inferred from data |
IPMAEntry fields
| Field | Type | Notes |
| lv | string | Predictor construct name |
| importance | float | Total effect on target_lv |
| performance | float | 0–100 rescaled composite mean (clamped) |
ℹ
Pass scale_min and scale_max matching your survey instrument (e.g. 1 and 5 for a five-point Likert scale) for correct 0–100 performance rescaling. When omitted, NAVAL-SEM uses the observed data range and appends a warning.
POST /nca
Necessary Condition Analysis (Dul 2016, 2020).
Tests whether each predictor is a necessary (not merely sufficient) condition for an outcome.
Computes CE-FDH (staircase ceiling) and CR-FDH (regression ceiling) effect sizes
plus permutation-based significance.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset |
| modelrequired | string | — | lavaan syntax; all structural IV→DV pairs are tested |
| n_permutationsoptional | int | 1000 | Permutation samples for p-value (max 20 000) |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → NCAResult
| Field | Type | Notes |
| entries | NCAEntry[] | One per unique IV→DV pair |
| n_permutations | int | |
| warnings | string[] | Pairs skipped due to insufficient n |
NCAEntry fields
| Field | Type | Notes |
| iv / dv / n_obs | string / int | |
| ce_fdh_d / ce_fdh_label / ce_fdh_p | float / string / float | Staircase ceiling effect size and permutation p |
| cr_fdh_d / cr_fdh_label | float / string | Regression ceiling effect size |
| cr_fdh_slope / cr_fdh_intercept / cr_fdh_p | float | Regression ceiling line parameters |
| significant | bool | True when max(ce_p, cr_p) < 0.05 |
| scatter_x / scatter_y | float[] | Sampled scatter plot coordinates (≤ 200 pts) |
| ceiling_x / ceiling_y | float[] | CE-FDH staircase coordinates (≤ 100 pts) |
NCA Effect Size Benchmarks (Dul 2016)
| d | Label |
| < 0.10 | negligible |
| 0.10 – 0.29 | small |
| 0.30 – 0.49 | medium |
| ≥ 0.50 | large |
Utilities
GET /logs/{run_id}
Server-Sent Events stream for a running analysis. Open immediately after submitting any endpoint that returns a run_id. The stream emits JSON objects and terminates with a done packet.
Stream events
| Event type | Fields | Notes |
| log entry | t, level, msg | level = step | info | ok | warn | error |
| done | done: true, fingerprint, audit | Stream closes after this |
Stream auto-closes after 4 hours; results continue in background.
GET /fingerprint/{run_id}
Retrieve the full SHA-256 audit record for a completed run. The fingerprint covers model syntax hash, data hash (SHA-256 of sorted column hashes), algorithm, environment versions, and key fit metrics.
Returns 404 if run_id is unknown; 425 if the run is still in progress.
POST /upload/preview
Parse an uploaded file and return column names, row count, first 5 rows, and dtype map — without running any analysis. Useful for validating uploads before model submission.
| Returns | Type | Notes |
| columns | string[] | All column names |
| n_rows | int | Total row count |
| preview | object[] | First 5 rows as records |
| dtypes | Dict[str, str] | pandas dtype per column |
GET /health
{ "status": "ok", "version": "0.7.1" }
Reference
Schemas
All response models are Pydantic v2 classes. Key schemas:
PathParameter
One row per model parameter (measurement loading, structural path, or covariance). std_error, z_value, and p_value are null for ~~ rows.
PathParameter
lhs str
op str =~ | ~ | ~~
rhs str
estimate float
std_estimate float?
std_error float?
z_value float?
p_value float?
ci_lower / ci_upper float?
significant bool
FitIndices
cfi / tli float?
rmsea float?
rmsea_ci_lower/upper float?
srmr float?
chi_square / df / p_value float?
aic / bic float?
r_squared Dict?
ave / composite_reliability Dict?
cronbach_alpha Dict?
fornell_larcker Dict?
cfi_good / rmsea_good / srmr_good bool?
IndirectEffect
from_var / to_var str
through str[] mediator chain
indirect_effect float
bs_se float?
ci_lower_95 / ci_upper_95 float?
significant bool?
F2Entry
lhs / rhs str
r2_full / r2_reduced float
f2 float ΔR²/(1−R²_full)
effect str negligible–large
Q2Entry (v0.5)
lv str endogenous LV
q2 float 1 − SSE/SSO
sse / sso float
omission_distance int
predictive_relevance str
HTMTEntry
construct_a / b str
htmt float
acceptable bool HTMT < 0.90
v0.9 Schemas
CVIResult
item_cvi Dict[str, float] I-CVI per item
s_cvi_ave float mean I-CVI
s_cvi_ua float universal agreement
kappa_star float modified κ*
n_experts / n_items int
interpretation str Excellent/Acceptable/Poor
ScaleDevelopmentResult
method str e.g. PCA_Varimax
n_factors int
kmo / bartlett_chi2 / bartlett_p float
eigenvalues float[]
variance_explained float[] per factor
cumulative_variance float
loadings Dict[] {item, factor, loading}
cross_loadings Dict[]? secondary |λ| > 0.30
warnings str[]
NomologicalResult
construct_name str (JSON key: "construct")
r_squared float observed R²
benchmark float minimum R² threshold
passed bool R² ≥ benchmark
interpretation str Substantial/Moderate/Weak/Not supported
MeasurementInvarianceResult
group_col / groups str / str[]
configural MeasurementInvarianceLevel
metric MeasurementInvarianceLevel
scalar MeasurementInvarianceLevel
partial_invariance str[]? released items
conclusion str Full scalar / Partial scalar / Metric only / Configural only
NCAESSEResult
entries NCAESSEEntry[]
threshold_range float[]
benchmark str "joint_uniform"
n_permutations / n_benchmark_reps int
warnings str[]
NCAESSEEntry
iv / dv / n_obs str / int
thresholds NCAESSEThresholdPoint[]
recommended_threshold float?
recommended_effect_size float?
recommended_label str? negligible–large
ceiling_x / ceiling_y float[] staircase at recommended threshold
warnings str[]
NCAESSEThresholdPoint
threshold / pct_excluded float
empirical_d float CE-FDH at this step
theoretical_d float uniform benchmark
delta_empirical / delta_theoretical float?
delta_diff float? >0 = genuine signal
p_value float? permutation p
significant bool p < 0.05
MeasurementInvarianceLevel
model str configural | metric | scalar
cfi / rmsea / srmr float?
delta_cfi / delta_rmsea float? vs. preceding model
passed bool ΔCFI > −0.010
Fit Thresholds
Verdict flags set automatically by the engine:
| Index | Acceptable | Good | Field |
| CFI | ≥ 0.90 | ≥ 0.95 | cfi_acceptable / cfi_good |
| TLI (NNFI) | ≥ 0.90 | ≥ 0.95 | tli_acceptable / tli_good |
| RMSEA | ≤ 0.08 | ≤ 0.06 | rmsea_acceptable / rmsea_good |
| SRMR | — | ≤ 0.08 | srmr_good |
| AVE | ≥ 0.50 | — | per-LV in fit.ave |
| Composite Reliability | ≥ 0.70 | ≥ 0.80 | fit.composite_reliability |
| Cronbach's α | ≥ 0.70 | — | fit.cronbach_alpha |
| VIF | < 5.0 | < 3.3 (PLS strict) | vif[].acceptable |
| HTMT | < 0.90 | < 0.85 | htmt[].acceptable |
Effect Sizes
Cohen's f² (structural paths)
| f² | Label |
| < 0.02 | negligible |
| 0.02 – 0.14 | small |
| 0.15 – 0.34 | medium |
| ≥ 0.35 | large |
Stone-Geisser Q² (predictive relevance)
| Q² | Label |
| ≤ 0 | none |
| 0 – 0.25 | small |
| 0.25 – 0.50 | medium |
| ≥ 0.50 | large |
NCA Effect Size d (Dul 2016)
| d | Label |
| < 0.10 | negligible |
| 0.10 – 0.29 | small |
| 0.30 – 0.49 | medium |
| ≥ 0.50 | large |
v0.8
POST /fimix
FIMIX-PLS (Finite Mixture PLS) detects unobserved heterogeneity by running an
Expectation-Maximisation algorithm over PLS-SEM path weights to identify latent segments
with distinct structural coefficient profiles. Reference: Hahn et al. 2002; Ringle et al. 2010.
✦
Segment selection: run with k = 2 to 5 and compare AIC3 / CAIC / relative entropy (EN ≥ 0.5 indicates acceptable separation).
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset (CSV, XLSX, SAV) |
| modelrequired | string | — | lavaan syntax (PLS-SEM) |
| n_segmentsrequired | int | — | Number of segments k (2–5) |
| max_iteroptional | int | 100 | EM iterations before convergence |
| seedoptional | int | 42 | Random seed for reproducibility |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → FIMIXResult
| Field | Type | Notes |
| n_segments | int | k as requested |
| segments | FIMIXSegment[] | One entry per segment |
| aic / bic / aic3 / caic | float | Information criteria for model selection |
| entropy | float | Relative entropy EN (0–1; ≥ 0.5 recommended) |
| n_obs | int | Effective sample size |
| warnings | string[] | |
FIMIXSegment fields
| Field | Type | Notes |
| segment_id | int | 1-indexed |
| size / size_pct | int / float | Observations assigned to this segment |
| path_coefficients | dict[str, float] | Segment-specific β values keyed by "lhs~rhs" |
| r_squared | dict[str, float] | R² per endogenous construct in this segment |
| posterior_probs | float[] | Per-observation membership probability |
v0.8
POST /pos
PLS Prediction-Oriented Segmentation (PLS-POS) assigns observations to segments
by minimising within-segment residuals iteratively — no distributional assumptions required.
Reference: Becker et al. 2013.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset (CSV, XLSX, SAV) |
| modelrequired | string | — | lavaan syntax (PLS-SEM) |
| n_segmentsrequired | int | — | Number of segments k (2–5) |
| max_iteroptional | int | 100 | |
| seedoptional | int | 42 | |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → POSResult
| Field | Type | Notes |
| n_segments | int | |
| segments | POSSegment[] | One entry per segment |
| converged | bool | Whether assignment stabilised before max_iter |
| iterations | int | Actual iterations run |
| n_obs | int | |
| warnings | string[] | |
POSSegment fields
| Field | Type | Notes |
| segment_id | int | 1-indexed |
| size / size_pct | int / float | |
| path_coefficients | dict[str, float] | Segment-specific β values |
| loadings | dict[str, float] | Outer loadings for this segment |
| r_squared | dict[str, float] | R² per endogenous construct |
| observation_ids | int[] | 0-indexed row numbers assigned to this segment |
v0.8
POST /gaussian-copula
The Gaussian Copula approach (Park & Gupta 2012; Hult et al. 2018) corrects for
endogeneity arising from non-normal, non-elliptical predictor distributions by augmenting the
structural model with copula terms derived from the empirical CDF of each non-normal predictor.
✦
Precondition: non-normality of the predictor is a necessary condition — normally distributed predictors cannot form valid copula terms. NAVAL-SEM screens automatically using Henze-Zirkler and skewness tests.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | Dataset (CSV, XLSX, SAV) |
| modelrequired | string | — | lavaan syntax |
| algorithmoptional | string | pls | |
| bootstrap_noptional | int | 500 | Bootstrap resamples for copula coefficient CIs |
| significanceoptional | float | 0.05 | α threshold for non-normality test |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → GaussianCopulaResult
| Field | Type | Notes |
| corrections | CopulaCorrection[] | One entry per endogenous path corrected |
| screened_predictors | dict[str, bool] | Non-normality test result per predictor |
| n_obs | int | |
| bootstrap_n | int | |
| warnings | string[] | |
CopulaCorrection fields
| Field | Type | Notes |
| lhs / rhs | string | Outcome and predictor construct names |
| copula_coef | float | Copula term coefficient in augmented model |
| ci_lower_95 / ci_upper_95 | float | null | Bootstrap percentile CI |
| significant | bool | True when CI excludes 0 — endogeneity confirmed |
| original_coef / corrected_coef | float | Path coefficient before and after correction |
| f2_correction | float | Cohen's f² for endogeneity increment |
v0.8
POST /export/pdf
Generate a publication-ready PDF report from any completed run.
The report covers measurement model validity, structural path estimates,
bootstrapped confidence intervals, model fit indices, and any advanced analysis
results (FIMIX, PLS-POS, Gaussian Copula, IPMA, NCA) that were included in the run.
Tables follow APA 7th edition formatting conventions.
Form Parameters
| Field | Type | Default | Description |
| run_idrequired | string | — | UUID of the completed run to export |
| include_advancedoptional | bool | true | Include FIMIX / PLS-POS / Copula / IPMA / NCA sections if present |
| paperoptional | string | A4 | A4 | Letter |
Response
| Header | Value |
| Content-Type | application/pdf |
| Content-Disposition | attachment; filename="naval-sem-{run_id}.pdf" |
Example — cURL
curl -X POST http://localhost:8000/export/pdf \
-F "run_id=abc-123" \
--output report.pdf
v0.9 — New
POST /cvi
Content Validity Index (Lynn 1986; Polit & Beck 2006).
Accepts an expert ratings matrix — rows are experts, columns are scale items, values are
1–4 relevance ratings — and returns item-level I-CVI, scale-level S-CVI/Ave and S-CVI/UA,
and modified kappa (κ*) correcting I-CVI for chance agreement.
✦
Acceptability thresholds (Lynn 1986): I-CVI ≥ 0.78 per item;
S-CVI/Ave ≥ 0.90 for excellent content validity. Use a minimum of 5 experts;
κ* ≥ 0.74 is considered excellent agreement beyond chance.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV or XLSX — rows = experts, columns = items, values = 1–4 ratings |
| n_expertsrequired | int | — | Number of expert raters (rows in the file) |
| relevant_scaleoptional | string | 3,4 | Comma-separated rating values counted as "relevant" (default top-two of a 4-pt scale) |
| missingoptional | string | listwise | |
Returns → CVIResult
| Field | Type | Notes |
| item_cvi | Dict[str, float] | I-CVI per item (proportion of experts rating it relevant) |
| s_cvi_ave | float | Mean of all I-CVI values |
| s_cvi_ua | float | Universal Agreement: proportion of items with I-CVI = 1.00 |
| kappa_star | float | Modified kappa averaged across items — adjusts for chance agreement |
| n_experts / n_items | int | |
| interpretation | string | "Excellent" (S-CVI/Ave ≥ 0.90 and all I-CVIs ≥ 0.78) · "Acceptable" (≥ 0.80) · "Poor" |
Input file format
# CSV — rows are experts (E1…E7), columns are items (Q1…Q6)
,Q1,Q2,Q3,Q4,Q5,Q6
E1 , 4, 3, 2, 4, 3, 4
E2 , 3, 4, 4, 4, 3, 3
E3 , 4, 4, 3, 3, 4, 4
# ... up to n_experts rows
cURL example
curl -X POST http://localhost:8000/cvi \
-F "file=@expert_ratings.csv" \
-F "n_experts=7"
v0.9 — New
POST /efa
Exploratory Factor Analysis for early-stage scale development.
Computes KMO adequacy and Bartlett's test of sphericity as suitability diagnostics,
applies the Kaiser criterion (eigenvalue > 1) or a user-specified factor count,
and extracts factors via sklearn's FactorAnalysis with the requested rotation.
Flags weak-loading items (|λ| < 0.40) and cross-loading items (secondary |λ| > 0.30).
ℹ
EFA is intended for early item screening and dimensionality exploration.
Confirm final factor structure with CFA via POST /run before
reporting measurement quality metrics (AVE, CR, HTMT).
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV or XLSX — respondents × items (numeric only) |
| n_factorsoptional | int | Kaiser (eigenvalue > 1) | Number of factors to extract; omit for automatic Kaiser selection |
| rotationoptional | string | varimax | Rotation method passed to sklearn FactorAnalysis (e.g. varimax, quartimax) |
| missingoptional | string | listwise | |
Returns → ScaleDevelopmentResult
| Field | Type | Notes |
| method | string | E.g. "PCA_Varimax" |
| n_factors | int | Number of factors extracted (auto or specified) |
| kmo | float | Kaiser-Meyer-Olkin adequacy statistic (≥ 0.60 acceptable, ≥ 0.80 good) |
| bartlett_chi2 / bartlett_p | float | Bartlett's test of sphericity χ² and p-value (p < 0.05 needed) |
| eigenvalues | float[] | Full eigenvalue spectrum (descending) |
| variance_explained | float[] | Proportion of variance explained per factor |
| cumulative_variance | float | Total variance explained by all retained factors |
| loadings | Dict[] | Array of {item, factor, loading} — primary assignment per item |
| cross_loadings | Dict[] | null | {item, primary_factor, secondary_factor, secondary_loading} — only items where secondary |λ| > 0.30 |
| warnings | string[] | Weak-loading items, KMO below threshold, non-significant Bartlett |
Diagnostic thresholds
| Statistic | Acceptable | Good |
| KMO | ≥ 0.60 | ≥ 0.80 |
| Bartlett p | < 0.05 | < 0.001 |
| Primary loading |λ| | ≥ 0.40 | ≥ 0.60 |
| Secondary loading |λ| | < 0.30 (no cross-loading) | — |
cURL example — 3-factor solution with varimax
curl -X POST http://localhost:8000/efa \
-F "file=@survey_items.csv" \
-F "n_factors=3" \
-F "rotation=varimax"
v0.9 — New
POST /nomological
Nomological Validity (Hair et al. 2019).
Fits the structural model and checks whether each endogenous construct's R² meets
the expected benchmark — the minimum explanatory power consistent with
the theoretical nomological network the construct is embedded in.
Returns a per-construct verdict (Substantial / Moderate / Weak / Not supported)
using Cohen's (1988) conventional R² bands.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV, XLSX, or SAV dataset |
| modelrequired | string | — | lavaan syntax |
| algorithmoptional | string | pls | pls | cb | wls |
| benchmarksoptional | JSON string | {"*": 0.10} | Per-construct minimum R² map; use "*" as wildcard default |
| missingoptional | string | listwise | |
Returns → NomologicalResult[] (one per endogenous construct)
| Field | Type | Notes |
| construct_name | string | Endogenous construct name (JSON key: "construct") |
| r_squared | float | Observed R² from model fit |
| benchmark | float | Minimum R² threshold used (from benchmarks map) |
| passed | bool | True when R² ≥ benchmark |
| interpretation | string | "Substantial" (R² ≥ 0.26) · "Moderate" (≥ 0.13) · "Weak" (≥ benchmark) · "Not supported" |
R² Interpretation Bands (Hair et al. 2019)
| R² | Label |
| ≥ 0.26 | Substantial |
| 0.13 – 0.25 | Moderate |
| ≥ benchmark (default 0.10) | Weak |
| < benchmark | Not supported |
Example — custom benchmarks per construct
curl -X POST http://localhost:8000/nomological \
-F "file=@data.csv" \
-F "model=Y =~ y1+y2+y3\nX =~ x1+x2+x3\nY ~ X" \
-F 'benchmarks={"Y":0.15,"*":0.10}'
v0.9 — New
POST /invariance
Measurement Invariance testing via a configural → metric → scalar sequence
(Vandenberg & Lance 2000; Cheung & Rensvold 2002).
Tests whether the same measurement model holds equivalently across groups, a prerequisite
for meaningful multi-group structural comparisons.
Uses ΔCFI ≤ −0.010 as the invariance failure criterion (robust to sample size).
Returns a plain-English conclusion and, when scalar fails, a partial-invariance
item release list.
⚠
Prerequisite for MGA: at minimum, metric invariance (ΔCFI > −0.010 from configural to metric) must hold before interpreting path-coefficient differences from POST /mga. Full scalar invariance is required before comparing latent means.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV, XLSX, or SAV — must contain the grouping column |
| modelrequired | string | — | lavaan measurement syntax (=~ blocks) |
| group_colrequired | string | — | Column name whose values define groups (≥ 2 distinct values) |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | SSE tracking ID |
Returns → MeasurementInvarianceResult
| Field | Type | Notes |
| group_col | string | Grouping variable name |
| groups | string[] | Sorted distinct group labels |
| configural | MeasurementInvarianceLevel | Baseline — free parameters per group; always passes by definition |
| metric | MeasurementInvarianceLevel | Loadings constrained equal; ΔCFI vs configural |
| scalar | MeasurementInvarianceLevel | Loadings + intercepts constrained; ΔCFI vs metric |
| partial_invariance | string[] | null | Items released when scalar partially holds |
| conclusion | string | "Full scalar" · "Partial scalar" · "Metric only" · "Configural only" |
MeasurementInvarianceLevel fields
| Field | Type | Notes |
| model | string | "configural" | "metric" | "scalar" |
| cfi / rmsea / srmr | float | null | Fit indices for this constrained model |
| delta_cfi / delta_rmsea | float | null | Change vs. preceding model in the sequence |
| passed | bool | True when ΔCFI > −0.010 |
ℹ
The engine fits each constrained model on the pooled stacked dataset (all groups combined). Per-group fit in the configural step is averaged using harmonic-mean weighting by group size.
Example
curl -X POST http://localhost:8000/invariance \
-F "file=@data.csv" \
-F "model=X =~ x1+x2+x3\nY =~ y1+y2+y3" \
-F "group_col=gender"
v0.9 — New · Unscheduled
POST /nca-esse
NCA with Effect Size Sensitivity Extension
(Becker, Richter, Ringle & Sarstedt 2026, Journal of Business Research, 206, 115920
— CC BY 4.0).
Extends deterministic NCA (Dul 2016) by sweeping an ECDF removal threshold
(default 0–5% in 0.5% steps) and recomputing CE-FDH ceiling lines at each step
after discarding the most extreme ceiling-violating observations.
Benchmarks the empirical sensitivity curve against a joint-uniform random
distribution (no necessity by construction) and permutation-tests the effect
size at every threshold, to distinguish genuine necessity from artefactual
ceiling movement.
✦
When to use: run NCA-ESSE after a standard POST /nca when
you suspect a small share of atypical response combinations may be collapsing
a true necessity effect to zero. The recommended threshold tells you the smallest
exclusion level at which the empirical effect consistently beats the no-necessity benchmark.
Form Parameters
| Field | Type | Default | Description |
| filerequired | File | — | CSV, XLSX, or SAV dataset |
| modelrequired | string | — | lavaan syntax; all structural IV→DV pairs are tested |
| thresholdsoptional | JSON float[] | [0.0, 0.005, … 0.05] | ECDF removal thresholds to sweep (0–1 fractions) |
| n_permutationsoptional | int | 200 | Permutation samples per threshold per pair |
| n_benchmark_repsoptional | int | 200 | Joint-uniform benchmark replications per pair |
| seedoptional | int | 42 | RNG seed |
| missingoptional | string | listwise | |
| run_idoptional | string | auto UUID | |
Returns → NCAESSEResult
| Field | Type | Notes |
| entries | NCAESSEEntry[] | One per unique IV→DV structural pair (requires ≥ 30 valid observations) |
| threshold_range | float[] | The threshold sweep list used |
| benchmark | string | "joint_uniform" — theoretical no-necessity distribution |
| n_permutations / n_benchmark_reps | int | |
| warnings | string[] | Pairs skipped (insufficient n, data errors) |
NCAESSEEntry fields
| Field | Type | Notes |
| iv / dv / n_obs | string / int | |
| thresholds | NCAESSEThresholdPoint[] | One point per threshold step |
| recommended_threshold | float | null | Largest threshold in a contiguous benchmark-beating gain run from the first nonzero step |
| recommended_effect_size | float | null | CE-FDH d at the recommended threshold |
| recommended_label | string | null | negligible / small / medium / large (Dul 2016) |
| ceiling_x / ceiling_y | float[] | CE-FDH staircase at the recommended threshold (≤ 100 pts) |
| warnings | string[] | Per-pair notices |
NCAESSEThresholdPoint fields
| Field | Type | Notes |
| threshold | float | ECDF removal fraction (e.g. 0.0, 0.005, … 0.05) |
| pct_excluded | float | Actual fraction removed (may differ from threshold on discrete data) |
| empirical_d | float | CE-FDH effect size after removal |
| theoretical_d | float | Mean CE-FDH on joint-uniform benchmark at same threshold |
| delta_empirical / delta_theoretical | float | null | Gain vs. previous threshold step |
| delta_diff | float | null | delta_empirical − delta_theoretical; > 0 means genuine necessity signal |
| p_value | float | null | Permutation p-value (proportion of shuffled-Y runs ≥ empirical_d) |
| significant | bool | True when p < 0.05 |
⚠
A minimum of 30 valid observations per IV→DV pair is required for a
meaningful threshold sweep. Pairs below this threshold are skipped and reported in
warnings. Pairs with ties at identical (x, y) coordinates are handled by
removing the full tied cell at each threshold step.
cURL example
curl -X POST http://localhost:8000/nca-esse \
-F "file=@data.csv" \
-F "model=Y =~ y1+y2+y3\nX =~ x1+x2+x3\nY ~ X" \
-F "n_permutations=500" \
-F "n_benchmark_reps=500"
Reference
Becker, J.-M., Richter, N. F., Ringle, C. M., & Sarstedt, M. (2026).
Must-have, or maybe not? A sensitivity-based extension to necessary
condition analysis. Journal of Business Research, 206, 115920.
https://doi.org/10.1016/j.jbusres.2025.115920 (CC BY 4.0)
Changelog
v0.9
- Content Validity Index: I-CVI per item, S-CVI/Ave, S-CVI/UA, modified kappa (κ*) —
POST /cvi (Lynn 1986; Polit & Beck 2006)
- Exploratory Factor Analysis: KMO, Bartlett's sphericity, Kaiser criterion, configurable rotation, weak- and cross-loading detection —
POST /efa
- Nomological Validity: per-construct R² vs Hair et al. (2019) benchmarks, Substantial/Moderate/Weak/Not-supported verdicts —
POST /nomological
- Measurement Invariance: configural → metric → scalar sequence, ΔCFI ≤ −0.010 criterion, partial-invariance item release —
POST /invariance
- NCA-ESSE (unscheduled): ECDF threshold sweep 0–5%, joint-uniform benchmark, permutation significance, recommended threshold and ceiling line —
POST /nca-esse (Becker et al. 2026, JBR)
- pytest suite: 47 unit tests across all analysis engines (
pytest tests/)
v0.8
- FIMIX-PLS: EM-based finite mixture segmentation, k=2–5, AIC/BIC/AIC3/CAIC/EN, segment-specific path coefficients and R²
- PLS-POS: prediction-oriented segmentation via iterative residual-based assignment
- Gaussian Copula: endogeneity correction for non-normal predictors (Park & Gupta 2012), bootstrap CIs, f² effect size
- PDF Report Export: APA-style publication-ready PDF via
POST /export/pdf
- Centralised versioning:
app/version.py single source of truth, CI-injected from git tag
- CI/CD: sync-to-master step removed from release workflow; all platform builds clean
v0.7
- Moderation: product-of-composites, OLS fallback for manifest models, bootstrap simple slopes at −1 SD / 0 / +1 SD, Δ R², Cohen's f²
- IPMA: total-effect importance, 0–100 rescaled performance, configurable theoretical scale range
- NCA: CE-FDH staircase ceiling, CR-FDH regression ceiling, permutation p-values, scatter / ceiling coordinates for frontend rendering
- Moderated Mediation (Hayes Models 7, 14, 58/59): IMM with bootstrap CI, conditional indirect effects at 3 moderator levels
- ModelSummary digest in
/run response: structural path table, construct validity table, verdicts
- Auto reverse-scoring via
r-prefix convention (auto_reverse_score)
- Residual covariance expansion fix (TC-52): multi-target
~~ shorthand correctly split into individual pairs
- Reproducibility fingerprint (SHA-256) in all
/run responses and accessible via GET /fingerprint/{run_id}
v0.6
- Higher-Order Constructs: repeated-indicator and two-stage approaches
- GitHub Actions CI/CD: version injected from git tags
- Full-width results drawer with live SSE logging
- MGA and MICOM tabs wired in UI
- SourceForge distribution connector via native GitHub integration
v0.5
- Predictive relevance suite: Q², PLSpredict, CVPAT (
POST /predict)
- Common Method Bias marker variable analysis (
POST /cmb)
- Outer weights bootstrap with t-statistics
v0.4
- Multi-Group Analysis with MICOM (
POST /mga)
- Indirect effects decomposition (
POST /indirect)
- VIF collinearity table, Cohen's f² for all structural paths
v0.3
- HTMT discriminant validity (
POST /htmt)
- AVE, composite reliability, Cronbach's α in FitIndices
- Fornell-Larcker criterion matrix
≤ v0.2
- Initial public release: PLS-SEM / CB-SEM / WLS via semopy backend
- Bootstrap standalone (
POST /bootstrap)
- CSV / XLSX / SAV parser, SSE log stream,
/health endpoint
- PyInstaller distributable for Windows, macOS, Linux