
Extract cross-lineage coevolutionary modules
Source:R/extract-sigma.R
extract_coevolution_modules.Rdextract_coevolution_modules() standardizes a component-specific
cross-lineage covariance block and decomposes it into coupled trait axes.
For a named coevolution kernel tier, the helper computes
R = Sigma_row^{-1/2} Gamma Sigma_col^{-1/2} from the shared covariance
returned by extract_Sigma(), then applies a singular-value decomposition.
This is a point-estimate derived-output helper, covered by the package's
validation tests, for fitted
dense-kernel coevolution models whose component-specific Gamma blocks are
already extractable with extract_Gamma(). It does not report
uncertainty, choose the biological rank, estimate rho, or calibrate null
thresholds. Use simulation, bootstrap, and kernel-separability checks before
treating the returned axes as scientific evidence.
Arguments
- fit
A fitted
gllvmTMB_multiobject.- level
Character scalar naming the fitted covariance tier.
- row_traits, col_traits
Character vectors naming the row-lineage and column-lineage traits.
- scale
Character scalar.
"shape"(default) usesGamma_shape = Lambda_row Lambda_col^T;"effect"uses the fixed-rhoGamma_effectavailable for kernels built bymake_cross_kernel().- n_modules
Optional positive integer limiting the number of returned singular axes. By default all axes are returned.
- tol
Numerical tolerance for the generalized inverse square roots of the within-lineage shared covariance blocks.
Value
A list with R, modules, row_axes, and col_axes. R is the
standardized cross-lineage correlation-like block. modules has one row
per singular axis with its singular value and squared-value share.
row_axes and col_axes contain trait loadings for each coupled axis.
Examples
set.seed(1)
## A small host-partner system: 12 host and 12 partner species. Each
## lineage has its own similarity matrix. Here similarity decays with
## distance along a species ordering, a stand-in for a phylogenetic
## correlation matrix such as ape::vcv(tree, corr = TRUE).
host <- paste0("H", 1:12)
partner <- paste0("P", 1:12)
A_H <- 0.6^abs(outer(1:12, 1:12, "-"))
A_P <- A_H
dimnames(A_H) <- list(host, host)
dimnames(A_P) <- list(partner, partner)
## W records which hosts interact with which partners: host i interacts
## with partners i - 1, i and i + 1.
W <- 1 * (abs(outer(1:12, 1:12, "-")) <= 1)
dimnames(W) <- list(host, partner)
## One kernel over all 24 species; the fixed rho sets how strongly
## interacting hosts and partners are linked.
K <- make_cross_kernel(A_H, A_P, W, rho = 0.9)
## Simulate one latent axis shared by all four traits. Host traits are
## measured on hosts only and partner traits on partners only: four
## replicate measurements per species, each with noise (SD 0.3).
lambda <- c(h_size = 0.9, h_defence = 0.6, p_size = 0.8, p_attack = -0.5)
z <- drop(t(chol(K)) %*% rnorm(24))
names(z) <- rownames(K)
dat <- expand.grid(rep = 1:4, trait = names(lambda),
species = rownames(K), stringsAsFactors = FALSE)
dat <- dat[startsWith(dat$species, "H") == startsWith(dat$trait, "h_"), ]
dat$value <- lambda[dat$trait] * z[dat$species] +
rnorm(nrow(dat), sd = 0.3)
dat$trait <- factor(dat$trait, levels = names(lambda))
dat$species <- factor(dat$species, levels = rownames(K))
## Each record (one species, one replicate) is a sampling unit
dat$obs <- factor(paste(dat$species, dat$rep))
## One latent axis whose species scores are correlated as in K. The
## term is named "cross"; the functions below refer to it by name.
fit <- gllvmTMB(
value ~ 0 + trait + kernel_latent(species, K = K, d = 1, name = "cross"),
data = dat, trait = "trait", unit = "obs", cluster = "species"
)
## Coupled host and partner trait axes (point estimates only). With
## one latent axis (d = 1) there is a single module (n_modules = 1),
## and its singular value is 1 by construction, so read the trait
## loadings: hosts with larger size and defence are linked with
## partners of larger size and lower attack. Flipping the signs of
## both axes gives the same module.
mods <- extract_coevolution_modules(
fit,
level = "cross",
row_traits = c("h_size", "h_defence"),
col_traits = c("p_size", "p_attack"),
n_modules = 1
)
mods$modules
#> component module singular_value squared_share
#> 1 cross module_1 1 1
mods$row_axes
#> component side module trait loading
#> 1 cross row module_1 h_size -0.8315609
#> 2 cross row module_1 h_defence -0.5554336
mods$col_axes
#> component side module trait loading
#> 1 cross column module_1 p_size -0.8799045
#> 2 cross column module_1 p_attack 0.4751505