Hamilton’s rule and kin selection

evolutionary ecology
social evolution
natural selection
R
ecology tutorial
Simulate kin selection in R: an altruism allele spreads only when relatedness times benefit exceeds cost, rb greater than c, the core of social evolution.
Author

Tidy Ecology

Published

2026-08-06

Why would an animal ever help another at a cost to itself? Hamilton (1964) gave the answer that founded social evolution: a helping allele can spread if the help falls often enough on others who carry it. His rule is one line,

\[ r b > c, \]

where c is the fitness cost to the altruist, b the benefit to the recipient, and r their genetic relatedness. This post builds an altruism allele in a structured population in base R and shows that it spreads exactly when rb beats c, not before.

An altruism allele in family groups

Take a large population split into groups of relatives. Each individual carries an altruism allele (1) or not (0), and within a group the genotypes are correlated with relatedness r. An altruist pays a cost c and spreads a benefit b across its groupmates. Whether the allele is favoured is a selection differential: the covariance of fitness with genotype, straight from the Price equation.

make_pop <- function(p, r, G, n) {                      # G groups of n, relatedness r
  s <- sqrt(r); founder <- rbinom(G, 1, p)
  g <- as.vector(sapply(founder, function(f) ifelse(runif(n) < s, f, rbinom(n, 1, p))))
  list(g = g, grp = rep(seq_len(G), each = n))
}
selection_on_allele <- function(b, c = 1, r = 0.5, p = 0.3, G = 12000, n = 4, syn = 0, seed = 2) {
  set.seed(seed); po <- make_pop(p, r, G, n); g <- po$g; grp <- po$grp
  g_others <- (ave(g, grp, FUN = sum) - g) / (n - 1)    # mean genotype of the OTHER groupmates
  w <- 1 - c * g + b * g_others + syn * g * g_others    # fitness: pay c, receive b from group
  mean((w - mean(w)) * (g - mean(g))) / mean(w)         # Cov(w, g) / wbar
}

The rule in action

Fix relatedness at r = 0.5 (full siblings) and cost at c = 1. Hamilton’s rule says the allele spreads once b exceeds c / r = 2. Sweep the benefit across that threshold.

r <- 0.5; c <- 1
cat(sprintf("Hamilton threshold: b* = c/r = %.2f\n", c / r))
Hamilton threshold: b* = c/r = 2.00
for (b in c(1.0, 2.0, 3.0))
  cat(sprintf("b = %.1f  (rb - c = %+.2f):  selection on allele = %+.4f\n",
              b, r * b - c, selection_on_allele(b)))
b = 1.0  (rb - c = -0.50):  selection on allele = -0.1073
b = 2.0  (rb - c = +0.00):  selection on allele = -0.0025
b = 3.0  (rb - c = +0.50):  selection on allele = +0.0627

Below the threshold, at b = 1, selection on the allele is negative (-0.107): altruism is weeded out. At the threshold b = 2 it is essentially zero (-0.003), the knife-edge where rb = c. Above it, at b = 3, selection turns positive (+0.063) and altruism spreads. The sign of selection tracks rb - c exactly, because that quantity is the covariance of fitness with genotype once relatedness is folded in.

library(ggplot2)
bs <- seq(0.5, 3.5, 0.25)
dd <- data.frame(b = bs, sel = vapply(bs, selection_on_allele, numeric(1)))
ggplot(dd, aes(b, sel)) +
  geom_hline(yintercept = 0, colour = "#93a87f", linewidth = 0.4) +
  geom_vline(xintercept = c / r, linetype = "dashed", colour = "#93a87f") +
  geom_line(colour = "#275139", linewidth = 0.9) + geom_point(colour = "#16241d", size = 1.6) +
  annotate("text", x = c / r + 0.05, y = -0.08, label = "b* = c/r", colour = "#46604a", size = 3.4, hjust = 0) +
  labs(x = "Benefit b", y = "Selection on altruism allele") +
  theme_minimal(base_size = 12)
A rising curve of selection against benefit, crossing zero at benefit 2, with a dashed vertical line at the threshold.
Figure 1: Selection on an altruism allele as the benefit b rises, at r = 0.5 and c = 1. It crosses zero exactly at b = c/r = 2, the point where Hamilton’s rule tips from opposing altruism to favouring it.

What relatedness buys

The rule makes the logic of altruism quantitative. Help is favoured when it is cheap (c small), generous (b large), or aimed at close kin (r large). A gene for helping full siblings needs the help to more than double the recipient’s success to pay for itself; for cousins at r = 0.125, the benefit must be eight times the cost. This is why extreme cooperation, sterile worker castes, alarm calls, is concentrated among close relatives: only high relatedness makes steep costs worth paying. The next post shows that r is not a fixed pedigree number but a regression you can estimate.

An honest limit

Hamilton’s rule in this clean form assumes the fitness effects are additive: the cost and benefit simply add up, with no synergy between cooperating partners. When helping pays off only if a partner also helps, or when benefits saturate, rb > c can predict the wrong direction. It also assumes the benefit lands on relatives rather than on competitors, which is not automatic in a crowded patch. Both assumptions fail in ways that matter, and the checking post works through them. The rule is the right first question to ask about any cooperative behaviour; it is not the last word once payoffs stop being additive.

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.