ecological-agent-skills 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/AGENT_CONTEXT.md +191 -0
  2. package/CATALOG.md +329 -0
  3. package/LICENSE +692 -0
  4. package/README.md +347 -0
  5. package/bin/install.mjs +168 -0
  6. package/docs/comparison-with-alternatives.md +38 -0
  7. package/docs/global-examples-index.md +103 -0
  8. package/docs/repository-statistics.md +101 -0
  9. package/docs/theoretical-foundations.md +188 -0
  10. package/environment.yaml +106 -0
  11. package/examples/community/arctic_tundra_vegetation_example.md +247 -0
  12. package/examples/community/bird_landuse_example.md +63 -0
  13. package/examples/community/phytoplankton_reservoir_example.md +60 -0
  14. package/examples/community/reef_fish_indopacific_example.md +221 -0
  15. package/examples/impact/baci_road_example.md +57 -0
  16. package/examples/impact/ecosystem_services_atlantic_forest.md +83 -0
  17. package/examples/impact/forest_loss_borneo_timeseries_example.md +225 -0
  18. package/examples/occupancy/puma_camera_example.md +61 -0
  19. package/examples/occupancy/snow_leopard_himalayas_example.md +204 -0
  20. package/examples/reproducible/whittaker_biome_sdm_example.md +406 -0
  21. package/examples/sdm/anteater_cerrado_example.md +69 -0
  22. package/examples/sdm/jaguar_amazon_example.md +80 -0
  23. package/examples/sdm/koala_climate_change_example.md +170 -0
  24. package/examples/sdm/wolf_recolonization_europe_example.md +193 -0
  25. package/package.json +43 -0
  26. package/renv.lock +194 -0
  27. package/skills/SKILL_INDEX.json +1020 -0
  28. package/skills/acoustic-monitoring/SKILL.md +163 -0
  29. package/skills/acoustic-monitoring/examples/example-prompts.md +100 -0
  30. package/skills/acoustic-monitoring/examples/temperate_forest_birds_example.md +285 -0
  31. package/skills/acoustic-monitoring/resources/acoustic-indices-reference.md +93 -0
  32. package/skills/acoustic-monitoring/resources/soundscape-ecology-guide.md +90 -0
  33. package/skills/acoustic-monitoring/resources/species-id-tools-comparison.md +89 -0
  34. package/skills/acoustic-monitoring/scripts/batch_species_detection.py +360 -0
  35. package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.R +235 -0
  36. package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.py +374 -0
  37. package/skills/biostatistics-workbench/SKILL.md +140 -0
  38. package/skills/biostatistics-workbench/examples/example-prompts.md +39 -0
  39. package/skills/biostatistics-workbench/resources/effect-size-reference.md +81 -0
  40. package/skills/biostatistics-workbench/resources/glm-family-link-reference.md +47 -0
  41. package/skills/biostatistics-workbench/resources/test-selection-guide.md +93 -0
  42. package/skills/biostatistics-workbench/scripts/glm_pipeline.R +78 -0
  43. package/skills/biostatistics-workbench/scripts/glm_pipeline.py +210 -0
  44. package/skills/camera-trap-processing/SKILL.md +159 -0
  45. package/skills/camera-trap-processing/examples/example-prompts.md +103 -0
  46. package/skills/camera-trap-processing/examples/leopard_serengeti_example.md +231 -0
  47. package/skills/camera-trap-processing/resources/activity-patterns-reference.md +113 -0
  48. package/skills/camera-trap-processing/resources/camtrapR-workflow-guide.md +130 -0
  49. package/skills/camera-trap-processing/resources/detection-event-definition-guide.md +89 -0
  50. package/skills/camera-trap-processing/scripts/estimate_activity.R +169 -0
  51. package/skills/camera-trap-processing/scripts/process_camtrap_data.R +179 -0
  52. package/skills/camera-trap-processing/scripts/process_camtrap_data.py +192 -0
  53. package/skills/community-ecology-ordination/SKILL.md +133 -0
  54. package/skills/community-ecology-ordination/examples/example-prompts.md +35 -0
  55. package/skills/community-ecology-ordination/resources/dissimilarity-metric-guide.md +53 -0
  56. package/skills/community-ecology-ordination/resources/nmds-interpretation-guide.md +104 -0
  57. package/skills/community-ecology-ordination/scripts/__pycache__/community_analysis.cpython-311.pyc +0 -0
  58. package/skills/community-ecology-ordination/scripts/community_analysis.R +143 -0
  59. package/skills/community-ecology-ordination/scripts/community_analysis.py +231 -0
  60. package/skills/ecological-data-foundation/SKILL.md +129 -0
  61. package/skills/ecological-data-foundation/examples/example-prompts.md +40 -0
  62. package/skills/ecological-data-foundation/resources/coordinate-cleaning-flags.md +66 -0
  63. package/skills/ecological-data-foundation/resources/darwin-core-glossary.md +91 -0
  64. package/skills/ecological-data-foundation/resources/data-citation-guide.md +265 -0
  65. package/skills/ecological-data-foundation/resources/gbif-data-citation-guide.md +193 -0
  66. package/skills/ecological-data-foundation/resources/qa-checklist.md +83 -0
  67. package/skills/ecological-data-foundation/scripts/__pycache__/clean_occurrences.cpython-311.pyc +0 -0
  68. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_ebird.cpython-311.pyc +0 -0
  69. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_inat.cpython-311.pyc +0 -0
  70. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_iucn.cpython-311.pyc +0 -0
  71. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_obis.cpython-311.pyc +0 -0
  72. package/skills/ecological-data-foundation/scripts/clean_occurrences.R +230 -0
  73. package/skills/ecological-data-foundation/scripts/clean_occurrences.py +268 -0
  74. package/skills/ecological-data-foundation/scripts/download_from_ebird.R +251 -0
  75. package/skills/ecological-data-foundation/scripts/download_from_ebird.py +364 -0
  76. package/skills/ecological-data-foundation/scripts/download_from_gbif.R +315 -0
  77. package/skills/ecological-data-foundation/scripts/download_from_gbif.py +407 -0
  78. package/skills/ecological-data-foundation/scripts/download_from_inat.R +238 -0
  79. package/skills/ecological-data-foundation/scripts/download_from_inat.py +304 -0
  80. package/skills/ecological-data-foundation/scripts/download_from_iucn.R +273 -0
  81. package/skills/ecological-data-foundation/scripts/download_from_iucn.py +344 -0
  82. package/skills/ecological-data-foundation/scripts/download_from_obis.R +248 -0
  83. package/skills/ecological-data-foundation/scripts/download_from_obis.py +318 -0
  84. package/skills/ecological-impact-assessment/SKILL.md +123 -0
  85. package/skills/ecological-impact-assessment/examples/example-prompts.md +32 -0
  86. package/skills/ecological-impact-assessment/resources/baci-design-guide.md +55 -0
  87. package/skills/ecological-impact-assessment/resources/fragmentation-metrics-reference.md +86 -0
  88. package/skills/ecological-impact-assessment/resources/pressure-index-template.md +78 -0
  89. package/skills/ecological-impact-assessment/resources/study-design-guide.md +168 -0
  90. package/skills/ecological-impact-assessment/scripts/baci_analysis.R +161 -0
  91. package/skills/ecological-impact-assessment/scripts/fragmentation_analysis.py +141 -0
  92. package/skills/ecological-impact-assessment/scripts/power_analysis_baci.R +274 -0
  93. package/skills/ecosystem-services-assessment/SKILL.md +125 -0
  94. package/skills/ecosystem-services-assessment/examples/example-prompts.md +24 -0
  95. package/skills/ecosystem-services-assessment/resources/es-indicator-reference.md +45 -0
  96. package/skills/ecosystem-services-assessment/resources/invest-parameter-guide.md +86 -0
  97. package/skills/ecosystem-services-assessment/resources/rusle-coefficients.md +88 -0
  98. package/skills/ecosystem-services-assessment/scripts/__pycache__/compute_es.cpython-311.pyc +0 -0
  99. package/skills/ecosystem-services-assessment/scripts/compute_es.py +189 -0
  100. package/skills/ecosystem-services-assessment/scripts/tradeoff_analysis.R +161 -0
  101. package/skills/environmental-time-series/SKILL.md +125 -0
  102. package/skills/environmental-time-series/examples/example-prompts.md +33 -0
  103. package/skills/environmental-time-series/resources/anomaly-indices-reference.md +88 -0
  104. package/skills/environmental-time-series/resources/bfast-parameter-guide.md +69 -0
  105. package/skills/environmental-time-series/scripts/__pycache__/recovery_trajectory.cpython-311.pyc +0 -0
  106. package/skills/environmental-time-series/scripts/__pycache__/trend_analysis.cpython-311.pyc +0 -0
  107. package/skills/environmental-time-series/scripts/recovery_trajectory.R +305 -0
  108. package/skills/environmental-time-series/scripts/recovery_trajectory.py +178 -0
  109. package/skills/environmental-time-series/scripts/trend_analysis.R +192 -0
  110. package/skills/environmental-time-series/scripts/trend_analysis.py +184 -0
  111. package/skills/geoprocessing-for-ecology/SKILL.md +123 -0
  112. package/skills/geoprocessing-for-ecology/examples/example-prompts.md +32 -0
  113. package/skills/geoprocessing-for-ecology/resources/crs-reference.md +62 -0
  114. package/skills/geoprocessing-for-ecology/resources/global-predictor-sources.md +331 -0
  115. package/skills/geoprocessing-for-ecology/resources/resampling-methods.md +57 -0
  116. package/skills/geoprocessing-for-ecology/scripts/__pycache__/download_predictors.cpython-311.pyc +0 -0
  117. package/skills/geoprocessing-for-ecology/scripts/download_predictors.R +239 -0
  118. package/skills/geoprocessing-for-ecology/scripts/download_predictors.py +379 -0
  119. package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.R +224 -0
  120. package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.py +172 -0
  121. package/skills/landscape-connectivity/SKILL.md +170 -0
  122. package/skills/landscape-connectivity/examples/example-prompts.md +96 -0
  123. package/skills/landscape-connectivity/examples/jaguar_mesoamerica_corridor_example.md +271 -0
  124. package/skills/landscape-connectivity/resources/circuitscape-parameter-guide.md +155 -0
  125. package/skills/landscape-connectivity/resources/graph-theory-for-ecology.md +134 -0
  126. package/skills/landscape-connectivity/resources/resistance-surface-guide.md +141 -0
  127. package/skills/landscape-connectivity/scripts/connectivity_analysis.py +387 -0
  128. package/skills/landscape-connectivity/scripts/connectivity_metrics.R +274 -0
  129. package/skills/landscape-connectivity/scripts/resistance_surface.R +239 -0
  130. package/skills/model-validation-and-uncertainty/SKILL.md +131 -0
  131. package/skills/model-validation-and-uncertainty/examples/example-prompts.md +30 -0
  132. package/skills/model-validation-and-uncertainty/resources/extrapolation-risk-guide.md +236 -0
  133. package/skills/model-validation-and-uncertainty/resources/metric-selection-guide.md +52 -0
  134. package/skills/model-validation-and-uncertainty/resources/threshold-selection-guide.md +64 -0
  135. package/skills/model-validation-and-uncertainty/scripts/__pycache__/validate_model.cpython-311.pyc +0 -0
  136. package/skills/model-validation-and-uncertainty/scripts/extrapolation_risk.R +315 -0
  137. package/skills/model-validation-and-uncertainty/scripts/validate_model.py +226 -0
  138. package/skills/model-validation-and-uncertainty/scripts/validate_sdm.R +162 -0
  139. package/skills/occupancy-and-detection/SKILL.md +126 -0
  140. package/skills/occupancy-and-detection/examples/example-prompts.md +33 -0
  141. package/skills/occupancy-and-detection/resources/detection-history-format.md +100 -0
  142. package/skills/occupancy-and-detection/resources/occupancy-study-design.md +47 -0
  143. package/skills/occupancy-and-detection/scripts/__pycache__/occupancy_analysis.cpython-311.pyc +0 -0
  144. package/skills/occupancy-and-detection/scripts/occupancy_analysis.R +160 -0
  145. package/skills/occupancy-and-detection/scripts/occupancy_analysis.py +159 -0
  146. package/skills/population-viability-analysis/SKILL.md +161 -0
  147. package/skills/population-viability-analysis/examples/african_elephant_pva_example.md +266 -0
  148. package/skills/population-viability-analysis/examples/example-prompts.md +95 -0
  149. package/skills/population-viability-analysis/resources/extinction-risk-thresholds.md +128 -0
  150. package/skills/population-viability-analysis/resources/matrix-model-guide.md +139 -0
  151. package/skills/population-viability-analysis/resources/sensitivity-elasticity-reference.md +182 -0
  152. package/skills/population-viability-analysis/scripts/matrix_pva.R +258 -0
  153. package/skills/population-viability-analysis/scripts/pva_analysis.py +442 -0
  154. package/skills/population-viability-analysis/scripts/stochastic_pva.R +353 -0
  155. package/skills/predictive-modeling-best-practices/SKILL.md +136 -0
  156. package/skills/predictive-modeling-best-practices/examples/example-prompts.md +58 -0
  157. package/skills/predictive-modeling-best-practices/resources/collinearity-decision-tree.md +65 -0
  158. package/skills/predictive-modeling-best-practices/resources/sampling-bias-correction.md +267 -0
  159. package/skills/predictive-modeling-best-practices/resources/spatial-cv-guide.md +73 -0
  160. package/skills/predictive-modeling-best-practices/scripts/__pycache__/spatial_cv.cpython-311.pyc +0 -0
  161. package/skills/predictive-modeling-best-practices/scripts/collinearity_check.R +112 -0
  162. package/skills/predictive-modeling-best-practices/scripts/spatial_cv.py +182 -0
  163. package/skills/reproducible-ecology-pipeline/SKILL.md +139 -0
  164. package/skills/reproducible-ecology-pipeline/examples/example-prompts.md +35 -0
  165. package/skills/reproducible-ecology-pipeline/resources/directory-structure-template.md +94 -0
  166. package/skills/reproducible-ecology-pipeline/resources/params-yaml-template.yaml +84 -0
  167. package/skills/reproducible-ecology-pipeline/resources/reproducibility-checklist-template.md +66 -0
  168. package/skills/reproducible-ecology-pipeline/scripts/generate_file_manifest.py +110 -0
  169. package/skills/reproducible-ecology-pipeline/scripts/init_project.sh +53 -0
  170. package/skills/spatial-prioritization/SKILL.md +162 -0
  171. package/skills/spatial-prioritization/examples/biodiversity_hotspot_prioritization_example.md +289 -0
  172. package/skills/spatial-prioritization/examples/example-prompts.md +93 -0
  173. package/skills/spatial-prioritization/resources/cost-surface-reference.md +130 -0
  174. package/skills/spatial-prioritization/resources/marxan-vs-prioritizr-comparison.md +125 -0
  175. package/skills/spatial-prioritization/resources/prioritizr-formulation-guide.md +188 -0
  176. package/skills/spatial-prioritization/resources/representation-targets-guide.md +186 -0
  177. package/skills/spatial-prioritization/scripts/prioritization_sensitivity.R +320 -0
  178. package/skills/spatial-prioritization/scripts/run_prioritization.R +336 -0
  179. package/skills/species-distribution-modeling/SKILL.md +139 -0
  180. package/skills/species-distribution-modeling/examples/example-prompts.md +36 -0
  181. package/skills/species-distribution-modeling/resources/algorithm-comparison.md +25 -0
  182. package/skills/species-distribution-modeling/resources/calibration-area-guide.md +71 -0
  183. package/skills/species-distribution-modeling/resources/climate-scenario-preparation.md +170 -0
  184. package/skills/species-distribution-modeling/resources/maxent-calibration-guide.md +211 -0
  185. package/skills/species-distribution-modeling/resources/sdm-checklist.md +37 -0
  186. package/skills/species-distribution-modeling/scripts/predict_distribution.R +236 -0
  187. package/skills/species-distribution-modeling/scripts/predict_distribution.py +286 -0
  188. package/skills/species-distribution-modeling/scripts/prepare_future_layers.R +351 -0
  189. package/skills/species-distribution-modeling/scripts/project_scenarios.R +220 -0
  190. package/skills/species-distribution-modeling/scripts/run_ensemble_sdm.R +99 -0
  191. package/skills/species-distribution-modeling/scripts/sdm_pipeline.py +318 -0
  192. package/skills/species-distribution-modeling/scripts/tune_maxnet.R +344 -0
  193. package/templates/SKILL_TEMPLATE.md +225 -0
  194. package/templates/checklists/data-submission-checklist.md +38 -0
  195. package/templates/checklists/post-analysis-checklist.md +55 -0
  196. package/templates/checklists/pre-analysis-checklist.md +31 -0
  197. package/templates/prompts/debug-skill.md +47 -0
  198. package/templates/prompts/invoke-skill.md +34 -0
  199. package/templates/prompts/invoke-workflow.md +45 -0
  200. package/templates/reports/technical-report-template.md +80 -0
  201. package/templates/scripts/logger_setup.R +79 -0
  202. package/templates/scripts/logger_setup.py +119 -0
  203. package/templates/scripts/params_loader.R +28 -0
  204. package/templates/scripts/params_loader.py +38 -0
  205. package/workflows/analyze-community-structure/WORKFLOW.md +72 -0
  206. package/workflows/analyze-environmental-change/WORKFLOW.md +73 -0
  207. package/workflows/assess-ecological-impact/WORKFLOW.md +75 -0
  208. package/workflows/assess-ecosystem-services/WORKFLOW.md +68 -0
  209. package/workflows/assess-landscape-connectivity/WORKFLOW.md +84 -0
  210. package/workflows/build-fire-risk-map/WORKFLOW.md +79 -0
  211. package/workflows/produce-technical-report/WORKFLOW.md +113 -0
  212. package/workflows/run-camera-trap-occupancy/WORKFLOW.md +87 -0
  213. package/workflows/run-conservation-prioritization/WORKFLOW.md +89 -0
  214. package/workflows/run-multispecies-screening/WORKFLOW.md +197 -0
  215. package/workflows/run-occupancy-analysis/WORKFLOW.md +74 -0
  216. package/workflows/run-population-viability/WORKFLOW.md +90 -0
  217. package/workflows/run-sdm-study/WORKFLOW.md +99 -0
