% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/efa_group.R
\name{efa_group}
\alias{efa_group}
\title{Multigroup exploratory factor analysis}
\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{
\item{x}{A data frame or matrix of raw data (with \code{groups}), or a named list
of per-group data sets -- either raw data frames/matrices or correlation
matrices (all of one kind).}

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

\item{n_factors}{numeric. The common number of factors extracted in every
group.}

\item{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 \code{N} is taken from each group's
data. Default is \code{NA}.}

\item{reference_group}{The group to align the others to (a group name or an
integer index). If \code{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.}

\item{b_boot}{numeric. The number of non-parametric bootstrap replicates used to
form percentile confidence intervals for the between-group Tucker congruences.
\code{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.}

\item{ci}{numeric. The confidence level for the bootstrap congruence intervals, a
single value in \verb{(0, 1)}. Default is \code{0.95}.}

\item{seed}{numeric or \code{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
\link[future.apply:future_lapply]{future_lapply()}, configurable via \code{\link[future:plan]{future::plan()}}). The
caller's random-number stream is restored afterwards, leaving no side effect. Default is
\code{NULL}.}

\item{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 \code{delta} in absolute value. This is a descriptive
salience heuristic, not a significance test; common alternatives are \code{0.15} and \code{0.20}.
The threshold applies to whatever loading metric the chosen rotation produces (pattern
coefficients for an oblique rotation). \code{0} flags every cell. Default is \code{0.1}.

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

\item{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 \emph{Value}).
Default is \code{FALSE}.}

\item{se}{Not used. \code{efa_group()} itself sets the standard-error method of the
per-group \code{\link[=efa_fit]{efa_fit()}} calls, so a supplied value is dropped with a warning. Ask for
bootstrap confidence intervals of the between-group congruences with \code{b_boot}.
Default is \code{NULL}.}

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

A rotation-engine extra that shares a name with an \code{efa_group()} argument cannot reach
the rotation through \code{...}, because the argument takes the name first (for example
geomin's \code{delta}; see \code{delta} above).}
}
\value{
An object of class \code{efa_group}, a list containing:
\item{loadings}{A named list of the aligned per-group loading matrices. Their
columns match the columns of \code{target} in order and sign.}
\item{target}{The alignment target: the symmetric consensus target, or the
reference group's own loadings.}
\item{Phi}{A named list of the aligned per-group factor intercorrelations for
an oblique rotation; \code{NULL} otherwise.}
\item{congruence}{Tucker congruence between the aligned group loadings, a list
with: \code{matrices}, a nested list whose \verb{[[g]][[h]]} element is the
factor-by-factor congruence matrix between the aligned loadings of groups
\code{g} and \code{h}; \code{matched}, a groups-by-groups-by-factors array of the
matched-factor congruences (the diagonal of each pairwise matrix); and
\code{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 \code{NA}. When \code{b_boot > 0} (raw data), three further
elements are added: \code{matched_se}, the bootstrap standard error of each
matched congruence; \code{matched_ci}, a list of \code{lower} and \code{upper} percentile
confidence limits (each a groups-by-groups-by-factors array); and \code{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 \code{\link[=efa_fit]{efa_fit()}}).}
\item{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 (\code{rmse}), and \code{n_flagged}, the number of
loading cells whose absolute difference reaches \code{delta}.}
\item{flags}{A data frame with one row per group pair, item, and factor giving the signed
loading difference (\code{diff}), its absolute value (\code{abs_diff}), and \code{flagged}, whether it
reaches \code{delta}. When a bootstrap was run (\code{b_boot > 0}, raw data), \code{ci_lower},
\code{ci_upper}, and \code{ci_excludes_0} add the percentile confidence interval for the
difference and whether it excludes zero; otherwise these are \code{NA}.}
\item{invariance}{When \code{invariance = TRUE}, a data frame with one row per group pair and
factor giving the matched Tucker congruence (\code{phi}), its bootstrap CI lower bound
(\code{phi_lower}, \code{NA} without a bootstrap), and an approximate-invariance \code{verdict} based on
the Lorenzo-Seva and ten Berge (2006) similarity bands: \code{phi >= 0.95} is "equal" and
\verb{[0.85, 0.95)} is "fair"; congruences \verb{< 0.85}, below their bands, are labelled
"incongruent". The verdict is read from \code{phi_lower} when a bootstrap is available
(conservative) and from \code{phi} otherwise. A wide interval therefore lowers the verdict:
\code{phi} = 0.989 with \code{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 \code{diffs}. \code{NULL} when \code{invariance = FALSE}.}
\item{efa}{The named list of per-group \code{\link[=efa_fit]{efa_fit()}} objects (each retains its own
diagnostics, e.g. \code{heywood}).}
\item{alignment}{The alignment result: the consensus object (see
\code{\link[=efa_procrustes]{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 \code{target}/\code{aligned_loadings} are in a different (pre-gauge)
orientation than the gauged \code{target}/\code{loadings} returned above. Use \code{target}/\code{loadings}
above for comparisons.}
\item{settings}{A list of the settings used, including the per-group \code{N}, the
alignment method, the group that seeded the consensus frame (\code{alignment_start},
\code{NULL} on the reference path), the orientation the shared frame was put in
(\code{gauge}: the rotation's own name, \code{"principal_axes"}, or \code{"identity"} for a
single factor; \code{NULL} on the reference path), the rotation, the estimator, the
input type, whether a bootstrap is available (\code{can_bootstrap}, \code{FALSE} for
correlation-matrix input), and \code{seed} (\code{NULL} when none was supplied).}
}
\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
\code{\link[=efa_fit]{efa_fit()}}; the solutions are then aligned either to a symmetric consensus target
or to a chosen reference group (see \emph{Alignment}).
}
\details{
\subsection{Input}{

Groups can be supplied in two ways: raw data together with a grouping vector
(\code{x} a data frame or matrix, \code{groups} one value per row), or a named list of
per-group data sets in \code{x} (with \code{groups} left \code{NULL}). The list may hold raw
data frames or correlation matrices (supply \code{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 \code{n_factors}. Extra arguments in \code{...} (for example
\code{estimator}, \code{rotation}, \code{cor_method}, or an \code{\link[=estimate_control]{estimate_control()}} / \code{\link[=rotate_control]{rotate_control()}}
carrying the tuning knobs) are forwarded unchanged to each \code{\link[=efa_fit]{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 \code{n_factors}-factor model to be identified for the shared item set. Unlike a single
\code{\link[=efa_fit]{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.
}

\subsection{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:
\itemize{
\item \strong{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 \code{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.
\item \strong{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
\code{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 \code{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 \code{target}.
}

\subsection{Comparing the aligned loadings}{

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

\donttest{
# 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")
}

}
\references{
Efron, B., & Tibshirani, R. J. (1993). \emph{An Introduction to the Bootstrap}.
Chapman & Hall.

Gower, J. C. (1975). Generalized Procrustes analysis. \emph{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. \emph{Methodology}, 2, 57-64.
doi: 10.1027/1614-2241.2.2.57
}
\seealso{
Other factor analysis:
\code{\link[=efa_average]{efa_average()}},
\code{\link[=efa_fit]{efa_fit()}},
\code{\link[=efa_mi]{efa_mi()}},
\code{\link[=plot.efa_group]{plot.efa_group()}},
\code{\link[=print.efa_group]{print.efa_group()}}
}
\concept{factor analysis}
