BIO 202, Spring 2026, draft v1. Florida Scrub Jay. A real population whose effective size dropped from habitat fragmentation, leaving a measurable drift signature.
Heterozygosity decays at rate 1 − 1/(2Nₑ) per generation. Fit Nₑ from the decay. Detect a bottleneck. Apply to FSJ data.
Watch heterozygosity fall over time. Slide Nₑ; watch the decay rate change. Larger Nₑ → slower decay.
One locus, observed heterozygosity Hₜ. Each generation: H decays by factor (1 − 1/(2Nₑ)). Plot the trajectory. Bigger Nₑ → slower decay. The decay rate per generation is the inverse of "effective genetic time."
Ne <- 100gens <- 500H0 <- 0.5H <- H0 * (1 - 1/(2*Ne))^(0:gens)plot(0:gens, H, type = "l", ylim = c(0, H0))
Two-epoch demography. Slide the bottleneck depth and duration. Watch the heterozygosity trajectory bend.
Pre-bottleneck Nₑ = 1000. At generation 200, Nₑ drops to a slider value (the bottleneck) for some duration, then recovers. Heterozygosity decays slowly, then crashes, then resumes slow decay (now from a lower starting point).
Ne_pre <- 1000; Ne_bot <- 50; dur <- 50gens <- 800; t_bot <- 200; H <- numeric(gens+1); H[1] <- 0.5for (g in 1:gens) { Ne <- if (g >= t_bot && g < t_bot + dur) Ne_bot else Ne_pre H[g+1] <- H[g] * (1 - 1/(2*Ne))}
Given noisy H̄ measurements over generations, find the Nₑ that best explains the decay. Profile-likelihood-style — scan Nₑ values, find the minimum sum-of-squared residuals.
Simulate H̄ trajectories with a known true Nₑ plus measurement noise. Fit Nₑ by scanning candidate values. Read off the minimum and the 95% CI from the SSR curve.
Hobs <- read.csv("data/clean/fsj_allele_freq.csv")Ne_grid <- seq(10, 5000, 10)ssr <- sapply(Ne_grid, function(N) { Hpred <- Hobs$H[1] * (1 - 1/(2*N))^Hobs$gen sum((Hobs$H - Hpred)^2)})Ne_grid[which.min(ssr)]
Allele frequency time courses from a wild population. Find Nₑ. Decide whether you need a one-epoch or two-epoch fit.
Loci from data/clean/fsj_allele_freq.csv across decades. Mean heterozygosity per year is plotted. Fit a one-epoch model (constant Nₑ) vs a two-epoch (bottleneck) model. The faint blue trajectories around the 1-epoch fit are five simulated decay paths under the same fitted Nₑ — they show how much noise constant-Nₑ drift alone produces. Compare the observed dots to that envelope, not just to the mean line.
fsj <- read.csv("data/clean/fsj_allele_freq.csv")H <- tapply(2*fsj$p*(1-fsj$p), fsj$year, mean)yrs <- as.numeric(names(H))Ne_grid <- seq(10, 5000, 10)ssr <- sapply(Ne_grid, function(N) { Hp <- H[1] * (1 - 1/(2*N))^(yrs - yrs[1]) sum((H - Hp)^2)})