Allee effects and thresholds in R

ecology tutorial
R
population ecology
conservation
population dynamics
Allee effects in R: strong versus weak, the critical density as an unstable equilibrium, and the extinction threshold a shrinking population must clear.
Author

Tidy Ecology

Published

2026-07-30

Crowding usually slows a population: the more individuals, the less each one gets, and per-capita growth falls. An Allee effect is the opposite, at low density. When a population is small, being small is itself a problem, because mates are hard to find, group defence collapses, or there are too few individuals to swamp predators. Per-capita growth then rises with density over the sparse range, and that inversion, positive density dependence, changes what it means for a population to be rare (Stephens and Sutherland 1999 Trends in Ecology and Evolution 14(10):401-405; Courchamp et al. 1999 Trends in Ecology and Evolution 14(10):405-410).

Strong and weak

There are two flavours, and the difference is a matter of sign. Under a weak Allee effect per-capita growth is reduced when the population is sparse but stays positive: a rare population grows slowly, but it still grows. Under a strong Allee effect per-capita growth goes negative below a critical density: a population that falls below it shrinks to extinction on its own. That critical density is the whole story, so it is worth building all three per-capita growth curves side by side.

r <- 0.5; K <- 100; A <- 30
g_log    <- function(N) r*(1 - N/K)                 # logistic: crowding only
g_weak   <- function(N) r*(1 - N/K)*(N/(N + 12))    # weak Allee: reduced but positive
g_strong <- function(N) r*(1 - N/K)*(N/A - 1)       # strong Allee: negative below A
sapply(list(logistic = g_log, weak = g_weak, strong = g_strong), function(f) f(10))
  logistic       weak     strong 
 0.4500000  0.2045455 -0.3000000 

At a density of 10 the logistic population still grows at 0.45 per head, the weak-Allee one at a reduced but positive 0.20, and the strong-Allee one at -0.30: negative. Only the strong Allee effect has a density below which the population is doomed.

Ng <- seq(0, 100, 0.5)
d <- rbind(data.frame(N = Ng, g = g_log(Ng),    type = "logistic"),
           data.frame(N = Ng, g = g_weak(Ng),   type = "weak Allee"),
           data.frame(N = Ng, g = g_strong(Ng), type = "strong Allee"))
ggplot(d, aes(N, g, colour = type)) +
  geom_hline(yintercept = 0, colour = col_ref, linewidth = 0.4) +
  geom_line(linewidth = 1) +
  geom_vline(xintercept = A, colour = col_strong, linetype = "dashed") +
  annotate("text", x = A + 1.5, y = -0.28, label = "critical density A", hjust = 0, colour = col_strong, size = 3.4) +
  scale_colour_manual(values = c("logistic" = col_log, "weak Allee" = col_weak, "strong Allee" = col_strong)) +
  labs(x = "Population density", y = "Per-capita growth rate", colour = NULL,
       title = "An Allee effect is positive density dependence at low density")
Three per-capita growth curves against density; the grey logistic declines linearly, the green weak-Allee curve dips near the origin but stays positive, the ochre strong-Allee curve is negative below a critical density and positive above it.
Figure 1: Per-capita growth against density. The logistic line (grey) falls steadily. The weak Allee curve (green) dips at low density but stays above zero. The strong Allee curve (ochre) crosses zero at the critical density and is negative below it.

The threshold is an unstable equilibrium

The strong Allee model has three equilibria: extinction at zero, carrying capacity at 100, and the critical density at 30. The first two are stable, attractors the population settles into. The critical density is unstable: it is a watershed, and the smallest push to either side sends the population away from it, up to carrying capacity or down to nothing. Run trajectories from just below and just above it.

traj <- function(N0, g, tmax = 60, dt = 0.05) {
  N <- N0; out <- numeric(tmax/dt + 1); out[1] <- N0
  for (i in 2:length(out)) { N <- max(N + N*g(N)*dt, 0); out[i] <- N }
  data.frame(t = seq(0, tmax, dt), N = out)
}
sapply(c(25, 31), function(N0) round(tail(traj(N0, g_strong)$N, 1), 1))
[1]   0 100

A population starting at 25, five below the threshold, collapses to 0.0. One starting at 31, one above it, climbs to 100. Six individuals of difference in starting size decide extinction versus a full population. Nothing about the two populations differs except which side of the watershed they began on.

runs <- do.call(rbind, lapply(c(15, 25, 29, 31, 45, 80), function(N0) {
  tr <- traj(N0, g_strong); tr$N0 <- factor(N0); tr }))
ggplot(runs, aes(t, N, group = N0, colour = N < 1 | N0 %in% c("15","25","29"))) +
  geom_hline(yintercept = A, colour = col_ref, linetype = "dashed") +
  geom_line(linewidth = 0.9) +
  annotate("text", x = 2, y = A + 4, label = "critical density", hjust = 0, colour = col_ref, size = 3.4) +
  scale_colour_manual(values = c("FALSE" = col_weak, "TRUE" = col_strong), guide = "none") +
  labs(x = "Time", y = "Population size", title = "The critical density decides extinction or recovery")
Population trajectories over time from different starting sizes; curves starting below a dashed threshold line decline to zero, curves starting above it rise to carrying capacity.
Figure 2: Trajectories of a strong-Allee population from several starting sizes. Those below the critical density (dashed) fall to extinction; those above it rise to carrying capacity. The threshold divides the outcomes.

Why the threshold is only the beginning

A deterministic threshold is a clean line, and reality is not. A real population does not sit at a fixed density and wait; it fluctuates, and near the threshold a run of bad luck can carry it across even when the average trend is upward. That is why a strong Allee effect makes extinction likely at densities the deterministic model calls safe, the subject of the extinction-risk post. Before that, the next post asks where the threshold comes from: an Allee effect at the population level is built from Allee effects on individual fitness components, and the two are not the same thing. And the threshold that matters most is the one hardest to see, because it lives at low density where populations are rare and data are thin, which the checking post takes up.

References

  • Courchamp F et al. 1999. Trends in Ecology and Evolution 14(10):405-410 (10.1016/S0169-5347(99)01683-3).
  • Stephens PA, Sutherland WJ 1999. Trends in Ecology and Evolution 14(10):401-405 (10.1016/S0169-5347(99)01684-5).
  • Stephens PA, Sutherland WJ, Freckleton RP 1999. Oikos 87(1):185-190 (10.2307/3547011).

Newsletter

Get new tutorials by email

New R and QGIS tutorials for ecologists, straight to your inbox. No spam; unsubscribe anytime.

By subscribing you agree to receive these emails and confirm your address once. See the privacy policy.