Effective Population Size: Population Size Bottlenecks
The effective population size, Ne, is the size of a population that behaves like an
ideal (Wright-Fisher) population of size Ne. Let's demonstrate what that means.
The first graph shows the evolutionary dynamics of a population of size N=1000. The
effective population size is calculated as the harmonic mean, and shown in the
legend. Because the population size is constant at N=1000, the effective population
size is Ne=1000 as well.
Let's go ahead and introduce a population size bottleneck of N=10 at every 10th
generation. What is the effective population size of such a population?
Our population is of size N=1000 for 9 generations, then N=10 for one generation (the bottleneck),
then N=1000 for 9 generations again, then N=10 for one generation, etc. The average
population size over time is 0.9*1000 + 0.1*10, or 901. We could be tempted to think
that the population thus behaves like an ideal population at a constant size N=901, which would look
as follows:
However, the harmonic mean of the population size is only 92. What would an ideal population at constant size N=92 look like? Let's take a look:
At N=92, the effect of genetic drift has strongly increased compared to N=1000. Let's now take a look at the evolutionary dynamics of the population with N=1000, but with bottlenecks of N=10 every 10th generation:
Thus, despite having an average size of N=901, this population behaves like an ideal population of N=92. In other words, while its census population size is N=901, its effective population size is Ne=92.
Code
var p;
var N = 1000;
var generations = 100;
var data = [];
var simulations = 10;
var population_sizes = [];
function next_generation(simulation_data, current_N) {
var draws = 2 * current_N;
var a1 = 0;
var a2 = 0;
for (var i = 0; i < draws; i = i + 1) {
if (Math.random() <= p) {
a1 = a1 + 1;
}
else {
a2 = a2 + 1;
}
}
p = a1/draws;
simulation_data.push(p);
}
function simulation(simulation_counter) {
p = 0.5;
var population_size;
for (var i = 0; i < generations; i = i + 1) {
if (i%10 == 9) {
population_size = 10;
}
else {
population_size = N;
}
population_sizes.push(population_size);
next_generation(data[simulation_counter],population_size);
}
}
function effective_population_size(all_Ns) {
var denominator = 0;
for (var i = 0; i < all_Ns.length; i = i + 1) {
denominator = denominator + (1 / all_Ns[i]);
}
return Math.round(all_Ns.length / denominator);
}
for (var i = 0; i < simulations; i = i + 1) {
data.push([]);
simulation(i);
}
Ne = effective_population_size(population_sizes);
draw_line_chart(data,"Generation","p",["Eff. Population Size:",Ne,"Generations:",generations]);
Note: the draw_line_chart function is built with D3.js
and can be found here.