
Compare two loading matrices after Procrustes alignment
Source:R/rotate-loadings.R
compare_loadings.RdWhen two latent-variable fits use the same traits, the loading matrices are
only identified up to rotation and sign. Procrustes alignment finds the
orthogonal transform that brings one matrix as close as possible to the
other, then reports the residual disagreement. This is mainly a validation
helper for comparing gllvmTMB() with another implementation.
Value
A list with the optimal rotation R, the rotated Lambda_a_rot,
and the Frobenius distance after alignment.
Examples
set.seed(1)
# Simulate six traits, each measured twice on 100 units, from known
# loadings: traits 2-3 share one axis, traits 4-6 a second, and
# trait_1 loads on both.
Lambda_true <- cbind(c(0.7, 1.0, 0.9, 0.0, 0.0, 0.0),
c(0.7, 0.0, 0.0, 1.0, 0.9, 0.8))
sim <- simulate_unit_trait(n_units = 100, n_obs_per_unit = 2,
n_traits = 6, Lambda_B = Lambda_true,
psi_B = 0.4, sigma2_eps = 0.3)
fit <- gllvmTMB(value ~ 0 + trait +
latent(0 + trait | unit, d = 2, unique = TRUE),
data = sim$data, trait = "trait", unit = "unit")
L_hat <- extract_ordination(fit, "unit")$loadings
# The fitted axes may be rotated or sign-flipped relative to the true
# ones, so compare_loadings() first rotates the fitted loadings to
# match the truth as closely as possible, then measures the gap.
cmp <- compare_loadings(L_hat, Lambda_true)
# Fitted loadings after alignment; compare them with Lambda_true.
round(cmp$Lambda_a_rot, 2)
#> [,1] [,2]
#> trait_1 0.68 0.57
#> trait_2 0.94 0.01
#> trait_3 1.01 0.09
#> trait_4 -0.07 0.97
#> trait_5 0.01 0.84
#> trait_6 0.09 0.88
# Overall distance after alignment, and per-axis correlation.
round(cmp$frobenius, 3)
#> [1] 0.255
round(cmp$cor_per_factor, 3)
#> [1] 0.988 0.984
# Raw and varimax-rotated loadings from one fit differ only by a
# rotation, so once aligned they match exactly (distance 0).
L_rot <- rotate_loadings(fit, level = "unit")$Lambda
round(compare_loadings(L_rot, L_hat)$frobenius, 6)
#> [1] 0