--- title: "3. Deriva genética y heterocigosidad" author: "Enrique Lessa" date: "`r Sys.Date()`" output: default: word_document --- ```{r setup, include=FALSE} library("knitr") knitr::opts_chunk$set(echo = TRUE) library("markdown") ``` ### Introducción La deriva genética es el cambio de frecuencias alélicas en la población a lo largo del tiempo debido al azar. En genética de poblaciones, existen varios modelos que especifican cómo se produce dicho cambio. Uno de los más usados es el modelo Wright-Fisher, desarrollado en forma más o menos paralela por Sewall Wright y Ronald Fisher, dos de los fundadores del campo. En dicho modelo, los alelos de una generación se obtienen por un muestreo al azar con reposición de los alelos de la generación precedente. Para un sistema diploide, la población tiene 2N alelos. Cada uno de los 2N alelos de la generación $t-{1}$ se tomaron al azar (imaginemos, para visualizarlo, uno a uno) de los 2N disponibles en la generación $t_{0}$. Cada uno de los 2N alelos de la generación parental en $t_{0}$ tiene igual probabilidad de ser muestreado al escoger uno de la generación $t-{1}$. Por lo tanto, la probabilidad de que un alelo sea de tipo A en *t* es igual a la frecuencia de A en *t*-1. El proceso se repite generación tras generación. $$p_{0} ==> p_{1} ==> p_{2}...$$ El modelo básico de Wright-Fisher asume que no hay mutación, que no hay selección sobre el gen de interés, y que la población es homogénea (sin subdivisiones) y cerrada (no hay migración). Además, tiene un tamaño constante de N individuos. Para un sistema diploide, el número de alelos es 2N, pero el modelo puede aplicarse para cualquier sistema: es un modelo "haploide" en el sentido de que observamos los cambios en las frecuencias alélicas a lo largo del tiempo sin importarnos si se combinan para formar individuos (y en caso afirmativo de qué manera). Aquí usaremos 2N alelos porque asumimos en que estamos siguiendo un locus diploide autosómico. *Ejercicio 1*. Completar la siguiente tabla, indicando el número de alelos que deben tenerse en cuenta para usar el modelo Wright-Fisher en loci con distintos modos de herencia en una población de N individuos de una especie de mamífero (diploide). Asumimos una proporción de sexos de 1:1. |Número de individuos|Locus autosómico |Locus mitocondrial|Cromosoma X |Cromosoma Y| |:--:|:--:|:--:|:--:|:--:| |N| | | | | ### Distribución binomial Antes de utilizar la distribución binomial para entender la deriva genética, puede ser recomendable estudiar la introducción disponible en "1. Distribución binomial en RStudio Cloud." Recordemos que hay dos formas de utilizar la binomial: a) Usando la función *dbinom* ("d" de distribución) para calcular la probabilidad de observar de observar 0, 1, ... n copias de A en una muestra de tamaño n, dada la frecuencia de A ($p$). b) Usando la función *rbinom* ("r" de random) para simular el muestreo al azar de n copias de los alelos de una generación y registrar cuántos de ellos son de tipo A, dada la frecuencia de A en la población muestreada. Para estudiar la deriva genética, usamos *dbinom* para calcular probabilidades de obtener un cierto número de copias de A en una generación a partir de la frecuencia de A en la generación precedente. A cada resultado posible (0, 1, ... n copias) asociamos de este modo una probabilidad. Por otra parte, usamos *rbinom* para simular una o más realizaciones del proceso de muestreo de una generación a la siguiente. Si queremos simular el proceso a lo largo del tiempo, usaremos *rbinom* en cada paso (de la generación 0 a la 1, de 1 a la 2, de la 2 a la 3, etc.), actualizando los valores de $p$ a lo largo del tiempo ($p_{0}, p_{1}, ... p_{t}$). *Ejemplo 1: distribución de probabilidad de frecuencias en una generación* Usamos *dbinom* para calcular las probabilidades como se explicó más arriba. Usamos $2N$ porque estamos estudiando un locus autosómico, con alelos A y a. Como estamos evaluando todos los resultados posibles, verificamos también que la suma de los *P(i)* es 1. ```{r binomial_probs} N = 5 # número de individuos (diploides) en la población (asumimos que N es constante). pr = 0.3 # valor inicial de p (frecuencia del alelo A) en la población #A. Probabilidad de observar 0, 1, ... 2N copias del alelo A en una muestra de n=10, dado que la frecuencia del # alelo en la población es pr serie= c(0:(2*N)) dist = dbinom(serie, 2*N, pr) # c(0:2*N) es el rango de resultados cuyas probabilidades queremos calcular print(dist) sum(dist) # como calculamos para todos los resultados posibles, verificamos que la suma da 1 #B. Graficamos los valores obtenidos plot(c(0:(2*N)), dist, type = "b", main = "Binomial, p = 0,3, 2N =10", xlab = "i (número de copias del alelo A en la segunda generación)", ylab = "P(i)", col = "brown" ) ``` Observamos que, si $0\lt{p_{0}\lt1}$, $p_{1}$ puede tomar cualquier valor entre 0 1 y (el número de copias de A puede ser 0, 1, ... 2N). Notemos también que el proceso de muestreo binomial se puede repetir de la generación 1 a la 2, y así sucesivamente. Las frecuencias seguirán fluctuando según las reglas de la distribución binomial, excepto si A se pierde (p=0) o se fija (p=1). En estos casos, como el modelo básico asume que no hay mutación, la variación se agota y no habrá más cambios en frecuencias. Estos estados se llaman estados absorbentes. *Ejercicio 2* a) Los valores iniciales usados para *dbinom* en el ejemplo de arriba son $N=5$ y $p=0.3$. ¿Cuál es la probabilidad de alcanzar un estado abosrbente en una generación en este caso? b) usar el código provisto, modificando los valores iniciales, para averiguar cómo pueden modificarse los parámetros anteriores para aumentar o reducir la probabilidad de alcanzar un estado absorbente en una generación. ### Muestras al azar y cambios a lo largo del tiempo En esta aplicación de la binomial, en cada generación muestreamos todos los alelos de la población (*i=2N*) usando la frecuencia *p* de la generación inmediatamente precedente. Para obtener una realización aleatoria del proceso, usamos la función *rbinom*. para obtener trayectorias a lo largo del tiempo, definimos además el número de generaciones en las que queremos seguir el proceso. ```{r deriva_1} # Condiciones de la simulación N = 20 # número de individuos; como simulamos loci diploides, hay 2N alelos en la población p0 = 0.5 # frecuencia inicial de un alelo (A), cuya frecuencia en la población seguimos a lo largo del tiempo. t = 50 # número de generaciones pvec = as.vector(p0) # inicializando un vector de frecuencias # Simulación p=p0 #frecuencia inicial for(i in seq(1:t)){ p = rbinom(1, 2*N, p)/(2*N) # print(p) pvec = append(pvec, p) } # Frecuencias inicial y final print(c(p0, pvec[length(pvec)])) # Gráfica de la trayectoria de evolución por deriva genética de la simulación precedente plot(pvec, type = "l" ,main = "Evolución por deriva genética: una realización", xlab = "tiempo", ylab = "p", ylim = range(0,1), col = "blue") ``` Podemos usar la simulación de más arriba para ganar cierta intuición sobre las características de la deriva genética. Por ejemplo, podemos probar distintos tamaños poblacionales (modificando el valor de N) para ver cómo cambia el proceso. Del mismo modo, podemos experimentar con diferentes frecuencias iniciales o cambiar el número de generaciones. A continuación se presenta código para comparar dos procesos de deriva con el mismo punto de partida e idénticas condiciones generales. ```{r deriva 2} # Condiciones de la simulación # Nota: tomamos los valores de N, p0 y t del bloque anterior # En cambio, creamos dos vectores para registrar las frecuencias a lo largo del tiempo, comenzando por asignarle a cada uno el valor de p0 pvec1 = pvec2 = as.vector(p0) # inicializando dos vectores de frecuencias # Simulación p1 = p2 = p0 # frecuencias iniciales for(i in seq(1:t)){ p1 = rbinom(1, 2*N, p1)/(2*N) # muestreo de la binomial (en número de copias del alelo), divido por 2N para obener la frecuencia relativa pvec1 = append(pvec1, p1) # el valor obtenido de p1 se agrega al final del vector de frecuencias p2 = rbinom(1, 2*N, p2)/(2*N) # lo mismo de arriba pero para la segunda simulación pvec2 = append(pvec2, p2) } # Frecuencias inicial y final print(c("p(0) =", p0, "p(t) =", pvec1[length(pvec1)])) print(c("p(0) =", p0, "p(t) =", pvec2[length(pvec2)])) # Gráfica de la trayectoria de evolución por deriva genética de la simulación precedente plot(pvec1, type = "l" ,main = "Dos realizaciones delproceso de deriva genética", xlab = "tiempo", ylab = "p", ylim = range(0,1), col = "orange") lines(pvec2, col = "blue") ``` ### Comentarios Las ideas principales que queremos reforzar simulando dos realizaciones (y repitiendo este experimento varias veces) del mismo proceso son: - Concepto de proceso aleatorio: el proceso tiene reglas probabilísticas, no deterministas, por lo que dos procesos con las mismas reglas y el mismo punto de partida tienen diferentes trayectorias. Se dice que son dos realizaciones de un mismo proceso. - Proceso markoviano, sin memoria: en cada paso, el proceso depende únicamente de su estado (en nuestro caso, la frecuencia del alelo de referencia) y de las reglas para pasar de dicho estado al siguiente. - Estados absorbentes: una vez que se llega a la fijación (p=1) o eliminación (p=0) del alelo, no hay más cambios; esos dos estados son absorbentes (se puede llegar a ellos, pero no se puede salir de ellos). Notemos, de paso, que las trayectorias "azul" y "roja" son independientes, aunque les marcamos un mismo punto de partida y siguen las mismas reglas (muestreo binomial con reposición, idéntico tamaño poblacional). Estas dos trayectorias pueden pensarse como: - Ejemplos de dos posibles trayectorias de la frecuencia de un mismo alelo, partiendo de p0. Si la línea azul representa la trayectoria de un alelo en una población real, podemos pensar en la línea roja como otra trayectoria igualmente probable... y podríamos seguir agregando más y más trayectorias. - La historia de dos genes no ligados (esto es, con trayectorias independientes) en una misma población, con idénticas frecuencias iniciales. ### Efecto del tamaño poblacional ```{r deriva 3} # Condiciones de la simulación # Nota: tomamos los valores de p0 y t de los bloques previos # Pero usaremos dos tamaños poblacionales diferentes: N1 = 20 N2 = 200 # En cambio, creamos dos vectores para registrar las frecuencias a lo largo del tiempo, comenzando por asignarle a cada uno el valor de p0 pvec1 = pvec2 = as.vector(p0) # inicializando dos vectores de frecuencias # Simulación p1 = p2 = p0 # frecuencias iniciales for(i in seq(1:t)){ p1 = rbinom(1, 2*N1, p1)/(2*N1) # muestreo de la binomial (en número de copias del alelo), divido por 2N para obener la frecuencia relativa pvec1 = append(pvec1, p1) # el valor obtenido de p1 se agrega al final del vector de frecuencias p2 = rbinom(1, 2*N2, p2)/(2*N2) # lo mismo de arriba pero para la segunda simulación pvec2 = append(pvec2, p2) } # Frecuencias inicial y final print(c("p(0) =", p0, "p(t) =", pvec1[length(pvec1)])) print(c("p(0) =", p0, "p(t) =", pvec2[length(pvec2)])) # Gráfica de la trayectoria de evolución por deriva genética de la simulación precedente plot(pvec1, type = "l" , main = "naranja: N1; azul: N2", xlab = "tiempo", ylab = "p", ylim = range(0,1), col = "orange") lines(pvec2, col = "blue") ``` ### Heterocigosidad y panmixia El término heterocigosidad se usa de un modo ligeramente distinto en diferentes contextos. Así, hablamos de heterocigosidad esperada $H_{e}$ para referirnos a la frecuencia esperada de heterocigotas bajo el modelo Hardy-Weinberg. Dadas las frecuencias alélicas en la población ${p_1, p_2, ..., p_k}$, $$H_{e} = \sum\limits_{i*Ejercicio 1*. Completar la siguiente tabla, indicando el número de alelos que deben tenerse en cuenta para usar el modelo Wright-Fisher en loci con distintos modos de herencia en una población de N individuos de una especie de mamífero (diploide). Asumimos una proporción de sexos de 1:1. |Número de individuos|Locus autosómico |Locus mitocondrial|Cromosoma X |Cromosoma Y| |--:|:--:|:--:|:--:|:--:| |N| 2N | N/2 | 3N/2 | N/2 | *Recordamos que aceptamos que la mitad de la población son machos y la mitad hembras. Notamos que el número de alelos (tamaño de la población para el modelo haploide de Wright-Fisher), tanto en loci mitocondriales como en el caso del cromosoma Y, es la cuarta parte del número de alelos de un locus autosómico (2N vs. N/2). Esos loci son transmitidos solamente por uno de los sexos (hembras para loci mitocondriales; machos para el cromosoma Y), que transmite una copia de dichos loci (esto merece cierta discusión en el caso del ADN mitocondrial).* *En el caso del cromosoma X, hay 3 copias de X cada 4 de los loci autosómicos. Por tanto, para 2N autosomas el número de X es* $2N(3/4)=3N/2$. *Ejercicio 2* a) Los valores iniciales usados para *dbinom* en el ejemplo de arriba son $N=5$ y $p=0.3$. ¿Cuál es la probabilidad de alcanzar un estado absorbente en una generación en este caso? *El resultado se obtiene sumando las probabilidades de observar 0 o 2N (en el ejemplo 2N=10) alelos en una generación partiendo de* $p=0.3$. *Es decir 0.0282475249 + 0.0000059049. Notamos que el segundo valor, que es la probabilidad de pasar de 3 a 10 copias de A en una generación, es extremadamente bajo, o sea que la suma es casi igual al primer valor, que es la probabilidad de pasar de 3 a 0 copias de A en una generación.* b) usar el código provisto, modificando los valores iniciales, para averiguar cómo pueden modificarse los parámetros anteriores para aumentar o reducir la probabilidad de alcanzar un estado absorbente en una generación. *El código se basa en dos valores, *$N$ y $p$ *(que llamamos "pr" en el código porque "p" tiene otros usos). Cambiando esos valores (de a uno o en combinación), es fácil constatar que los estados absorbentes se obtienen con mayor probabilidad reduciendo* $N$ *y/o acercando* $p$ *a uno de los valores extremos. De manera complementaria, se puede reducir la probabilidad de fijación o pérdida de A aumentando el tamaño de la población *$N$ *y/o alejando* $p$ *de 0 o 1, es decir acercándolo al valor medio de* $0.5$.