efa_group: Multigroup exploratory factor analysis

View source: R/efa_group.R

efa_groupR Documentation

Multigroup exploratory factor analysis

Description

Fit an exploratory factor analysis in each of several groups at a common number of factors and bring the per-group solutions into one shared orientation so their loadings can be compared. Each group is fitted with efa_fit(); the solutions are then aligned either to a symmetric consensus target or to a chosen reference group (see Alignment).

Usage

efa_group(
  x,
  groups = NULL,
  n_factors,
  N = NA,
  reference_group = NULL,
  b_boot = 0L,
  ci = 0.95,
  seed = NULL,
  delta = 0.1,
  invariance = FALSE,
  se = NULL,
  ...
)

Arguments

x

A data frame or matrix of raw data (with groups), or a named list of per-group data sets – either raw data frames/matrices or correlation matrices (all of one kind).

groups

A vector with one value per row of x, giving each row's group. Only used when x is a single raw data set; leave NULL when x is a list. Rows with a missing group value are dropped with a warning.

n_factors

numeric. The common number of factors extracted in every group.

N

numeric. The number of observations per group, used only for correlation-matrix input: either a single value applied to all groups or one value per group. Ignored for raw data, where N is taken from each group's data. Default is NA.

reference_group

The group to align the others to (a group name or an integer index). If NULL (default), orthogonal and unrotated solutions use the symmetric consensus target; oblique solutions with more than one factor fall back to the first group as reference. Supplying a value forces the reference alignment.

b_boot

numeric. The number of non-parametric bootstrap replicates used to form percentile confidence intervals for the between-group Tucker congruences. 0 (the default) skips the bootstrap and returns the congruence point estimates only. Bootstrapping requires raw data; it is skipped with a warning for correlation-matrix input.

ci

numeric. The confidence level for the bootstrap congruence intervals, a single value in ⁠(0, 1)⁠. Default is 0.95.

seed

numeric or NULL. An optional seed making the analysis reproducible. It covers the per-group fits, some of whose rotations draw random starts, whether or not a bootstrap is run. With a bootstrap, it also makes the result independent of how many parallel workers are used (bootstrap replicates run with future_lapply(), configurable via future::plan()). The caller's random-number stream is restored afterwards, leaving no side effect. Default is NULL.

delta

numeric. The salience threshold for the per-item loading-difference flag table: an item's loading on a factor is flagged for a group pair when the groups' aligned loadings differ by at least delta in absolute value. This is a descriptive salience heuristic, not a significance test; common alternatives are 0.15 and 0.20. The threshold applies to whatever loading metric the chosen rotation produces (pattern coefficients for an oblique rotation). 0 flags every cell. Default is 0.1.

The geomin rotations take a criterion parameter of the same name. A delta given directly is always this salience threshold and never reaches the rotation; give the geomin parameter as rotate_control(delta = ...). With rotation = "geominT" or "geominQ", a supplied delta gives a warning that says which of the two applies.

invariance

logical. Whether to add an approximate-invariance verdict per factor and group pair from the Lorenzo-Seva and ten Berge (2006) congruence bands (see Value). Default is FALSE.

se

Not used. efa_group() itself sets the standard-error method of the per-group efa_fit() calls, so a supplied value is dropped with a warning. Ask for bootstrap confidence intervals of the between-group congruences with b_boot. Default is NULL.

...

Additional arguments passed to efa_fit() for every group (for example estimator, rotation, or cor_method). The estimate_control() and rotate_control() objects are accepted through ... as well, although they are not declared formals: pass them as ⁠estimate_control =⁠ / ⁠rotate_control =⁠ exactly as you would to efa_fit(). A name that is neither an efa_fit() argument nor a rotation-engine extra is rejected.

