Semantic factor analysis extracts factors from the meaning
of scale items. sfa_name() completes the workflow by giving
each factor a verbal label — the name of the psychological construct its
items express — retrieved deterministically from a pre-filtered pool of
~370,000 candidate terms (every WordNet entry and filtered Wikipedia
title that qualifies as a psychological-attribute noun phrase, completed
against five open construct inventories). Nothing is generated: the
label is the nearest eligible term to the factor in the embedding space,
under an instruction that asks the model to represent each item as
the construct it measures.
library(semanticfa)
fit <- sfa(big5$items, nfactors = 5)
labels <- sfa_name(fit)
labels
#> Factor labels (Qwen/Qwen3-Embedding-0.6B)
#>
#> F1 emotional instability [emotional instability, neuroticism]
#> F2 conscientiousness
#> ...The first call downloads the candidate pool for your embedding model
into the user cache (a one-time download; see sfa_pool());
afterwards everything is local and deterministic.
Or inline with the fit:
fit <- sfa(big5$items, nfactors = 5, label_factors = TRUE)
fit$labelsOne row per factor:
label — the automatic label. All quantitative claims
should use it.candidates — the leave-one-out candidate set: every
term that ranks first when the factor’s items are jackknifed one at a
time. This is the method’s error bar: a sharp factor yields a single
candidate, a fuzzy factor several. You may report a different member of
the set as the factor name, but state that a human chose it.rule — provenance: tier1 (the label is a
dictionary construct-noun) or top1 (no dictionary noun in
the top candidates; the raw best term was used).collision_moved — TRUE if this factor
originally picked the same label as another factor and was re-labeled
with its best non-conflicting candidate (the factor geometrically closer
to the shared term keeps it).Labels name the pole toward which the factor’s positive loadings point: a factor defined by reverse-worded sociability items may be labeled “social withdrawal” — that is the faithful reading of its loadings, not an error.
Extraction and naming reward different model properties: factor
structure is recovered well by compact encoders, but label
abstraction (choosing “anxiety” over a symptom word like
“palpitation”) improves with larger naming models.
sfa_name() therefore accepts its own model:
labels <- sfa_name(fit, model = "microsoft/harrier-oss-v1-27b")The factor structure stays exactly as fitted; only the naming space changes. Note the model itself is downloaded on first use and is large (~54 GB for the example above) — a GPU machine is strongly recommended for this option.
Naming reads whatever orientation the rotation happened to produce.
If you would rather choose the orientation that is most
nameable in the first place, sfa_leximax() rotates the
solution toward the construct lexicon, maximizing the agreement between
each factor’s naming target and the term retrieved for it:
# rotate an existing fit
rot <- sfa_leximax(fit)
rot$labels
# or fit and rotate in one call
fit_lex <- sfa(big5$items, nfactors = 5, rotate = "leximax")This is a rotation, so model fit is invariant: communalities and the
reproduced correlation matrix are unchanged, and only the axes move.
sfa_nameability() reports how nameable any given
orientation is, which is what lets you compare a leximax solution
against the oblimin one you started with.
Both need the naming word pool, so fetch it once with
sfa_pool(model, download = TRUE) before working
offline.
sfa_naming_instruction()); label robustness to instruction
rewording was validated during method development. Overriding it is
possible but warned.