Skip to contents

Applies a post-hoc rotation (e.g. varimax) to the loading matrix \(\Lambda\) of either the between-unit (level = "unit") or within-unit (level = "unit_obs") reduced-rank component. The latent scores are rotated by the inverse transform so the linear predictor (and the fitted log-likelihood) is unchanged; only the parameterisation changes.

Usage

rotate_loadings(
  fit,
  level = c("unit", "unit_obs", "B", "W"),
  method = c("varimax", "promax", "none")
)

Arguments

fit

A gllvmTMB_multi fit.

level

"unit" (between-unit) or "unit_obs" (within-unit). Legacy aliases "B" and "W" are accepted with a deprecation warning.

method

One of "varimax", "promax", or "none".

Value

A list with rotated Lambda (n_traits × d), rotated scores (with rows = units or within-unit observations, columns = factors), and the rotation matrix T such that \(\Lambda_{\text{rotated}} = \Lambda T\).

Details

Useful when the lower-triangular constraint inherited from the glmmTMB-style lower-triangular constraint produces hard-to-interpret factors; a varimax rotation almost always yields cleaner trait-loading patterns.

The rotation is applied to \(\Lambda\) on the left and to the latent scores on the right using the inverse transform, so \(\Lambda_{\text{rot}} \mathbf{z}_{\text{rot}} = \Lambda \mathbf{z}\) is unchanged. For varimax, T is orthogonal so the rotation preserves the implied covariance \(\Lambda \Lambda^\top\); for promax, T is oblique and the columns of \(\Lambda_{\text{rot}}\) are no longer orthogonal.

Examples

if (FALSE) { # \dontrun{
set.seed(1)
sim <- simulate_site_trait(
  n_sites = 60, n_species = 12, n_traits = 4,
  Lambda_B = matrix(c(1.0, 0.7, -0.3, 0.5,
                      0.3, -0.5, 0.8, 0.2),
                    nrow = 4, ncol = 2),
  S_B = c(0.3, 0.3, 0.3, 0.3),
  seed = 1
)
fit <- gllvmTMB(value ~ 0 + trait +
                        latent(0 + trait | site, d = 2) +
                        unique(0 + trait | site),
                data = sim$data)
raw <- extract_ordination(fit, "unit")
rot <- rotate_loadings(fit, level = "unit", method = "varimax")
# raw$loadings - lower-triangular (hard to read)
# rot$Lambda  - varimax-rotated (typically simpler structure)
} # }