@@ -0,0 +1,182 @@
1
+ ---
2
+ resource_id: sensitivity-elasticity-reference
3
+ skill_id: population-viability-analysis
4
+ ---
5
+
6
+ # Sensitivity and Elasticity Analysis Reference
7
+
8
+ ## Definitions
9
+
10
+ | Term | Symbol | Formula | Units |
11
+ |------|--------|---------|-------|
12
+ | Sensitivity | S_ij | ∂λ/∂a_ij | λ per matrix element unit |
13
+ | Elasticity | E_ij | (a_ij/λ) × ∂λ/∂a_ij | dimensionless proportion |
14
+ | Lower-level sensitivity | ∂λ/∂θ | chain rule via vital rates | λ per vital rate unit |
15
+ | LTRE contribution | c_ij | (ā_ij_treated - ā_ij_control) × s_ij | Change in λ per treatment |
16
+
17
+ ---
18
+
19
+ ## Life Table Response Experiment (LTRE)
20
+
21
+ LTRE partitions the observed difference in λ between two treatments (or populations) into contributions from each matrix element:
22
+
23
+ ```r
24
+ suppressPackageStartupMessages(library(popbio))
25
+
26
+ # Two population matrices
27
+ A_control <- matrix(c(0, 0, 1.2, 0.25, 0.55, 0, 0, 0.60, 0.85),
28
+ nrow = 3, byrow = TRUE)
29
+ A_treat <- matrix(c(0, 0, 0.9, 0.20, 0.50, 0, 0, 0.55, 0.80),
30
+ nrow = 3, byrow = TRUE)
31
+
32
+ lambda_c <- lambda(A_control)
33
+ lambda_t <- lambda(A_treat)
34
+ cat(sprintf("λ control = %.4f, λ treatment = %.4f, Δλ = %.4f\n",
35
+ lambda_c, lambda_t, lambda_t - lambda_c))
36
+
37
+ # Midpoint matrix and its sensitivity
38
+ A_mid <- (A_control + A_treat) / 2
39
+ S_mid <- sensitivity(A_mid)
40
+
41
+ # LTRE contributions
42
+ LTRE_contrib <- (A_treat - A_control) * S_mid
43
+ cat("LTRE contributions:\n"); print(round(LTRE_contrib, 4))
44
+ cat(sprintf("Sum of contributions: %.4f (should ≈ Δλ = %.4f)\n",
45
+ sum(LTRE_contrib), lambda_t - lambda_c))
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Prospective vs Retrospective Analysis
51
+
52
+ | Analysis type | Question answered | Method |
53
+ |---------------|------------------|--------|
54
+ | Prospective (sensitivity/elasticity) | Which vital rates, if changed, would most change λ? | Eigenvalue derivatives |
55
+ | Retrospective (LTRE) | Which vital rates actually caused observed λ differences? | Observed differences × sensitivities |
56
+
57
+ **Key distinction:** A vital rate may have high elasticity (prospective) but contribute little to observed population differences (retrospective) if it varies little among populations or years.
58
+
59
+ ---
60
+
61
+ ## Stochastic Sensitivity (Stochastic Elasticity)
62
+
63
+ When vital rates vary stochastically, use stochastic sensitivity (Tuljapurkar's approach):
64
+
65
+ ```r
66
+ # Stochastic elasticity via simulation
67
+ compute_stoch_elasticity <- function(A_list, n_sim = 10000, delta = 0.001) {
68
+ # A_list: list of annual matrices (environmental variation)
69
+ # Returns element-wise stochastic elasticity
70
+
71
+ log_lambda_s <- function(A_lst) {
72
+ N <- 100
73
+ n <- rep(1, nrow(A_lst[[1]]))
74
+ for (t in seq_len(N)) {
75
+ A_t <- A_lst[[sample(length(A_lst), 1)]]
76
+ n <- A_t %*% n
77
+ }
78
+ for (t in seq_len(n_sim)) {
79
+ A_t <- A_lst[[sample(length(A_lst), 1)]]
80
+ n <- A_t %*% n
81
+ }
82
+ log(sum(n)) / n_sim
83
+ }
84
+
85
+ lls_base <- log_lambda_s(A_list)
86
+ nr <- nrow(A_list[[1]])
87
+ E_stoch <- matrix(0, nr, nr)
88
+
89
+ for (i in seq_len(nr)) {
90
+ for (j in seq_len(nr)) {
91
+ A_perturbed <- lapply(A_list, function(A) {
92
+ A_p <- A
93
+ A_p[i, j] <- A_p[i, j] + delta
94
+ A_p
95
+ })
96
+ lls_pert <- log_lambda_s(A_perturbed)
97
+ E_stoch[i, j] <- (lls_pert - lls_base) / delta *
98
+ mean(sapply(A_list, function(A) A[i, j]))
99
+ }
100
+ }
101
+ E_stoch
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Common Elasticity Patterns by Taxon
108
+
109
+ | Taxon group | Typical dominant elasticity | Implication |
110
+ |-------------|---------------------------|-------------|
111
+ | Annual plants | Seed survival, germination, seedling survival | Protect seedling stage |
112
+ | Long-lived trees | Adult survival (Sa >> 0.95) | Any adult mortality has high λ cost |
113
+ | Large mammals (elephant, rhino) | Adult female survival (e > 0.6) | Protect adult females absolutely |
114
+ | Sea turtles | Juvenile/subadult survival | Bycatch reduction critical |
115
+ | Songbirds | Adult survival + fecundity | Both vital rates important |
116
+ | Amphibians | Egg/larval survival | Breeding habitat quality critical |
117
+
118
+ ---
119
+
120
+ ## Management Implications of Elasticity
121
+
122
+ ```r
123
+ E <- elasticity(A)
124
+
125
+ # Which life stage should managers prioritise?
126
+ stage_names <- c("Juvenile", "Subadult", "Adult")
127
+
128
+ # Column sums of E = elasticity of λ to survival/growth from each stage
129
+ col_sums <- colSums(E)
130
+ names(col_sums) <- stage_names
131
+ cat("Elasticity to transitions FROM each stage:\n")
132
+ print(round(col_sums, 4))
133
+
134
+ # Row sums = elasticity of λ to transitions INTO each stage
135
+ row_sums <- rowSums(E)
136
+ names(row_sums) <- stage_names
137
+ cat("Elasticity to transitions INTO each stage:\n")
138
+ print(round(row_sums, 4))
139
+
140
+ # Fecundity vs survival tradeoff
141
+ fec_el <- sum(E[1, ]) # fecundity row
142
+ surv_el <- sum(E[-1, ]) # survival rows
143
+ cat(sprintf("Fecundity elasticity total: %.3f\n", fec_el))
144
+ cat(sprintf("Survival elasticity total: %.3f\n", surv_el))
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Uncertainty Propagation in Sensitivity Analysis
150
+
151
+ ```r
152
+ # Bootstrap confidence intervals on elasticities
153
+ # Assumes you have multiple annual matrices
154
+ bootstrap_elasticity <- function(A_list, n_boot = 1000) {
155
+ boot_E <- replicate(n_boot, {
156
+ A_boot <- Reduce("+", sample(A_list, length(A_list), replace = TRUE)) /
157
+ length(A_list)
158
+ elasticity(A_boot)
159
+ }, simplify = FALSE)
160
+ # 95% CI for each element
161
+ E_lower <- apply(simplify2array(boot_E), c(1, 2), quantile, 0.025)
162
+ E_upper <- apply(simplify2array(boot_E), c(1, 2), quantile, 0.975)
163
+ list(lower = E_lower, upper = E_upper)
164
+ }
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Pitfalls
170
+
171
+ - **Reporting sensitivities for fecundity and survival on same scale:** Fecundity is unbounded (can be 0, 1, 10, 100 offspring) while survival is bounded [0, 1]. Sensitivities are not directly comparable; always interpret elasticities (proportional) for cross-element comparison.
172
+ - **Interpreting elasticity as management priority without feasibility:** High elasticity on adult survival does not mean managers CAN improve adult survival. Always overlay feasibility (budget, methods, political will).
173
+ - **LTRE with non-additive contributions:** When A_control and A_treat differ by large amounts, the linear midpoint approximation breaks down. Use fixed LTRE or random LTRE designs for large differences.
174
+ - **Elasticity changes with environmental context:** Elasticities are properties of the matrix, which changes with environment. Report elasticities for the average matrix AND across observed range of environmental variation.
175
+
176
+ ---
177
+
178
+ ## References
179
+
180
+ - de Kroon, H., van Groenendael, J. & Ehrlén, J. (2000). Elasticities: a review of methods and model limitations. *Ecology*, 81(3), 607–618. DOI: 10.1890/0012-9658(2000)081[0607:EAROMA]2.0.CO;2
181
+ - Caswell, H. (2000). Prospective and retrospective perturbation analyses. *Ecology*, 81(3), 619–627. DOI: 10.1890/0012-9658(2000)081[0619:PARPA]2.0.CO;2
182
+ - Tuljapurkar, S., Horvitz, C.C. & Pascarella, J.B. (2003). The many growth rates and elasticities of populations in random environments. *American Naturalist*, 162(4), 489–502. DOI: 10.1086/378648
@@ -0,0 +1,258 @@
1
+ # ecological-agent-skills / Copyright (C) 2026 Francisco Diego Barros Barata
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+
4
+ # Usage: Rscript matrix_pva.R <vital_rates_csv> <output_dir> [n_init] [t_max] [quasi_ext]
5
+ #
6
+ # Deterministic and stochastic population viability analysis using
7
+ # stage-structured matrix models (Leslie or Lefkovitch).
8
+ #
9
+ # Arguments:
10
+ # vital_rates_csv — CSV with vital rate time series:
11
+ # columns: year, stage_i_to_j (survival/growth),
12
+ # fecundity_i (fecundity), population_N (total census count)
13
+ # output_dir — Directory for output files
14
+ # n_init — Initial population size (default: from vital_rates_csv last year N)
15
+ # t_max — Time horizon in years (default: 100)
16
+ # quasi_ext — Quasi-extinction threshold in individuals (default: 50)
17
+ #
18
+ # Alternatively, provide a matrix directly via --matrix_csv argument (rows=cols of A)
19
+ #
20
+ # Outputs:
21
+ # lambda_summary.csv — λ, sensitivity, elasticity matrices
22
+ # stable_stage.csv — Stable stage distribution
23
+ # pva_trajectories.png — Deterministic projection plot
24
+ # elasticity_heatmap.png — Elasticity visualisation
25
+
26
+ # ── Inline logger ─────────────────────────────────────────────────────────────
27
+ SKILL_NAME <- "population-viability-analysis"
28
+ .log_ts <- function() format(Sys.time(), "[%Y-%m-%d %H:%M:%S]")
29
+ log_info <- function(...) message(.log_ts(), " [INFO] ", sprintf(...))
30
+ log_warn <- function(...) message(.log_ts(), " [WARN] ", sprintf(...))
31
+ log_error<- function(...) message(.log_ts(), " [ERROR] ", sprintf(...))
32
+ log_step <- function(n, d) log_info("-- STEP %d: %s", n, d)
33
+ log_decision <- function(v, val, why) log_info("DECISION | %s = %s | %s", v, val, why)
34
+ dir.create("logs", recursive=TRUE, showWarnings=FALSE)
35
+
36
+ suppressPackageStartupMessages(library(popbio))
37
+ suppressPackageStartupMessages(library(dplyr))
38
+ suppressPackageStartupMessages(library(ggplot2))
39
+ suppressPackageStartupMessages(library(tidyr))
40
+
41
+ args <- commandArgs(trailingOnly = TRUE)
42
+ if (length(args) < 2) {
43
+ cat("Usage: Rscript matrix_pva.R <vital_rates_csv> <output_dir>",
44
+ "[n_init] [t_max] [quasi_ext]\n")
45
+ quit(status = 1)
46
+ }
47
+
48
+ vr_path <- args[1]
49
+ output_dir <- args[2]
50
+ n_init <- if (length(args) >= 3) as.integer(args[3]) else NA_integer_
51
+ t_max <- if (length(args) >= 4) as.integer(args[4]) else 100L
52
+ quasi_ext <- if (length(args) >= 5) as.numeric(args[5]) else 50
53
+
54
+ # ── Input precondition checks ─────────────────────────────────────────────────
55
+ if (!file.exists(vr_path)) {
56
+ log_error("Input nao encontrado: %s\nCausa provavel: passo anterior nao concluiu.\nVerifique: outputs do skill anterior.\nSkill anterior: species-distribution-modeling", vr_path)
57
+ stop("Missing input: ", vr_path)
58
+ }
59
+
60
+ log_decision("t_max", t_max, "Time horizon in years for population projection")
61
+ log_decision("quasi_ext", quasi_ext, "Quasi-extinction threshold in individuals (IUCN-based)")
62
+ log_decision("n_init", ifelse(is.na(n_init), "from_data", n_init), "Initial population size; NA means read from vital_rates_csv last year")
63
+
64
+ dir.create(output_dir, recursive = TRUE, showWarnings = FALSE)
65
+
66
+ # ── Load vital rates ─────────────────────────────────────────────────────────
67
+ log_step(1, "Load vital rates CSV")
68
+ tryCatch({
69
+ vr <- read.csv(vr_path)
70
+ log_info("Loaded vital rates: %d rows, %d columns.", nrow(vr), ncol(vr))
71
+ }, error = function(e) {
72
+ log_error("Falha em load_vital_rates: %s\nCausa provavel: arquivo CSV malformado ou permissoes de leitura.\nVerifique: formato do CSV e caminho correto.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
73
+ stop(e)
74
+ })
75
+
76
+ # Detect matrix structure from column names
77
+ # Expected pattern: a_i_j where i = row, j = col (1-indexed)
78
+ log_step(2, "Detect matrix structure from column names")
79
+ mat_cols <- grep("^a_[0-9]+_[0-9]+$", names(vr), value = TRUE)
80
+
81
+ if (length(mat_cols) == 0) {
82
+ log_error("Nenhuma coluna de elemento de matriz encontrada.\nCausa provavel: CSV nao tem colunas no padrao a_i_j.\nVerifique: nomes das colunas do arquivo de taxas vitais.")
83
+ stop("No matrix element columns found. Columns should be named a_1_1, a_1_2, ...")
84
+ }
85
+
86
+ # Determine matrix dimension
87
+ indices <- regmatches(mat_cols, gregexpr("[0-9]+", mat_cols))
88
+ max_idx <- max(sapply(indices, function(x) max(as.integer(x))))
89
+ k <- max_idx # matrix dimension k×k
90
+
91
+ log_info("Matrix dimension: %d x %d", k, k)
92
+ log_decision("k", k, "Matrix dimension inferred from max index in column names")
93
+
94
+ # Build mean matrix (average across years)
95
+ log_step(3, "Build mean matrix averaged across years")
96
+ tryCatch({
97
+ A_mean <- matrix(0, k, k)
98
+ for (col in mat_cols) {
99
+ idx <- as.integer(regmatches(col, gregexpr("[0-9]+", col))[[1]])
100
+ i <- idx[1]; j <- idx[2]
101
+ A_mean[i, j] <- mean(vr[[col]], na.rm = TRUE)
102
+ }
103
+ log_info("Mean matrix A built successfully.")
104
+ log_info("Mean matrix A:\n%s", paste(capture.output(print(round(A_mean, 4))), collapse = "\n"))
105
+ }, error = function(e) {
106
+ log_error("Falha em build_mean_matrix: %s\nCausa provavel: indices de coluna inconsistentes ou valores NA excessivos.\nVerifique: integridade dos dados de taxas vitais.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
107
+ stop(e)
108
+ })
109
+
110
+ # ── Deterministic analysis ────────────────────────────────────────────────────
111
+ log_step(4, "Deterministic analysis: lambda, sensitivity, elasticity")
112
+ tryCatch({
113
+ lambda_val <- lambda(A_mean)
114
+ log_info("lambda (dominant eigenvalue) = %.4f", lambda_val)
115
+
116
+ if (lambda_val < 0.95) {
117
+ log_warn("lambda < 0.95 (%.4f) — populacao em declinio rapido. Recomenda-se PVA estocastico e calculo de MTE.", lambda_val)
118
+ } else if (lambda_val < 1.0) {
119
+ log_warn("lambda < 1.0 (%.4f) — populacao em declinio (pode ser lento).", lambda_val)
120
+ }
121
+
122
+ SS <- stable.stage(A_mean)
123
+ RV <- reproductive.value(A_mean)
124
+ S <- sensitivity(A_mean)
125
+ E <- elasticity(A_mean)
126
+
127
+ log_info("Stable stage distribution: %s", paste(round(SS, 3), collapse = " "))
128
+ log_info("Reproductive value: %s", paste(round(RV, 3), collapse = " "))
129
+ log_info("Sum of elasticities = %.4f", sum(E))
130
+ }, error = function(e) {
131
+ log_error("Falha em deterministic_analysis: %s\nCausa provavel: matriz singular ou eigenvalor complexo.\nVerifique: estrutura da matriz de transicao e taxas vitais.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
132
+ stop(e)
133
+ })
134
+
135
+ # ── Write λ summary CSV ───────────────────────────────────────────────────────
136
+ log_step(5, "Write lambda summary and sensitivity/elasticity CSVs")
137
+ tryCatch({
138
+ lam_df <- data.frame(
139
+ metric = c("lambda", "log_lambda", "doubling_time_yr", "halving_time_yr"),
140
+ value = c(
141
+ lambda_val,
142
+ log(lambda_val),
143
+ ifelse(lambda_val > 1, log(2) / log(lambda_val), NA),
144
+ ifelse(lambda_val < 1, log(0.5) / log(lambda_val), NA)
145
+ )
146
+ )
147
+ write.csv(lam_df, file.path(output_dir, "lambda_summary.csv"), row.names = FALSE)
148
+
149
+ # Write stable stage
150
+ ss_df <- data.frame(stage = seq_len(k), proportion = round(SS, 4),
151
+ repro_value = round(RV, 4))
152
+ write.csv(ss_df, file.path(output_dir, "stable_stage.csv"), row.names = FALSE)
153
+
154
+ # Write sensitivity and elasticity (long format)
155
+ make_long_mat <- function(mat, name) {
156
+ as.data.frame(as.table(mat)) %>%
157
+ rename(row_stage = Var1, col_stage = Var2, !!name := Freq)
158
+ }
159
+ SE_df <- left_join(make_long_mat(S, "sensitivity"),
160
+ make_long_mat(E, "elasticity"),
161
+ by = c("row_stage", "col_stage"))
162
+ SE_df$row_stage <- as.integer(SE_df$row_stage)
163
+ SE_df$col_stage <- as.integer(SE_df$col_stage)
164
+ write.csv(SE_df, file.path(output_dir, "sensitivity_elasticity.csv"), row.names = FALSE)
165
+ log_info("Sensitivity/elasticity written.")
166
+ }, error = function(e) {
167
+ log_error("Falha em write_lambda_summary: %s\nCausa provavel: permissoes de escrita ou diretorio de saida inexistente.\nVerifique: output_dir e permissoes do sistema de arquivos.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
168
+ stop(e)
169
+ })
170
+
171
+ # ── Deterministic projection ──────────────────────────────────────────────────
172
+ log_step(6, "Deterministic population projection over t_max years")
173
+ tryCatch({
174
+ n0 <- if (!is.na(n_init)) n_init else {
175
+ if ("population_N" %in% names(vr)) as.integer(tail(vr$population_N, 1))
176
+ else 1000L
177
+ }
178
+ log_decision("n0", n0, "Initial N for projection; from CLI arg or last year in data or default 1000")
179
+
180
+ # Stage distribution proportional to stable stage
181
+ n_vec <- round(n0 * SS)
182
+
183
+ proj_df <- data.frame(time = 0, total_N = sum(n_vec))
184
+ n_cur <- n_vec
185
+ for (t in seq_len(t_max)) {
186
+ n_cur <- A_mean %*% n_cur
187
+ proj_df <- rbind(proj_df, data.frame(time = t, total_N = sum(n_cur)))
188
+ }
189
+
190
+ p_proj <- ggplot(proj_df, aes(x = time, y = total_N)) +
191
+ geom_line(linewidth = 1.2, colour = "#2166AC") +
192
+ geom_hline(yintercept = quasi_ext, linetype = "dashed", colour = "red") +
193
+ annotate("text", x = t_max * 0.9, y = quasi_ext * 1.2,
194
+ label = sprintf("Ne = %g", quasi_ext), colour = "red", size = 3) +
195
+ labs(x = "Time (years)", y = "Population size (N)",
196
+ title = sprintf("Deterministic projection (lambda = %.4f, N0 = %d)", lambda_val, n0)) +
197
+ scale_y_continuous(labels = scales::comma) +
198
+ theme_minimal(base_size = 11)
199
+
200
+ ggsave(file.path(output_dir, "pva_trajectories.png"), p_proj,
201
+ width = 8, height = 5, dpi = 150)
202
+ log_info("Projection plot saved.")
203
+ }, error = function(e) {
204
+ log_error("Falha em deterministic_projection: %s\nCausa provavel: n_init invalido ou erro na multiplicacao de matrizes.\nVerifique: dimensao da matriz e valor de n_init.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
205
+ stop(e)
206
+ })
207
+
208
+ # ── Elasticity heatmap ────────────────────────────────────────────────────────
209
+ log_step(7, "Generate elasticity heatmap")
210
+ tryCatch({
211
+ stage_labels <- paste0("Stage ", seq_len(k))
212
+ E_df <- expand.grid(Row = seq_len(k), Col = seq_len(k))
213
+ E_df$Elasticity <- as.vector(E)
214
+
215
+ p_el <- ggplot(E_df, aes(x = Col, y = Row, fill = Elasticity)) +
216
+ geom_tile(colour = "white") +
217
+ geom_text(aes(label = round(Elasticity, 3)), size = 3) +
218
+ scale_fill_gradient2(low = "white", high = "steelblue", mid = "lightblue",
219
+ midpoint = max(E) / 2) +
220
+ scale_x_continuous(breaks = seq_len(k), labels = stage_labels) +
221
+ scale_y_reverse(breaks = seq_len(k), labels = stage_labels) +
222
+ labs(x = "From stage (column)", y = "To stage (row)",
223
+ fill = "Elasticity",
224
+ title = "Elasticity matrix") +
225
+ theme_minimal(base_size = 10)
226
+
227
+ ggsave(file.path(output_dir, "elasticity_heatmap.png"), p_el,
228
+ width = 5, height = 4.5, dpi = 150)
229
+ log_info("Elasticity heatmap saved.")
230
+ }, error = function(e) {
231
+ log_error("Falha em elasticity_heatmap: %s\nCausa provavel: erro no ggplot2 ou permissoes de escrita.\nVerifique: instalacao do ggplot2 e output_dir.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
232
+ stop(e)
233
+ })
234
+
235
+ # ── CV check ─────────────────────────────────────────────────────────────────
236
+ log_step(8, "Compute vital rate coefficients of variation")
237
+ tryCatch({
238
+ cv_df <- data.frame(element = mat_cols)
239
+ cv_df$mean_val <- sapply(mat_cols, function(c) mean(vr[[c]], na.rm = TRUE))
240
+ cv_df$sd_val <- sapply(mat_cols, function(c) sd(vr[[c]], na.rm = TRUE))
241
+ cv_df$CV <- cv_df$sd_val / cv_df$mean_val
242
+ high_cv <- cv_df[!is.na(cv_df$CV) & cv_df$CV > 0.30, ]
243
+
244
+ log_info("Vital rate coefficients of variation computed for %d elements.", nrow(cv_df))
245
+
246
+ if (nrow(high_cv) > 0) {
247
+ log_warn("Elementos com CV alto (> 0.30) detectados (%d) — PVA estocastico recomendado: %s",
248
+ nrow(high_cv), paste(high_cv$element, collapse = ", "))
249
+ }
250
+
251
+ write.csv(cv_df, file.path(output_dir, "vital_rate_cv.csv"), row.names = FALSE)
252
+ log_info("Vital rate CV table written.")
253
+ }, error = function(e) {
254
+ log_error("Falha em cv_check: %s\nCausa provavel: serie temporal muito curta ou valores NA excessivos.\nVerifique: numero de anos no CSV de taxas vitais.\nSkill anterior: species-distribution-modeling", conditionMessage(e))
255
+ stop(e)
256
+ })
257
+
258
+ log_info("Matrix PVA complete.")