Ne_sensitivity_Vk()
Ne_sensitivity_L()
Ne_sensitivity_d()
The previous vignette (NeStage Functions: A User Guide) established that all three Lepanthes orchid species in Puerto Rico have Ne/N ratios far below the short-term inbreeding threshold of Ne ≥ 50, even at census sizes of N = 40 — a large population for these species. Knowing that Ne/N = 0.18 for a particular population tells you the situation is serious. It does not tell you what to do about it.
Sensitivity analysis answers the management question: which parameter, if changed, would increase Ne/N the most? This matters because managers have limited resources and can realistically influence only certain aspects of a population’s biology:
| What a manager can influence | Relevant parameter |
|---|---|
| Reducing harvest or disturbance of reproductive adults | Stage frequency D — more adults |
| Translocating individuals between populations | Census N, D |
| Ex-situ propagation with controlled pollination | Reproductive variance Vk/k̄ |
| Removing dominant clonal genets | Clonal fraction d, Vc/c̄ |
| Improving habitat to extend plant lifespan | Generation time L |
NeStage provides four sensitivity functions, each
sweeping one parameter while holding all others fixed:
| Function | Parameter swept | Applies to |
|---|---|---|
Ne_sensitivity_Vk() |
Sexual reproductive variance Vk/k̄ | Sexual, mixed |
Ne_sensitivity_Vc() |
Clonal reproductive variance Vc/c̄ | Mixed only |
Ne_sensitivity_d() |
Clonal fraction d | Mixed only |
Ne_sensitivity_L() |
Generation time L | All three models |
Each function returns a data frame, a ggplot, and an
Ne_sensitivity object with elasticity statistics from
print().
Raw sensitivity (how much does Ne/N change per unit change in a parameter) is hard to compare across parameters measured in different units. Elasticity standardises by expressing the response as a proportional change:
\[ E = \frac{\partial (N_e/N)}{\partial \theta} \times \frac{\theta_\text{ref}}{(N_e/N)_\text{ref}} \]
An elasticity of −0.4 means a 10% increase in \(\theta\) produces a 4% decrease in Ne/N. Elasticities are dimensionless and directly comparable across parameters and populations.
We use three representative Lepanthes populations from the previous vignette — one from each species — chosen to span the range of Ne/N values observed.
# ---- L. rupestris population 1 (Ne/N = 0.572) ----
MatU_Lrup1 <- matrix(c(
0, 0, 0, 0, 0, 0,
0.738, 0.738, 0, 0, 0, 0,
0, 0, 0.515, 0, 0.076, 0.013,
0, 0.038, 0, 0.777, 0, 0,
0, 0.002, 0.368, 0.011, 0.730, 0.171,
0, 0, 0.037, 0, 0.169, 0.790
), nrow = 6, byrow = TRUE,
dimnames = list(paste0("s", 1:6), paste0("s", 1:6)))
F_Lrup1 <- c(0, 0, 0, 0, 0.120, 0.414) # monthly fecundity
D_Lrup1 <- c(22, 22, 36, 36, 48.5, 48.5)
D_Lrup1 <- D_Lrup1 / sum(D_Lrup1)
# ---- L. rubripetala population 1 (Ne/N = 0.930) ----
MatU_Lrub1 <- matrix(c(
0, 0, 0, 0, 0,
0.667, 0.667, 0, 0, 0,
0, 0, 0.825, 0, 0.227,
0, 0.037, 0, 0.812, 0,
0, 0, 0.137, 0.010, 0.739
), nrow = 5, byrow = TRUE,
dimnames = list(paste0("s", 1:5), paste0("s", 1:5)))
F_Lrub1 <- c(0, 0, 0, 0, 0.043)
N_Lrub1 <- c(9/2, 9/2, 44/2, 44/2, 101)
D_Lrub1 <- N_Lrub1 / sum(N_Lrub1)
# ---- L. rubripetala population 5 (Ne/N = 0.250, lowest in Lrub) ----
MatU_Lrub5 <- matrix(c(
0, 0, 0, 0, 0,
0.667, 0.667, 0, 0, 0,
0, 0, 0.726, 0, 0.250,
0, 0.037, 0, 0.812, 0,
0, 0, 0.237, 0.010, 0.739
), nrow = 5, byrow = TRUE,
dimnames = list(paste0("s", 1:5), paste0("s", 1:5)))
F_Lrub5 <- c(0, 0, 0, 0, 0.067)
N_Lrub5 <- c(52/2, 52/2, 4/2, 4/2, 46)
D_Lrub5 <- N_Lrub5 / sum(N_Lrub5)
# Baseline Ne estimates for reference
Ne_base_Lrup1 <- Ne_sexual_Y2000(
T_mat = MatU_Lrup1, F_vec = F_Lrup1, D = D_Lrup1,
Ne_target = 50, census_N = 40, population = "L. rupestris pop 1"
)
Ne_base_Lrub1 <- Ne_sexual_Y2000(
T_mat = MatU_Lrub1, F_vec = F_Lrub1, D = D_Lrub1,
Ne_target = 50, census_N = 40, population = "L. rubripetala pop 1"
)
Ne_base_Lrub5 <- Ne_sexual_Y2000(
T_mat = MatU_Lrub5, F_vec = F_Lrub5, D = D_Lrub5,
Ne_target = 50, census_N = 40, population = "L. rubripetala pop 5"
)
# Baseline summary
data.frame(
Population = c("L. rupestris pop 1", "L. rubripetala pop 1",
"L. rubripetala pop 5"),
Stages = c(6, 5, 5),
NeN = round(c(Ne_base_Lrup1$NeN, Ne_base_Lrub1$NeN,
Ne_base_Lrub5$NeN), 3),
Ne_at_40 = c(Ne_base_Lrup1$Ne_at_census, Ne_base_Lrub1$Ne_at_census,
Ne_base_Lrub5$Ne_at_census),
Min_N_50 = c(Ne_base_Lrup1$Min_N, Ne_base_Lrub1$Min_N,
Ne_base_Lrub5$Min_N)
) |>
knitr::kable(
col.names = c("Population", "Stages", "Ne/N",
"Ne at N=40", "Min N (Ne≥50)"),
caption = "Baseline Ne/N estimates for the three study populations
(monthly matrices, Ne_target = 50, census_N = 40)."
)| Population | Stages | Ne/N | Ne at N=40 | Min N (Ne≥50) |
|---|---|---|---|---|
| L. rupestris pop 1 | 6 | 0.572 | 22.9 | 88 |
| L. rubripetala pop 1 | 5 | 0.930 | 37.2 | 54 |
| L. rubripetala pop 5 | 5 | 0.250 | 10.0 | 201 |
Ne_sensitivity_Vk()The variance-to-mean ratio of sexual reproductive output Vk/k̄ measures how equally seeds are distributed among reproductive adults. Under Poisson distribution (Vk/k̄ = 1), all adults contribute equally to the seed pool. Values above 1 mean some individuals dominate — a few adults produce most of the seeds, so the effective number of parents is smaller than the census count.
In Lepanthes, this is biologically plausible because:
The Poisson default (Vk/k̄ = 1) is almost certainly an underestimate of the true variance for most natural populations.
Ne_sensitivity_Vk() for the reproductive stageFor L. rupestris (6 stages), stage 5 (single-shoot reproductive adults) is the primary sexual reproductive stage. We sweep Vk/k̄ from 0.5 to 8.
sens_Vk_Lrup1 <- Ne_sensitivity_Vk(
model_fn = Ne_sexual_Y2000,
T_mat = MatU_Lrup1,
F_vec = F_Lrup1,
D = D_Lrup1,
stage_index = 5, # stage 5: single-shoot reproductive adults
Vk_range = seq(0.5, 8, by = 0.5),
Ne_target = 50,
population = "L. rupestris pop 1"
)
print(sens_Vk_Lrup1)
#>
#> === NeStage Sensitivity Analysis ===
#> Parameter swept : Vk/k_bar (sexual reproductive variance)
#> Focal stage : s5
#> Model : Ne_sexual_Y2000
#> Population : L. rupestris pop 1
#>
#> --- Range swept ---
#> Vk_over_k from 0.5 to 8 (16 values)
#>
#> --- Ne/N summary ---
#> At reference (Vk_over_k = 1) : Ne/N = 0.572 | Min N = 88
#> Maximum Ne/N (Vk_over_k = 0.5) : Ne/N = 0.604 | Min N = 83
#> Minimum Ne/N (Vk_over_k = 8) : Ne/N = 0.331 | Min N = 152
#> Ne/N range across sweep : 0.273
#>
#> --- Elasticity (Caswell 2001; Morris & Doak 2002) ---
#> Local elasticity at reference point : E = -0.104
#> Global elasticity (mean over range) : E = 0.285
#>
#> Interpretation (local elasticity):
#> A 10% increase in Vk_over_k at the reference point
#> produces a 1.0% change in Ne/N.
#> Direction: NEGATIVE -- higher variance reduces Ne/N (typical)
#>
#> --- Conservation implications ---
#> Min N at reference point : 88
#> Min N at worst case (Vk_over_k = 8) : 152
#> Ratio worst/reference : 1.7x more individuals
#> Ne target used : 50
#>
#> --- Comparison to empirical Ne/N (Frankham 1995) ---
#> Ne/N at reference (0.572) is ABOVE the empirical median of ~0.10
#> across 102 wildlife species (Frankham 1995). This population
#> has relatively high effective size.
#> Frankham (1995) empirical range: 0.01 -- 0.99
#>
#> --- References ---
#> Caswell H. (2001). Matrix Population Models, 2nd ed.
#> Sinauer Associates. [elasticity methodology]
#> Morris W.F. & Doak D.F. (2002). Quantitative Conservation
#> Biology. Sinauer Associates. Ch. 9. [sensitivity analysis]
#> Frankham R. (1995). Effective population size/adult population
#> size ratios in wildlife: a review.
#> Genetical Research 66: 95-107. [Ne/N empirical benchmarks]
#> Yonezawa et al. (2000). Evolution 54(6): 2007-2013. [model]The elasticity printed above tells us how strongly Ne/N responds to Vk/k̄. A negative local elasticity is expected — higher reproductive variance always reduces Ne/N. The conservation ratio (worst-case Ne/N relative to Poisson baseline) tells a manager: if reproductive inequality is at the high end of the plausible range, how many more individuals are needed?
# Also sweep stage 6 (two-or-more-shoot adults) for comparison
sens_Vk_Lrup1_s6 <- Ne_sensitivity_Vk(
model_fn = Ne_sexual_Y2000,
T_mat = MatU_Lrup1,
F_vec = F_Lrup1,
D = D_Lrup1,
stage_index = 6,
Vk_range = seq(0.5, 8, by = 0.5),
Ne_target = 50,
population = "L. rupestris pop 1 — stage 6"
)
# Combined data for comparison plot
df_vk_compare <- bind_rows(
sens_Vk_Lrup1$data %>% mutate(Stage = "Stage 5 (1-shoot repro)"),
sens_Vk_Lrup1_s6$data %>% mutate(Stage = "Stage 6 (2+-shoot repro)")
)
ggplot(df_vk_compare, aes(x = Vk_over_k, y = NeN, color = Stage)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
geom_vline(xintercept = 1, linetype = "dashed", color = "grey50") +
scale_color_manual(values = c("#2166ac", "#d6604d")) +
labs(
title = expression(paste("Ne/N vs. sexual reproductive variance ", V[k]/bar(k))),
subtitle = "L. rupestris pop 1 — comparing focal stage",
x = expression(V[k]/bar(k)),
y = expression(N[e]/N),
color = NULL,
caption = "Dashed line = Poisson default (Vk/k̄ = 1). Monthly matrices."
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom",
panel.grid.major.y = element_line(color = "grey92", linewidth = 0.4)
)Stage 6 (multi-shoot plants) has a larger effect because its fecundity (F = 0.414 per month) is more than three times that of stage 5 (F = 0.120). When high-fecundity adults have unequal reproductive output, the genetic consequences are proportionally larger.
Management implication: Strategies that equalise seed production — for example, hand-pollination programmes that ensure all reproductive adults contribute pollen — would have the greatest effect in stage 6 individuals.
Ne_sensitivity_L()Generation time L appears in the denominator of the Ne/N formula (Eq. 6 of Yonezawa et al. 2000): Ne/N = 2/(V·L). A longer generation time means that drift accumulates over more years per generation, reducing Ne/N. Conversely, anything that shortens the generation time — such as habitat degradation that reduces adult survival — also reduces Ne/N.
Generation time is also the most uncertain parameter in the model. Field studies of Lepanthes run for 16–34 months, which is far shorter than the generation time of these long-lived orchids. The L value we compute from monthly matrices may differ from the true population L if the observed years were not demographically representative.
L_range_use <- seq(5, 80, by = 5)
# Run for all three populations
make_L_sens <- function(MatU, F_vec, D, Ne_target, pop_label) {
Ne_sensitivity_L(
model_fn = Ne_sexual_Y2000,
T_mat = MatU,
F_vec = F_vec,
D = D,
L_range = L_range_use,
Ne_target = Ne_target,
population = pop_label
)
}
sens_L_Lrup1 <- make_L_sens(MatU_Lrup1, F_Lrup1, D_Lrup1, 50,
"L. rupestris pop 1")
sens_L_Lrub1 <- make_L_sens(MatU_Lrub1, F_Lrub1, D_Lrub1, 50,
"L. rubripetala pop 1")
sens_L_Lrub5 <- make_L_sens(MatU_Lrub5, F_Lrub5, D_Lrub5, 50,
"L. rubripetala pop 5")
# Observed L values from monthly matrices
L_obs <- c(Ne_base_Lrup1$L, Ne_base_Lrub1$L, Ne_base_Lrub5$L)
df_L <- bind_rows(
sens_L_Lrup1$data %>% mutate(Population = "L. rupestris pop 1",
L_obs = L_obs[1]),
sens_L_Lrub1$data %>% mutate(Population = "L. rubripetala pop 1",
L_obs = L_obs[2]),
sens_L_Lrub5$data %>% mutate(Population = "L. rubripetala pop 5",
L_obs = L_obs[3])
)
ggplot(df_L, aes(x = L, y = NeN, color = Population)) +
geom_line(linewidth = 1) +
geom_point(size = 1.8) +
geom_vline(aes(xintercept = L_obs, color = Population),
linetype = "dashed", linewidth = 0.7, alpha = 0.7) +
scale_color_manual(values = c("#2166ac", "#1b7837", "#d6604d")) +
labs(
title = expression(paste("Sensitivity of ", N[e]/N, " to generation time ", L)),
subtitle = "Dashed vertical lines = observed L from monthly matrices",
x = "Generation time L (months)",
y = expression(N[e]/N),
color = NULL,
caption = "All populations: Ne_sexual_Y2000, Poisson defaults, Ne_target = 50."
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom",
legend.text = element_text(face = "italic"),
panel.grid.major.y = element_line(color = "grey92", linewidth = 0.4)
)# Print elasticity summary for the most sensitive population
print(sens_L_Lrub5)
#>
#> === NeStage Sensitivity Analysis ===
#> Parameter swept : L (generation time, years)
#> Model : Ne_sexual_Y2000
#> Population : L. rubripetala pop 5
#>
#> --- Range swept ---
#> L from 5 to 80 (16 values)
#>
#> --- Ne/N summary ---
#> At reference (L = 20.833) : Ne/N = 0.260 | Min N = 193
#> Maximum Ne/N (L = 5) : Ne/N = 1.041 | Min N = 49
#> Minimum Ne/N (L = 80) : Ne/N = 0.065 | Min N = 769
#> Ne/N range across sweep : 0.976
#>
#> --- Elasticity (Caswell 2001; Morris & Doak 2002) ---
#> Local elasticity at reference point : E = -1.111
#> Global elasticity (mean over range) : E = 0.841
#>
#> Interpretation (local elasticity):
#> A 10% increase in L at the reference point
#> produces a 11.1% change in Ne/N.
#> Direction: NEGATIVE -- higher variance reduces Ne/N (typical)
#>
#> --- Conservation implications ---
#> Min N at reference point : 193
#> Min N at worst case (L = 80) : 769
#> Ratio worst/reference : 4.0x more individuals
#> Ne target used : 50
#>
#> --- Comparison to empirical Ne/N (Frankham 1995) ---
#> Ne/N at reference (0.26) is near the empirical median of ~0.10
#> across 102 wildlife species (Frankham 1995).
#> Frankham (1995) empirical range: 0.01 -- 0.99
#>
#> --- References ---
#> Caswell H. (2001). Matrix Population Models, 2nd ed.
#> Sinauer Associates. [elasticity methodology]
#> Morris W.F. & Doak D.F. (2002). Quantitative Conservation
#> Biology. Sinauer Associates. Ch. 9. [sensitivity analysis]
#> Frankham R. (1995). Effective population size/adult population
#> size ratios in wildlife: a review.
#> Genetical Research 66: 95-107. [Ne/N empirical benchmarks]
#> Yonezawa et al. (2000). Evolution 54(6): 2007-2013. [model]Because Ne/N = 2/(V·L), the relationship is hyperbolic — Ne/N declines steeply at short L and flattens at long L. This means:
Management implication: For populations where L is highly uncertain, it is worth investing in longer-term demographic monitoring (ideally ≥ 5 years) to pin down L more precisely before interpreting Ne/N.
Ne_sensitivity_d()A fundamental and counter-intuitive result of Yonezawa et al. (2000) is that under Poisson defaults (Vc/c̄ = Vk/k̄ = 1), the clonal fraction d has no effect on Ne/N. This is because when all individuals reproduce equally (whether sexually or clonally), the mode of reproduction does not matter for genetic drift.
Clonal fraction only reduces Ne/N when clonal output is more variable than sexual output — when some genets dominate clonal reproduction, reducing the number of genetically distinct lineages. We demonstrate both cases here using a hypothetical mixed-reproduction Lepanthes population.
# Use L. rubripetala pop 1 matrix structure with a mixed reproduction scenario
# Add a clonal component: imagining a hypothetical scenario where stage 5
# (reproductive adults) can reproduce clonally via offshoots
# Poisson case — d has no effect
sens_d_poisson <- Ne_sensitivity_d(
T_mat = MatU_Lrub1,
F_vec = F_Lrub1,
D = D_Lrub1,
d_fixed = c(0, 0, 0, 0, 0.5), # base: stage 5 is 50% clonal
stage_index = 5,
d_range = seq(0, 1, by = 0.1),
Vc_over_c = rep(1, 5), # Poisson clonal variance
Ne_target = 50,
population = "L. rubripetala pop 1 (Poisson)"
)
# Super-Poisson clonal variance — d starts to matter
sens_d_superpoisson <- Ne_sensitivity_d(
T_mat = MatU_Lrub1,
F_vec = F_Lrub1,
D = D_Lrub1,
d_fixed = c(0, 0, 0, 0, 0.5),
stage_index = 5,
d_range = seq(0, 1, by = 0.1),
Vc_over_c = c(1, 1, 1, 1, 4), # stage 5 clonal output highly variable
Ne_target = 50,
population = "L. rubripetala pop 1 (Vc/c̄ = 4)"
)
df_d <- bind_rows(
sens_d_poisson$data %>% mutate(Scenario = "Poisson (Vc/c̄ = 1)"),
sens_d_superpoisson$data %>% mutate(Scenario = "Super-Poisson (Vc/c̄ = 4)")
)
ggplot(df_d, aes(x = d_focal, y = NeN, color = Scenario)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
geom_vline(xintercept = 0.5, linetype = "dashed",
color = "grey50", linewidth = 0.7) +
annotate("text", x = 0.51, y = max(df_d$NeN) * 0.98,
label = "Base d = 0.5", hjust = 0, size = 3.2, color = "grey40") +
scale_color_manual(values = c("#1b7837", "#d6604d")) +
scale_x_continuous(breaks = seq(0, 1, by = 0.2)) +
labs(
title = expression(paste("Ne/N vs. clonal fraction ", d[i], " in stage 5")),
subtitle = "L. rubripetala pop 1 — hypothetical mixed reproduction scenario",
x = expression(paste("Clonal fraction ", d[i],
" (0 = fully sexual, 1 = fully clonal)")),
y = expression(N[e]/N),
color = NULL,
caption = "Ne_mixed_Y2000. Flat curve confirms Poisson result of Yonezawa et al. (2000)."
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom",
panel.grid.major.y = element_line(color = "grey92", linewidth = 0.4)
)print(sens_d_superpoisson)
#>
#> === NeStage Sensitivity Analysis ===
#> Parameter swept : d (clonal fraction)
#> Focal stage : s5
#> Model : Ne_mixed_Y2000
#> Population : L. rubripetala pop 1 (Vc/c̄ = 4)
#>
#> --- Range swept ---
#> d_focal from 0 to 1 (11 values)
#>
#> --- Ne/N summary ---
#> At reference (d_focal = 0.5) : Ne/N = 0.390 | Min N = 129
#> Maximum Ne/N (d_focal = 0) : Ne/N = 0.930 | Min N = 54
#> Minimum Ne/N (d_focal = 1) : Ne/N = 0.247 | Min N = 203
#> Ne/N range across sweep : 0.682
#>
#> --- Elasticity (Caswell 2001; Morris & Doak 2002) ---
#> Local elasticity at reference point : E = -0.588
#> Global elasticity (mean over range) : E = 0.432
#>
#> Interpretation (local elasticity):
#> A 10% increase in d_focal at the reference point
#> produces a 5.9% change in Ne/N.
#> Direction: NEGATIVE -- higher variance reduces Ne/N (typical)
#>
#> --- Conservation implications ---
#> Min N at reference point : 129
#> Min N at worst case (d_focal = 1) : 203
#> Ratio worst/reference : 1.6x more individuals
#> Ne target used : 50
#>
#> --- Comparison to empirical Ne/N (Frankham 1995) ---
#> Ne/N at reference (0.39) is ABOVE the empirical median of ~0.10
#> across 102 wildlife species (Frankham 1995). This population
#> has relatively high effective size.
#> Frankham (1995) empirical range: 0.01 -- 0.99
#>
#> --- References ---
#> Caswell H. (2001). Matrix Population Models, 2nd ed.
#> Sinauer Associates. [elasticity methodology]
#> Morris W.F. & Doak D.F. (2002). Quantitative Conservation
#> Biology. Sinauer Associates. Ch. 9. [sensitivity analysis]
#> Frankham R. (1995). Effective population size/adult population
#> size ratios in wildlife: a review.
#> Genetical Research 66: 95-107. [Ne/N empirical benchmarks]
#> Yonezawa et al. (2000). Evolution 54(6): 2007-2013. [model]Management implication: Simply removing clonal propagules (to reduce d) has no genetic benefit under Poisson variance assumptions. The conservation benefit of managing clonal reproduction comes only when dominant genets can be identified and their disproportionate contribution to the clonal pool reduced — which requires molecular markers to identify genets in the field.
We now compare the local elasticities across all parameters and all three populations to produce a prioritisation framework.
# Extract local elasticity from each sensitivity object
# Local elasticity approximated by central finite difference at Vk/k_bar = 1
# E = (dNeN/dtheta) * (theta_ref / NeN_ref)
local_elasticity <- function(sens_obj, param_col, ref_val) {
df <- sens_obj$data
idx <- which.min(abs(df[[param_col]] - ref_val))
# Use neighbouring points for central difference
if (idx == 1) idx <- 2
if (idx == nrow(df)) idx <- nrow(df) - 1
dNeN <- df$NeN[idx + 1] - df$NeN[idx - 1]
dtheta <- df[[param_col]][idx + 1] - df[[param_col]][idx - 1]
NeN_ref <- df$NeN[idx]
(dNeN / dtheta) * (ref_val / NeN_ref)
}
# L. rupestris pop 1: sweep Vk stages 5 and 6, and L
E_Vk5_Lrup1 <- local_elasticity(sens_Vk_Lrup1, "Vk_over_k", 1)
E_Vk6_Lrup1 <- local_elasticity(sens_Vk_Lrup1_s6, "Vk_over_k", 1)
E_L_Lrup1 <- local_elasticity(sens_L_Lrup1, "L",
Ne_base_Lrup1$L)
# L. rubripetala pop 1
sens_Vk_Lrub1 <- Ne_sensitivity_Vk(
model_fn = Ne_sexual_Y2000,
T_mat = MatU_Lrub1,
F_vec = F_Lrub1,
D = D_Lrub1,
stage_index = 5,
Vk_range = seq(0.5, 8, by = 0.5),
Ne_target = 50,
population = "L. rubripetala pop 1"
)
E_Vk5_Lrub1 <- local_elasticity(sens_Vk_Lrub1, "Vk_over_k", 1)
E_L_Lrub1 <- local_elasticity(sens_L_Lrub1, "L", Ne_base_Lrub1$L)
# L. rubripetala pop 5
sens_Vk_Lrub5 <- Ne_sensitivity_Vk(
model_fn = Ne_sexual_Y2000,
T_mat = MatU_Lrub5,
F_vec = F_Lrub5,
D = D_Lrub5,
stage_index = 5,
Vk_range = seq(0.5, 8, by = 0.5),
Ne_target = 50,
population = "L. rubripetala pop 5"
)
E_Vk5_Lrub5 <- local_elasticity(sens_Vk_Lrub5, "Vk_over_k", 1)
E_L_Lrub5 <- local_elasticity(sens_L_Lrub5, "L", Ne_base_Lrub5$L)
# Assemble table
elast_tbl <- data.frame(
Population = c("*L. rupestris* pop 1",
"*L. rubripetala* pop 1",
"*L. rubripetala* pop 5"),
Base_NeN = round(c(Ne_base_Lrup1$NeN,
Ne_base_Lrub1$NeN,
Ne_base_Lrub5$NeN), 3),
E_Vk_repro = round(c(E_Vk5_Lrup1, E_Vk5_Lrub1, E_Vk5_Lrub5), 3),
E_L = round(c(E_L_Lrup1, E_L_Lrub1, E_L_Lrub5), 3)
)
knitr::kable(elast_tbl,
col.names = c("Population", "Base Ne/N",
"E(Vk/k̄) repro stage", "E(L)"),
escape = FALSE,
caption = "Local elasticities at the Poisson reference point (Vk/k̄ = 1)
and at the observed L. E(Vk/k̄) < 0: increasing reproductive
variance reduces Ne/N. E(L) < 0: longer generation time
reduces Ne/N. Larger absolute values indicate higher sensitivity."
)| Population | Base Ne/N | E(Vk/k̄) repro stage | E(L) |
|---|---|---|---|
| L. rupestris pop 1 | 0.572 | -0.104 | -1.094 |
| L. rubripetala pop 1 | 0.930 | -0.486 | -1.038 |
| L. rubripetala pop 5 | 0.250 | -0.464 | -1.111 |
elast_long <- elast_tbl %>%
select(Population, E_Vk_repro, E_L) %>%
pivot_longer(cols = c(E_Vk_repro, E_L),
names_to = "Parameter",
values_to = "Elasticity") %>%
mutate(
Abs_E = abs(Elasticity),
Parameter = recode(Parameter,
E_Vk_repro = "Sexual repro variance\n(Vk/k̄, repro stage)",
E_L = "Generation time (L)")
)
ggplot(elast_long, aes(x = Population, y = Abs_E, fill = Parameter)) +
geom_col(position = "dodge", width = 0.6) +
scale_fill_manual(values = c("#2166ac", "#d6604d")) +
scale_x_discrete(labels = function(x) gsub("\\*", "", x)) +
labs(
title = "|Elasticity| of Ne/N: which parameter drives genetic drift most?",
subtitle = "Higher = Ne/N more sensitive to this parameter",
x = NULL,
y = "|Local elasticity|",
fill = "Parameter",
caption = "Local elasticity at Poisson reference (Vk/k̄ = 1) and observed L.
All populations: Ne_sexual_Y2000, monthly matrices."
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold"),
axis.text.x = element_text(face = "italic", size = 9),
legend.position = "right",
panel.grid.major.y = element_line(color = "grey92", linewidth = 0.4)
)The sensitivity analysis above leads to the following prioritisation framework for Lepanthes conservation, ordered by the expected genetic return on management investment.
Generation time L is the most influential parameter in all three populations and the most poorly estimated. Monthly matrices from 16–34 months of data capture only 1–3 generation cycles for these long-lived orchids. A dedicated mark-recapture programme of at least 5 years per population would substantially reduce uncertainty in L and allow more confident Ne/N estimates.
The elasticity of Ne/N to Vk/k̄ is consistently negative and non-trivial. In L. rupestris, where stage 6 (multi-shoot adults) has the highest fecundity (F = 0.414/month), ensuring that these adults all contribute to the seed pool — via controlled hand-pollination in ex situ programmes, or by removing physical barriers to pollinator movement — would increase Ne/N.
The minimum census size to achieve Ne ≥ 50 ranges from 54 (L. rubripetala pop 1) to 314 (L. rupestris pop 3). Translocation of individuals from source populations to these sinks would not only increase N directly, but would also restore genetic diversity lost to drift.
df_final <- bind_rows(
sens_Vk_Lrup1$data %>% mutate(Population = "*L. rupestris* pop 1"),
sens_Vk_Lrub1$data %>% mutate(Population = "*L. rubripetala* pop 1"),
sens_Vk_Lrub5$data %>% mutate(Population = "*L. rubripetala* pop 5")
)
ggplot(df_final, aes(x = Vk_over_k, y = NeN, color = Population)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
geom_vline(xintercept = 1, linetype = "dashed",
color = "grey50", linewidth = 0.7) +
geom_hline(yintercept = 50/40, linetype = "dotted",
color = "grey30", linewidth = 0.6) +
annotate("text", x = 7.5, y = 50/40 + 0.03,
label = "Ne=50 at N=40", hjust = 1, size = 3, color = "grey30") +
scale_color_manual(
values = c("#2166ac", "#1b7837", "#d6604d"),
labels = c(
expression(italic("L. rubripetala")~"pop 1"),
expression(italic("L. rubripetala")~"pop 5"),
expression(italic("L. rupestris")~"pop 1")
)
) +
labs(
title = expression(paste("Ne/N vs. sexual reproductive variance ",
V[k]/bar(k), " — three populations")),
subtitle = "Dotted horizontal line = Ne = 50 threshold at N = 40",
x = expression(V[k]/bar(k)~"(reproductive stage)"),
y = expression(N[e]/N),
color = NULL,
caption = "Ne_sexual_Y2000, monthly matrices, Ne_target = 50, census_N = 40."
) +
theme_classic(base_size = 12) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom",
panel.grid.major.y = element_line(color = "grey92", linewidth = 0.4)
)sessionInfo()
#> R version 4.5.2 (2025-10-31)
#> Platform: x86_64-apple-darwin20
#> Running under: macOS Monterey 12.7.6
#>
#> Matrix products: default
#> BLAS: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRblas.0.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
#>
#> locale:
#> [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#>
#> time zone: America/Puerto_Rico
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] knitr_1.51 purrr_1.2.1 tidyr_1.3.2 dplyr_1.2.0 ggplot2_4.0.2
#> [6] expm_1.0-0 Matrix_1.7-4 NeStage_0.8.0
#>
#> loaded via a namespace (and not attached):
#> [1] vctrs_0.7.1 cli_3.6.5 rlang_1.1.7 xfun_0.56
#> [5] otel_0.2.0 generics_0.1.4 S7_0.2.1 jsonlite_2.0.0
#> [9] labeling_0.4.3 glue_1.8.0 htmltools_0.5.9 sass_0.4.10
#> [13] scales_1.4.0 rmarkdown_2.30 grid_4.5.2 tibble_3.3.1
#> [17] evaluate_1.0.5 jquerylib_0.1.4 fastmap_1.2.0 yaml_2.3.12
#> [21] lifecycle_1.0.5 compiler_4.5.2 RColorBrewer_1.1-3 pkgconfig_2.0.3
#> [25] rstudioapi_0.18.0 farver_2.1.2 lattice_0.22-9 digest_0.6.39
#> [29] R6_2.6.1 tidyselect_1.2.1 pillar_1.11.1 magrittr_2.0.4
#> [33] bslib_0.10.0 withr_3.0.2 tools_4.5.2 gtable_0.3.6
#> [37] cachem_1.1.0