---
title: "Naming the factors"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Naming the factors}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

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*.

## Basic use

```{r}
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:

```{r}
fit <- sfa(big5$items, nfactors = 5, label_factors = TRUE)
fit$labels
```

## What you get

One 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.

## Using a larger model for naming

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:

```{r}
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.

## Rotating toward nameable factors

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:

```{r}
# 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.

## Reproducibility notes

* Deterministic: same items, same fit, same model, same pool version ⇒
  same labels, bit for bit.
* The naming instruction is fixed and inspectable
  (`sfa_naming_instruction()`); label robustness to instruction rewording
  was validated during method development. Overriding it is possible but
  warned.
* The candidate pool is a census (WordNet + Wikipedia + open construct
  ontologies) with mechanical filters only — no hand-curated word list
  anywhere in the pipeline.
```
