This document applies bootstrap balanced resampling to assess the robustness of developmental effects in LMM analysis.
In longitudinal studies, animals often contribute different numbers of observations. This can bias population-level estimates toward patterns in well-sampled individuals.
Bootstrap balanced resampling addresses this by:
| Approach | Mechanism | Advantage |
|---|---|---|
| Inverse Weighting | Downweight observations from large clusters | Uses all data |
| Bootstrap Balanced | Subsample to equal n per cluster | Distribution of estimates |
We use inverse-weighted LMM as a benchmark and bootstrap to assess consistency.
| Bootstrap Result | Interpretation |
|---|---|
| 95% CI excludes zero | Robust effect, not driven by sample imbalance |
| 95% CI includes zero | Effect may be unstable or driven by specific animals |
| Same direction as weighted | Converging evidence across methods |
| Opposite direction | Effect driven by dominant individuals |
A developmental effect is considered robust if:
✅ Bootstrap 95% CI excludes zero
✅ Bootstrap slope has same sign as weighted LMM
✅ Bootstrap and weighted estimates are within reasonable range
par(mfrow = c(2, 2))
for (name in names(td_boot$results)) {
slopes <- td_boot$results[[name]]$boot_samples[, "dph"]
hist(slopes, breaks = 30, col = "steelblue", border = "white",
main = name, xlab = "Slope (per day)")
abline(v = 0, col = "gray40", lwd = 2, lty = 2)
abline(v = mean(slopes), col = "red", lwd = 2)
abline(v = quantile(slopes, c(0.025, 0.975)), col = "red", lty = 2)
}
par(mfrow = c(1, 3))
for (name in names(amp_boot$results)) {
slopes <- amp_boot$results[[name]]$boot_samples[, "dph"]
hist(slopes, breaks = 30, col = "darkgreen", border = "white",
main = name, xlab = "Slope (per day)")
abline(v = 0, col = "gray40", lwd = 2, lty = 2)
abline(v = mean(slopes), col = "red", lwd = 2)
abline(v = quantile(slopes, c(0.025, 0.975)), col = "red", lty = 2)
}
par(mfrow = c(1, 3))
for (name in names(cH_boot$results)) {
slopes <- cH_boot$results[[name]]$boot_samples[, "dph"]
hist(slopes, breaks = 30, col = "darkorange", border = "white",
main = name, xlab = "Slope (per day)")
abline(v = 0, col = "gray40", lwd = 2, lty = 2)
abline(v = mean(slopes), col = "red", lwd = 2)
abline(v = quantile(slopes, c(0.025, 0.975)), col = "red", lty = 2)
}
# Add category labels
td_boot$summary$Category <- "TD Metrics"
amp_boot$summary$Category <- "Amplitude"
cH_boot$summary$Category <- "Info Flow"
# Combine
all_summary <- rbind(td_boot$summary, amp_boot$summary, cH_boot$summary)
# Reorder columns
all_summary <- all_summary[, c("Category", "Metric", "Slope", "SE", "CI_Lower", "CI_Upper", "Significant", "Direction")]
knitr::kable(all_summary, caption = "Bootstrap Analysis: All Metrics Summary", row.names = FALSE)
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
## [4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
## [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
## [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
##
## time zone: UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## loaded via a namespace (and not attached):
## [1] digest_0.6.39 R6_2.6.1 fastmap_1.2.0 xfun_0.54
## [5] cachem_1.1.0 knitr_1.50 htmltools_0.5.9 rmarkdown_2.30
## [9] lifecycle_1.0.4 cli_3.6.5 sass_0.4.10 jquerylib_0.1.4
## [13] compiler_4.4.1 tools_4.4.1 evaluate_1.0.5 bslib_0.9.0
## [17] yaml_2.3.12 rlang_1.1.6 jsonlite_2.0.0