Partition total trait variance into within-species (ITV) and between-species (BTV) components. Returns the ITV/total ratio per trait.
itv_btv_ratio <- function(df, sp_col, trait_cols) {
results <- lapply(trait_cols, function(trait) {
x <- df[[trait]]
sp <- df[[sp_col]]
x <- x[!is.na(x)]
sp <- sp[!is.na(df[[trait]])]
total_var <- var(x)
sp_means <- tapply(x, sp, mean, na.rm = TRUE)
btv <- var(sp_means[sp]) # between-species variance
itv <- total_var - btv # within-species variance
data.frame(trait = trait,
ITV = itv,
BTV = btv,
Total = total_var,
ITV_ratio = itv / total_var * 100)
})
do.call(rbind, results)
}
# ── Example ──────────────────────────────────────────────────────
result <- itv_btv_ratio(iris,
sp_col = "Species",
trait_cols = c("Sepal.Length","Sepal.Width",
"Petal.Length","Petal.Width"))
print(result)
# A high ITV_ratio means within-species variation dominates