Skip to contents

For a phylogenetic species-level GLLVM, decomposes each trait's between-species latent variance into three additive components that sum to one: $$H_t^2 \;=\; \frac{[\boldsymbol\Sigma_\text{phy}]_{tt}}{V_{\eta,t}}, \qquad C^2_{\text{non},t} \;=\; \frac{[\boldsymbol\Sigma_\text{non,shared}]_{tt}}{V_{\eta,t}}, \qquad \psi_t \;=\; \frac{[\boldsymbol\Psi_\text{non}]_{tt}}{V_{\eta,t}},$$ where \(V_{\eta,t} = [\boldsymbol\Sigma_\text{phy}]_{tt} + [\boldsymbol\Sigma_\text{non,shared}]_{tt} + [\boldsymbol\Psi_\text{non}]_{tt}\) is the total between-species latent variance for trait \(t\) .

Usage

extract_phylo_signal(
  fit,
  ci = FALSE,
  conf_level = 0.95,
  method = c("profile", "wald", "bootstrap"),
  nsim = 500L,
  seed = NULL
)

Arguments

fit

A fit returned by gllvmTMB() with a phylo_latent() term.

ci

Logical. When TRUE, adds confidence-interval columns to the output for the H^2 column. Default FALSE for backward compatibility.

conf_level

Confidence level when ci = TRUE. Default 0.95.

method

One of "profile" (default), "wald", "bootstrap". Only used when ci = TRUE. For 2-component decompositions (a phylo diagonal vs species-level diagonal component only) profile uses a linear contrast; for 3-component decompositions (PGLLVM with phylo_latent plus a species-level latent decomposition with Psi) the full profile path is not yet implemented and falls back to numerical delta-method Wald bounds labelled "wald(numeric)".

nsim

Number of bootstrap replicates when method = "bootstrap". Default 500.

seed

Optional RNG seed for the bootstrap.

Value

A data frame with columns trait, H2, C2_non, Psi, V_eta (the denominator), one row per trait. The three proportions sum to 1.0 by construction. When ci = TRUE, three additional columns are added: H2_lower, H2_upper, H2_method.

Details

Interpretation:

\(H_t^2\)

phylogenetic signal — proportion of between- species latent variance attributable to phylogenetically structured variation ("evolutionary conservatism"). When the model uses the folded phylo_latent(..., unique = TRUE) decomposition, \(\boldsymbol\Sigma_\text{phy}\) is the sum \(\boldsymbol\Lambda_\text{phy} \boldsymbol\Lambda_\text{phy}^{\!\top} + \boldsymbol\Psi_\text{phy}\) and \(H_t^2\) reflects the total phylogenetic variance.

\(C^2_{\text{non},t}\)

non-phylogenetic communality — proportion of variance attributable to shared non-phylogenetic axes ("coordinated tip-level lability" across traits).

\(\psi_t\)

uniqueness — proportion of variance not captured by any shared axis ("relative modularity").

Requires phylo_latent() (optionally with unique = TRUE for a phylogenetic Psi) plus a species-level latent() term, which carries its diagonal Psi companion by default. If the species-tier Psi is absent (a latent(..., unique = FALSE) subset), \(\psi_t = 0\) for all traits and a cli::cli_inform() advisory fires.

Which variances enter the denominator

V_eta is the species-level latent variance: only components whose grouping is the cluster column contribute. Concretely \(V_\eta = \sigma^2_{phy} + \sigma^2_{non}\), where \(\sigma^2_{non}\) collects the species-grouped non-phylogenetic variance.

  • When unit == cluster (the usual unit = "species" setup) the ordinary latent() term and its Psi companion are species-level, so both enter.

  • When unit != cluster (a crossed site x species design) Lambda_B and its Psi are site-level and do not enter; the species-level non-phylogenetic variance is the cluster-tier q_it term (indep(0 + trait | <cluster>), reported as sd_q^2).

The denominator is defined at the species level. Other defensible definitions exist — for instance a total-variance denominator that also absorbs site-level variation, which would give a smaller H2 for the same fit. Before 2026-07-08 this function used the unit tier unconditionally, which silently reported H2 = 1 for every trait in a crossed design because the q_it variance was never read. Compare extract_proportions() if you want every component reported separately rather than folded into a species-level ratio.

Examples

if (FALSE) { # \dontrun{
fit <- gllvmTMB(
  value ~ 0 + trait + phylo_latent(species, d = 2) +
                      latent(0 + trait | species, d = 2) +
                      indep(0 + trait | species),
  data       = df,
  trait      = "trait",
  unit       = "species",
  cluster    = "species",
  phylo_tree = tree
)
extract_phylo_signal(fit)
} # }