<jupyter_start><jupyter_text>EEB214: Understanding genetic drift through computer-based simulations in [`R`](https://www.r-project.org/)This tutorial (i.e. Jupyter Notebook) will guide you through some hands-on example on how *Genetic Drift* operates within populations. Read and follow the instructions below. Enter the information requested and run each block of code separately. Some blocks of code will give you an output, such as text or a plot, others will not. BackgroundImagine you have a population of a species of bacteria. Individuals (i.e. cells) in the population will have either the `A` or the `a` allele at a frequency set initially by you. Then you will let the population reproduce and every generation you will select randomly which individuals pass on their genes. Now, all population genetic models have a set of (normally simplifying) assumptions. In our case, we will assume that the population size (total number of individuals at any given generation) will remain constant and that `A/a` is a **neutral polymorphism**. That is, it is not affected by selection. Also, remember that bacteria are [haploid](https://simple.wikipedia.org/wiki/Ploidy) (only one or a single set of chromosomes) as opposed to humans being [diploid](https://simple.wikipedia.org/wiki/Ploidy) (two sets of chromosomes), meaning that every individual will only have one  of the alleles.The way the simulation works is that every generation we will randomly sample "cells" with either `A` or `a`. We'll keep on doing that until one allele gets "fixed" or goes "extinct" in the population. For tractability, we will only focus on the fate of allele `A`. Goals* Run through the exersizes* Try to understand what's happening in each step* Use your results (text and plots) to answer the quiz on [Quercus](https://q.utoronto.ca/). How does changing the initial parameters of the simulation help you understand how *Genetic Drift* operates. Let's get startedFirst load the libraries and functions needed for the exersize.<jupyter_code># load libraries and functions
source('genetic_drift.R')<jupyter_output><empty_output><jupyter_text>Now, let's set the initial parameters.<jupyter_code>population_size = 10
initial_frequency = 0.5<jupyter_output><empty_output><jupyter_text>These are [variables](https://en.wikipedia.org/wiki/Variable_(computer_science)) or placeholders. They are basically names that occupy a space in the memory of the computer and store a value.We can recall these at anytime.<jupyter_code># the print function prints to screen
print(population_size)
print(initial_frequency)<jupyter_output><empty_output><jupyter_text>Now let's run our first simulation with these parameters.<jupyter_code>simulation = genetic_drift(population_size, initial_frequency)
# first 6 rows of the table
head(simulation)
# last 6 rows of the table
tail(simulation)<jupyter_output><empty_output><jupyter_text>Note that `x` and `y` are randomly generated coordinates (**don't worry about them! They are for plotting**). The last set of rows under `generation` are also telling us at which generation the simulation stopped, as well as the frequency of allele `A`. Let's look at how our population of bacteria might lookWe will plot the population at generation `1`. Imagine the dark circle is our [Petri dish](https://en.wikipedia.org/wiki/Petri_dish) and the colored dots are our colonies with either the `A` or the `a` allele. Since our `initial_frequency` is set to `0.5`, this means that bacteria with `A` or with `a` will be at equal numbers at generation 1.<jupyter_code>p1 = plot_population(simulation, 1, population_size)
p1<jupyter_output><empty_output><jupyter_text>Now let's look at the last generation.<jupyter_code>last_generation = simulation[nrow(simulation),"generation"]
# print(last_generation)
p_last = plot_population(simulation, last_generation, population_size)
p_last<jupyter_output><empty_output><jupyter_text>**What happend here? Where did the other allele go?** Plot the frequecy of allele `A` through timeNow let's look at the frequency of allele `A`.<jupyter_code>p_freq = plot_frequency(simulation, population_size)
p_freq<jupyter_output><empty_output><jupyter_text>What is the frequency of allele `A` at the last generation? Did it fix or did go extinct? How many generations did it take? Play with the parameters: population sizeNow let's see what happens if we change the parameters a bit.<jupyter_code>population_size = 100
simulation = genetic_drift(population_size, initial_frequency)
plot_frequency(simulation, population_size)<jupyter_output><empty_output><jupyter_text>**What happened now? What changed?**Let's try repeating these two population sizes multiple times, then average the results.<jupyter_code># for storing plots
plots = vector("list",12)
# for the last generation in each simulation
last_gens = vector("numeric",12)
last_freq = vector("numeric",12)
# vector of population sizes
popsize = 10
# loop through and save plot
for (i in 1:12){
    simulation = genetic_drift(popsize, initial_frequency)
    last_gens[i] = simulation[nrow(simulation),"generation"]
    last_freq[i] = simulation[nrow(simulation),"frequency"]
    plots[[i]] = plot_frequency(simulation, popsize)
}
# this might take a while... Be patient!
# first 4
do.call(plot_grid, c(plots[1:4], ncol=2))
# next 4
do.call(plot_grid, c(plots[5:8], ncol=2))
# next 4
do.call(plot_grid, c(plots[9:12], ncol=2))<jupyter_output><empty_output><jupyter_text>**What do the** red **and** green **colors stand for? Can you note any patterns?**For a population size of 10 let's look at the averge time it takes an allele to get fixed.<jupyter_code>print(median(last_gens))<jupyter_output><empty_output><jupyter_text>Now let's look at the number of times the allele got fixed or got lost.<jupyter_code>tab = table(last_freq)
names(tab) = c("lost","fixed")
tab<jupyter_output><empty_output><jupyter_text>Based on these numbers we can calculate the probability an allele will get fixed as a function of it's initial frequency and a assuming a random sampling process (*Genetic Drift*).<jupyter_code>tab/sum(tab)<jupyter_output><empty_output><jupyter_text>Let's try increasing the population size.<jupyter_code># for storing plots
plots = vector("list",12)
# for the last generation in each simulation
last_gens = vector("numeric",12)
last_freq = vector("numeric",12)
# vector of population sizes
popsize = 100
# loop through and save plot
for (i in 1:12){
    simulation = genetic_drift(popsize, initial_frequency)
    last_gens[i] = simulation[nrow(simulation),"generation"]
    last_freq[i] = simulation[nrow(simulation),"frequency"]
    plots[[i]] = plot_frequency(simulation, popsize)
}
# this might take a while... Be patient!
# first 4
do.call(plot_grid, c(plots[1:4], ncol=2))
# next 4
do.call(plot_grid, c(plots[5:8], ncol=2))
# next 4
do.call(plot_grid, c(plots[9:12], ncol=2))<jupyter_output><empty_output><jupyter_text>Let's look at the average number of generations.<jupyter_code>print(median(last_gens))<jupyter_output><empty_output><jupyter_text>**Compare this result with the previous one (i.e. population size = 10). What's different?**Let's now look at how many times it fixed/went extinct and the probability of fixation.<jupyter_code>tab = table(last_freq)
names(tab) = c("lost","fixed")
tab
# probability
tab/sum(tab)<jupyter_output><empty_output><jupyter_text>**Compare the probability of fixation with the initial frequency. Is there a relationship?**  Play with the parameters: initial frequencyNow let's see what happens if we changes the initial frequency a bit.Let's run the same simulation, but this time let's start with a frequency of `0.1`.<jupyter_code># for storing plots
plots = vector("list",12)
# for the last generation in each simulation
last_gens = vector("numeric",12)
last_freq = vector("numeric",12)
# vector of population sizes
initfreq = 0.1
popsize = 100
# loop through and save plot
for (i in 1:12){
    simulation = genetic_drift(popsize, initfreq)
    last_gens[i] = simulation[nrow(simulation),"generation"]
    last_freq[i] = simulation[nrow(simulation),"frequency"]
    plots[[i]] = plot_frequency(simulation, popsize)
}
# this might take a while... Be patient!
# first 4
do.call(plot_grid, c(plots[1:4], ncol=2))
# next 4
do.call(plot_grid, c(plots[5:8], ncol=2))
# next 4
do.call(plot_grid, c(plots[9:12], ncol=2))<jupyter_output><empty_output><jupyter_text>**Is there a pattern?**Let's look at the average time to fixation or loss.<jupyter_code>print(median(last_gens))<jupyter_output><empty_output><jupyter_text>**If it's the same population size (population_size = 100), why is the time lower now?**Let's now look at the times it fixed/went extinct and the probability of fixation.<jupyter_code>tab = table(last_freq)
names(tab) = c("lost","fixed")
tab
# probability
tab/sum(tab)<jupyter_output><empty_output><jupyter_text>Let's round those numbers to their nearest decimal.<jupyter_code>round(tab/sum(tab),1)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09920944215566195, 'fraction_numerical': 0.011691348402182385, 'mean_word_length': 6.332244897959184, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 77, 'https://': 6, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '101224', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2825, 'n_tokens_neox': 2649, 'n_words': 987, 'path': 'genetic_drift.ipynb', 'repo_name': 'santiagosnchez/EEB214', 'size': '8981'}