align_loadings: Align ESEM/B-ESEM standardized loadings to a reference

View source: R/align_loadings.R

align_loadingsR Documentation

Align ESEM/B-ESEM standardized loadings to a reference

Description

Rotates a fitted solution's standardized loadings by an orthogonal Q so they match a reference matrix (Procrustes) or a deterministic canonical orientation. Useful when comparing bifactor solutions across software, or when you want a reproducible orientation across reruns.

Usage

align_loadings(
  x,
  target = "canonical",
  level = "configural",
  se_method = c("approx", "none")
)

Arguments

x

An esem_fit, besem_fit, esem_invariance, or raw lavaan S4 fit.

target

One of:

  • a numeric matrix (single-group) or list of matrices (multi-group) of reference standardized loadings, with rownames = items, colnames = factors;

  • another fit object of compatible structure (an esem_invariance target is read at the same level). The comparison is then made by orientation transfer: the unstandardized loadings are rotated to the reference and the factor covariances are transformed with the same rotation before standardizing. From the weak level on the non-reference groups have free factor covariances, and standardizing with the diagonal of Q'\Psi_g Q is not rotation-equivariant, so Procrustes alignment of standardized matrices leaves a residual there (about 0.01 to 0.02 on psych::bfi) that the transfer removes;

  • a list with one element per group, each a list with lambda (unstandardized loadings, items x factors), psi (factor covariance matrix) and theta (residual variances): a solution from elsewhere, compared by the same orientation transfer;

  • "group1" – align all groups to the first group's loadings within x (within-fit consistency, no external reference);

  • "canonical" (default) – apply deterministic sign + column order rule (sort columns by \Sigma\lambda^2 desc, sign-flip so the largest |\lambda| per column is positive). No external reference required.

level

Only used for esem_invariance objects. One of "configural" (default), "weak", "strong", "strict".

se_method

"approx" (default) returns row-wise diagonal delta-method SEs (ignores within-row covariance between loadings on different factors). "none" returns aligned point estimates only.

Details

Multi-group fits and esem_invariance objects are aligned per group. Item communalities (\Sigma\lambda^2), reliability indices (omega, ECV), and model fit are invariant under orthogonal rotation, so alignment affects only the per-loading partition, not substantive conclusions.

Value

An object of class aligned_loadings: a list with one element per group, each containing

loadings

aligned standardized loading matrix (items x factors)

se

aligned SEs (matching shape) or NULL

Q

the orthogonal rotation matrix applied

residual_max, residual_mean

max / mean abs residual vs target (only when an external reference was supplied)

The attribute "method" is "canonical", "procrustes" or "transfer".

Examples

data("HolzingerSwineford1939", package = "lavaan")

spec <- specify_model(
  Visual  = c("x1", "x2", "x3"),
  Textual = c("x4", "x5", "x6"),
  Speed   = c("x7", "x8", "x9"),
  data  = HolzingerSwineford1939,
  group = "school"
)


  inv <- esem_invariance(spec)

  # Canonical orientation (no external reference, deterministic)
  aligned <- align_loadings(inv, target = "canonical", level = "configural")
  print(aligned)

  # Within-fit: align all groups to group 1's orientation
  aligned <- align_loadings(inv, target = "group1", level = "configural")


## Not run: 
  # Align to Mplus output (requires a Mplus .out file + MplusAutomation)
  mp  <- MplusAutomation::readModels("besem_inv_configural.out")
  tgt <- extract_mplus_loadings(mp)
  aligned <- align_loadings(inv, target = tgt, level = "configural")

## End(Not run)

bifactory documentation built on Oct. 28, 2026, 1:06 a.m.