Skip to contents

When 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.

Usage

compare_loadings(Lambda_a, Lambda_b)

Arguments

Lambda_a, Lambda_b

Two n_traits × d loading matrices.

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