EFAtools exposes its functionality through a set of
lowercase efa_* functions: efa_screen(),
efa_retain(), efa_fit(), and so on. Earlier
releases used uppercase names instead (EFA(),
N_FACTORS(), OMEGA(), …). The uppercase names
have not been removed — they still work — but the
efa_* functions are now the recommended interface. This
vignette explains the change, gives the full old-to-new mapping, and
walks through the one migration that involves more than a rename:
EFA() to efa_fit().
Why the efa_* Interface
The efa_* names are the interface we recommend for new
code. They read consistently, share a common prefix that groups them in
tab-completion and the documentation index, and they are where the
package’s development now happens.
The uppercase names are superseded, not deprecated. In practice that means:
- They keep working with their original arguments, and calling them emits no warning or message. Existing scripts and saved analyses continue to run byte-for-byte unchanged.
- Their argument lists are frozen. New capabilities
are added only to the
efa_*functions, so an uppercase name will never grow a new argument.
So there is no urgency to rewrite working code. Migrate a script when you want the new functions’ additional features (or simply for consistency); until then the old calls remain valid.
The Old-to-New Mapping
Every uppercase function has a lowercase counterpart. For most of them the migration is a pure rename: the arguments are identical and only the name changes.
efa_reliability() and efa_scores() are
broader than the OMEGA() and FACTOR_SCORES()
they replace, but they cover the same use cases and are the recommended
way to obtain those quantities going forward.
Alongside the renamed functions, the package ships tools that have
no uppercase predecessor — they are new, and available
only under the efa_* interface (and the two control
constructors):
| New function | Purpose |
|---|---|
efa_screen() |
Data screening and factorability diagnostics in one report |
efa_group() |
Multigroup EFA with factor congruence |
efa_simulate() |
Simulate data from a factor model |
efa_power() |
Power analysis for EFA |
estimate_control() |
Bundle the estimation tuning knobs (see below) |
rotate_control() |
Bundle the rotation tuning knobs (see below) |
For a plain rename, the call is unchanged apart from the name. For example, the Kaiser-Meyer-Olkin criterion:
cor_mat <- test_models$baseline$cormat
# Recommended name -- exactly the same arguments as KMO():
efa_kmo(cor_mat)
#>
#> ── Kaiser-Meyer-Olkin criterion (KMO) ──────────────────────────────────────────
#>
#> ✔ The overall KMO value for your data is marvellous.
#> These data are probably suitable for factor analysis.
#>
#> Overall: 0.916
#>
#> For each variable:
#> V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13
#> 0.900 0.914 0.924 0.932 0.923 0.891 0.928 0.919 0.916 0.892 0.928 0.908 0.922
#> V14 V15 V16 V17 V18
#> 0.905 0.924 0.934 0.907 0.923
# The old name still works and returns the same result:
identical(KMO(cor_mat)$KMO, efa_kmo(cor_mat)$KMO)
#> [1] TRUEMigrating from EFA() to efa_fit()
EFA() to efa_fit() is the only migration
that changes an argument list. EFA() exposed every
estimation and rotation setting as a flat argument, which made for a
long and somewhat unwieldy signature. efa_fit() keeps the
primary choices as top-level arguments —
x, n_factors, N,
estimator, rotation, se,
cor_method, use, b_boot,
ci, and seed — and collects the
tuning knobs into two small control objects built by
estimate_control() and rotate_control().
Each flat EFA() tuning argument now lives in one of the
two controls:
| Control object | Arguments it holds |
|---|---|
estimate_control() |
type, init_comm, criterion,
criterion_type, max_iter,
abs_eigen, start_method
|
rotate_control() |
type, normalize, precision,
order_type, varimax_type, p_type,
k, random_starts
|
A few points make the translation mechanical:
-
The
typepreset feeds both controls.EFA()had a singletype("EFAtools","psych","SPSS", or"none") that governed both estimation and rotation. Pass the sametypeto each constructor to reproduce it. Because the two controls are independent, you can now give them different presets, but you do not have to. -
Three arguments were renamed. The estimator is
selected with
estimatorinstead ofmethod— passingmethodtoefa_fit()is an error that points to the new name, whileEFA()keeps itsmethodargument.P_typeis nowp_type, andrandomStartsis nowrandom_starts;EFA()still accepts those old spellings silently. -
Rotation-engine extras — the criterion-specific
arguments a rotation may take, such as
maxit,gam(oblimin), ordelta(geomin) — are passed throughrotate_control()’s...(or, equivalently,efa_fit()’s own...). Both validate the names: an extra the selected rotation’s engine cannot consume (for examplegamma, a misspelling of oblimin’sgam) is rejected with an error, whereEFA()silently ignores it.
The side-by-side below reproduces an SPSS-style analysis. The old flat call and the new control-based call give identical results:
# Old flat interface
efa_old <- EFA(cor_mat, n_factors = 3, N = 500, type = "SPSS")
# New interface: the SPSS preset travels through the two control objects
efa_new <- efa_fit(cor_mat, n_factors = 3, N = 500,
estimate_control = estimate_control(type = "SPSS"),
rotate_control = rotate_control(type = "SPSS"))
# Identical numerical result
all.equal(efa_old$rot_loadings, efa_new$rot_loadings)
#> [1] TRUEAn individual knob is set on the control it belongs to. A flat
EFA(..., type = "SPSS", max_iter = 500, k = 3) becomes:
efa_fit(cor_mat, n_factors = 3, N = 500, rotation = "promax",
estimate_control = estimate_control(type = "SPSS", max_iter = 500),
rotate_control = rotate_control(type = "SPSS", k = 3))One behavioural difference is worth knowing about.
efa_fit() rejects a tuning knob passed
directly, rather than silently ignoring it. This turns a common and
previously invisible mistake into an immediate, informative error that
names the constructor the knob belongs to:
efa_fit(cor_mat, n_factors = 3, N = 500, max_iter = 500)
#> Error: `max_iter` cannot be passed to `efa_fit()` directly.
#> i The estimation and rotation tuning knobs live in `estimate_control()` and `rotate_control()`.
#> i For example: `efa_fit(x, ..., estimate_control = estimate_control(max_iter = 500))`.The same move — flat estimation knobs into
estimate_control() — applies to the other functions that
run factor extractions internally. efa_retain(),
efa_schmid_leiman(), and the retention criteria that fit a
model or use EFA-based eigenvalues (efa_parallel(),
efa_kgc(), efa_scree(),
efa_hull(), efa_nest(), and
efa_smt()) all take an estimate_control()
object in place of the loose estimation arguments; and
efa_average() takes p_type in place of
P_type.
Returned Objects Keep Their Legacy Classes
Migrating a call does not break code that inspects or dispatches on
the result. The renamed functions attach the legacy class
alongside the new one, so inherits() checks and S3
methods written against the old classes keep working — including on
objects saved from earlier sessions.
The same holds for the other direct renames:
efa_retain() objects still inherit
"N_FACTORS", efa_bartlett() still inherits
"BARTLETT", and likewise for efa_kmo(),
efa_schmid_leiman(), efa_compare(),
efa_average(), and efa_mi(). The retention
criteria (efa_parallel(), efa_ekc(), and the
rest) all share the single efa_retention class they have
returned since EFAtools 0.8.0. And the uppercase functions themselves
keep working unchanged: an EFA() call returns the same
result as before, now additionally classed efa so that it
picks up the shared methods — inherits() checks against
"EFA" are unaffected.
Where to Next
That is the whole migration: rename the function, and — for
EFA() only — move the tuning knobs into
estimate_control() and rotate_control(). For
an overview of the full analysis workflow under the efa_*
interface, see the EFAtools vignette; the
individual help pages document each function’s arguments in full.