Nature, in Code

# 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.