Performs exploratory factor analysis on language model embeddings of psychological scale items. Embeds item text with sentence transformers or other language models, transforms the embeddings into item-by-item similarity matrices, and extracts latent factor structure via standard exploratory factor analysis, using several similarity transforms (atomic reversed, SQuID centering, mean-centered Pearson) and fit diagnostics tailored to embedding matrices (TEFI, RMSR, CAF, McDonald's omega). Factor retention spans embedding-adapted parallel analysis, the empirical Kaiser criterion, Velicer's minimum average partial, a comparison-data misfit profile, and a calibrated learned rule that reports conformal intervals. Further tools orient factor axes toward retrieved construct terms by lexical target rotation, and audit whether a scale's items cover their construct's semantic region without collecting responses. The underlying methods are documented with full citations in the corresponding function help pages. Returns objects compatible with 'psych' and 'EFAtools' workflows.
Response-free semantic analysis of psychometric scales.
semanticfa reads the meaning of a scale's item wording with a language model
and recovers, interprets, and refines the scale's latent structure — entirely
from the items, with no human response data. Factor analysis on the item
embeddings is the centerpiece, but the package is a full toolkit for working
with a scale before (or without) collecting data: building semantic similarity
matrices, deciding how many factors to keep, reading a semantic "loadings"
table, comparing the recovered structure to theory, flagging redundant items,
building short forms, vetting brand-new candidate items, detecting
jingle/jangle fallacies across scales, naming the factors it finds, auditing
whether the items actually cover their construct, and visualizing the item
space.
# from CRAN:
install.packages("semanticfa")
# development version:
# install.packages("remotes")
remotes::install_github("devon7y/semanticfa")
The core of the package is pure R. Turning item text into embeddings on your
machine uses Python via reticulate — needed only if you want the package to
embed text for you (you can always bring your own embeddings):
sfa_install_python() # one-time: provisions sentence-transformers
Two-dimensional item maps work out of the box (Rtsne and uwot are bundled).
One optional package, EGAnet, powers EGA-based factor retention /
dimension selection and the faithful UVA redundancy method — install it only if
you use those parts.
library(semanticfa)
data(big5) # 50 IPIP Big-Five items + precomputed Qwen3-Embedding-8B embeddings
# one call: embed -> similarity -> retain -> extract -> diagnose
fit <- sfa(
data.frame(code = big5$codes, item = big5$items,
factor = big5$factors, scoring = big5$scoring),
embeddings = big5$embeddings, nfactors = 5)
fit
# interpret and refine, all from the same fit
plot(fit, type = "scree") # scree with parallel-analysis overlay
sfa_corplot(fit) # item-by-item similarity heatmap, grouped by factor
sfa_anchor(fit) # item-by-construct "belonging" (a semantic loadings table)
sfa_congruence(fit, target = big5$factors, # agreement with theory (partition metrics)
metrics = c("nmi", "ari"))
sfa_redundancy(fit) # near-duplicate items
No respondents are involved at any step.
| Function | Purpose |
|---|---|
sfa_embed() |
Embed item text — on-device sentence-transformers (Qwen3 models, default), the OpenAI API, or any custom function. Results are cached. |
sfa_load_npz() |
Load pre-generated embeddings (e.g. a GPU job) from a NumPy .npz, no Python needed. |
sfa_similarity() |
Item-by-item similarity matrix with a choice of four encodings (below). |
sfa_nli_matrix() |
Signed, valence-aware similarity from natural-language inference (entailment − contradiction), so reverse-keyed items are handled directly. |
sfa_install_python(), sfa_clear_cache() |
Provision the embedding environment / clear the cache. |
| Function | Purpose |
|---|---|
sfa() |
The end-to-end pipeline: embed → similarity → retain → extract → diagnose. Accepts raw text, precomputed embeddings, an sfa_embeddings object, or a precomputed similarity matrix. |
sfa_nfactors() |
How many factors to keep — parallel analysis by default, with Kaiser, TEFI, EKC, EGA, MAP, and sem-k as an opt-in consensus battery. |
sfa_parallel() |
Embedding-adapted parallel analysis (random-unit-vector null; no sample size needed). |
sfa_ekc() |
Empirical Kaiser criterion (Braeken & van Assen) with the embedding dimension as the sample size. |
sfa_map() |
Velicer's minimum average partial (tracks all reliable structure — see docs before trusting it here). |
sfa_cd() |
Comparison-data misfit profile (Ruscio & Roche, adapted): does the matrix have a crisp factor count, or graded structure? |
sfa_semk() |
sem-k: a learned retention rule trained on planted-truth item sets, reporting a factor count with a 90% conformal interval. |
sfa_dimselect() |
Select the informative leading embedding coordinates ("depth") by EGA depth optimization. |
as_psych() |
Hand the solution to psych (factor.congruence(), fa.sort(), …) as a standard fa object. |
| Function | Purpose |
|---|---|
sfa_anchor() |
An item-by-construct belonging matrix — a semantic loadings table — built from construct centroids and/or construct-name embeddings. |
sfa_project() |
Place items on interpretable bipolar axes (e.g. mild ↔ severe, passive ↔ active). |
sfa_congruence() |
Compare the recovered structure to an empirical or theoretical one: Tucker φ, NMI, ARI, Frobenius, and disattenuated correlation. |
sfa_jinglejangle() |
Flag jingle (same name, different content) and jangle (different name, same content) fallacies across multiple scales. |
sfa_name() |
Name the factors: retrieve the construct term each factor's items point at, from a large word pool. |
sfa_leximax() |
Lexical target rotation: orient the axes toward nameable constructs (also sfa(rotate = "leximax")). Model fit is invariant. |
sfa_nameability() |
How nameable a given orientation is, plus each factor's retrieved label and runner-up candidates. |
| Function | Purpose |
|---|---|
sfa_redundancy() |
Detect near-duplicate items via faithful Unique Variable Analysis (absolute wTO on an EBICglasso network) or a direct cosine criterion. |
sfa_simplify() |
Build response-free short forms by selecting the most representative items per factor. |
sfa_item_fit() |
Vet a brand-new candidate item: how well does it match the construct name and the other items, and is it redundant with any of them? |
| Function | Purpose |
|---|---|
sfa_coverage() |
Test whether a scale's items cover their construct's semantic region, against a matched-size null: construct coverage, per-item relevance with p-values, and the gaps left uncovered. Multi-factor scales audit per subscale. |
sfa_build_region() |
Build a construct's semantic region from a text corpus (sfa_build_regions() for many at once). |
sfa_cross_matrix() |
The content analogue of a multitrait matrix: every factor audited against every construct region, with no data collection. |
sfa_gaps(), sfa_deletion_gaps() |
What a scale misses, and what a shortened form gives up relative to the full one. |
sfa_build_bank(), sfa_embedding_bank() |
Pre-embed items and regions once, then run every audit from saved embeddings with no encoder loaded. |
| Function | Purpose |
|---|---|
sfa_corplot() |
Heatmap of the item-by-item similarity matrix, grouped/ordered by factor (order accepts factor-name abbreviations, e.g. c("D","A","S")). |
sfa_itemplot() |
2-D item map via t-SNE, UMAP, PCA, or MDS (sfa_tsneplot() is a deprecated alias). |
plot(fit, "scree") |
Scree plot with the parallel-analysis overlay. |
Every sfa() fit reports KMO, a real partition-based TEFI (negative;
lower is better), RMSR, CAF, McDonald's ω, and — when theoretical
factors are supplied — a factor-to-theory alignment matrix (DAAL).
summary(fit) adds the full breakdown, and calibrate = TRUE adds a Monte
Carlo null reference for the diagnostics.
sfa_similarity(..., encoding=))| Method | Description | Keying |
|---|---|---|
"atomic" |
L2-normalize, cosine similarity | keying-free (scoring ignored) |
"atomic_reversed" |
Sign-flip reverse-keyed items, L2-normalize, cosine | uses scoring sign-flip |
"squid" |
Subtract the questionnaire-mean embedding, then cosine | keying-free |
"mean_centered_pearson" (default) |
Mean-center → cosine = Pearson correlation | keying-free |
data(big5) — the 50-item IPIP Big-Five markers (public domain) with precomputed
Qwen3-Embedding-8B embeddings (rounded to 4 decimal places), so every example runs
without Python or network access.
A getting-started tour, worked end-to-end on the bundled Big Five inventory, is
in the package vignette (vignette("introduction", package = "semanticfa")).
GPL (>= 3)