A rotation-engine extra that shares a name with an efa_group() argument cannot reach the rotation through ..., because the argument takes the name first (for example geomin's delta; see delta above).

Details

Input

Groups can be supplied in two ways: raw data together with a grouping vector (x a data frame or matrix, groups one value per row), or a named list of per-group data sets in x (with groups left NULL). The list may hold raw data frames or correlation matrices (supply N), but not a mix of the two. All groups must contain the same items in the same order; a different item set or order is an error rather than being silently reordered.

Every group is fitted at the same n_factors. Extra arguments in ... (for example estimator, rotation, cor_method, or an estimate_control() / rotate_control() carrying the tuning knobs) are forwarded unchanged to each efa_fit() call, so the estimator and rotation are common to all groups.

The requested number of factors must be small enough, relative to the number of items, for the n_factors-factor model to be identified for the shared item set. Unlike a single efa_fit() fit – which only warns on an under-identified model – a multigroup fit aborts when this fails, because a shared alignment target across an under-identified group is not interpretable.

Alignment

A factor solution is identified only up to a rotation of its factors, so the per-group solutions must be brought into a common orientation before their loadings can be compared. Two strategies are available and are chosen automatically:

  • Consensus (the default for orthogonal rotations and for unrotated solutions): a symmetric target is built across all groups using Generalized Procrustes Analysis (Gower, 1975), and every group's loadings are rotated to it. Because this target's own orientation is arbitrary, it is then rotated once more into a fixed convention (called the gauge), and the same transform is applied to every group. The gauge uses the same simple-structure criterion as the requested rotation, applied to the target itself, so the shared loadings are in the same kind of frame as the per-group solutions they summarise. Where no rotation criterion identifies a unique frame (an unrotated solution, or a two-factor bifactorT request), the target's principal-axes orientation is used instead. Either way the columns are ordered by decreasing sum of squares and signed by their column sums, and the shared orientation – and hence every reported congruence, difference, and flag – does not depend on the order the groups are supplied, to well beyond the precision loadings are reported at.

  • Reference: every group's loadings are aligned by Procrustes rotation to one reference group's loadings, which are kept fixed. This path is used when reference_group is given, and is used automatically for oblique rotations because the consensus iteration is not defined for oblique transforms with more than one factor. When an oblique rotation triggers the reference path without an explicit reference_group, the first group is used and a message reports this; the requested rotation is never silently changed.

In both cases the returned per-group loadings share the column order and sign of the returned target.

Comparing the aligned loadings

Because the per-group loadings share one orientation, they can be compared cell by cell. efa_group() reports a per-pair summary of their differences (diffs) and a per-item, per-factor flag table (flags) marking cells whose absolute difference reaches delta; a bootstrap (b_boot > 0) additionally reports, for every cell, whether its difference's confidence interval excludes zero. With invariance = TRUE, each factor and group pair also gets an approximate-invariance verdict based on the matched Tucker congruence (see Value for the similarity bands and how a bootstrap is used).

Value

An object of class efa_group, a list containing:

loadings

A named list of the aligned per-group loading matrices. Their columns match the columns of target in order and sign.

target

The alignment target: the symmetric consensus target, or the reference group's own loadings.

Phi

A named list of the aligned per-group factor intercorrelations for an oblique rotation; NULL otherwise.

congruence

Tucker congruence between the aligned group loadings, a list with: matrices, a nested list whose ⁠[[g]][[h]]⁠ element is the factor-by-factor congruence matrix between the aligned loadings of groups g and h; matched, a groups-by-groups-by-factors array of the matched-factor congruences (the diagonal of each pairwise matrix); and degenerate, a groups-by-groups logical matrix flagging pairs whose congruence is undefined (for example, a near-zero factor), for which the corresponding entries are NA. When b_boot > 0 (raw data), three further elements are added: matched_se, the bootstrap standard error of each matched congruence; matched_ci, a list of lower and upper percentile confidence limits (each a groups-by-groups-by-factors array); and n_boot, the number of bootstrap replicates that aligned in every group and so contributed to the intervals (a replicate whose fit did not converge is retained, as in efa_fit()).

diffs

A data frame with one row per group pair summarising the differences between their aligned loadings: the mean, median, minimum, and maximum absolute difference, the root-mean-square difference (rmse), and n_flagged, the number of loading cells whose absolute difference reaches delta.

flags

A data frame with one row per group pair, item, and factor giving the signed loading difference (diff), its absolute value (abs_diff), and flagged, whether it reaches delta. When a bootstrap was run (b_boot > 0, raw data), ci_lower, ci_upper, and ci_excludes_0 add the percentile confidence interval for the difference and whether it excludes zero; otherwise these are NA.

invariance

When invariance = TRUE, a data frame with one row per group pair and factor giving the matched Tucker congruence (phi), its bootstrap CI lower bound (phi_lower, NA without a bootstrap), and an approximate-invariance verdict based on the Lorenzo-Seva and ten Berge (2006) similarity bands: phi >= 0.95 is "equal" and ⁠[0.85, 0.95)⁠ is "fair"; congruences ⁠< 0.85⁠, below their bands, are labelled "incongruent". The verdict is read from phi_lower when a bootstrap is available (conservative) and from phi otherwise. A wide interval therefore lowers the verdict: phi = 0.989 with phi_lower = 0.726 is labelled "incongruent", because the band is applied to the lower bound. Tucker's congruence is invariant to a proportional rescaling of a factor's loadings, so a factor can be graded "equal" even when one group's loadings on it are uniformly stronger; read the verdict alongside diffs. NULL when invariance = FALSE.

efa

The named list of per-group efa_fit() objects (each retains its own diagnostics, e.g. heywood).

alignment

The alignment result: the consensus object (see efa_procrustes()), or a list with the reference group, the target, and the per-group Procrustes results. On the consensus path, this is the raw Procrustes iteration output: its target/aligned_loadings are in a different (pre-gauge) orientation than the gauged target/loadings returned above. Use target/loadings above for comparisons.

settings

A list of the settings used, including the per-group N, the alignment method, the group that seeded the consensus frame (alignment_start, NULL on the reference path), the orientation the shared frame was put in (gauge: the rotation's own name, "principal_axes", or "identity" for a single factor; NULL on the reference path), the rotation, the estimator, the input type, whether a bootstrap is available (can_bootstrap, FALSE for correlation-matrix input), and seed (NULL when none was supplied).

References

Efron, B., & Tibshirani, R. J. (1993). An Introduction to the Bootstrap. Chapman & Hall.

Gower, J. C. (1975). Generalized Procrustes analysis. Psychometrika, 40, 33-51. doi: 10.1007/BF02291478

Lorenzo-Seva, U., and ten Berge, J. M. F. (2006). Tucker's congruence coefficient as a meaningful index of factor similarity. Methodology, 2, 57-64. doi: 10.1027/1614-2241.2.2.57

See Also

Other factor analysis: efa_average(), efa_fit(), efa_mi(), plot.efa_group(), print.efa_group()

Examples

# Raw data split by a grouping vector (unrotated, consensus alignment)
g <- rep(c("g1", "g2"), length.out = nrow(GRiPS_raw))
mg <- efa_group(GRiPS_raw, groups = g, n_factors = 1)
mg$loadings

# Per-pair difference summary and the per-item salience-flag table
mg$diffs
mg$flags


# Percentile bootstrap confidence intervals for the between-group congruences, with an
# approximate-invariance verdict read conservatively off the congruence CI lower bound
mg_ci <- efa_group(GRiPS_raw, groups = g, n_factors = 1, b_boot = 100, seed = 42,
                   invariance = TRUE)
mg_ci$congruence$matched_ci
mg_ci$invariance

# A named list of correlation matrices sharing the same items, common
# three-factor model, orthogonal rotation -> symmetric consensus target
bands <- list(age_6_8 = WJIV_ages_6_8$cormat, age_14_19 = WJIV_ages_14_19$cormat)
Ns <- c(WJIV_ages_6_8$N, WJIV_ages_14_19$N)
efa_group(bands, n_factors = 3, N = Ns, rotation = "varimax")

# An oblique rotation aligns to a reference group (reported via a message)
efa_group(bands, n_factors = 3, N = Ns, rotation = "promax")



EFAtools documentation built on Aug. 21, 2026, 5:16 p.m.