Los modelos aditivos generalizados (GAMs) utilizan métodos no paramétricos para ajustar la relación entre dos variables evitando restringir esta relación a una forma particular. Pueden utilizarse una variedad de métodos de ajuste (entre ellos los cubic splines propuestos por Schluter en 1988) y distintas formas de determinar cuál es su complejidad óptima (en general diferentes formas de validación cruzada). Por otra parte, utilizan métodos propios de los modelos lineares generalizados para estimar la significancia del modelo.
No es el objetivo del curso aprender a ajustar GAMs, sino para utilizarlos como una herramienta para la visualización y para reconocer las diferentes formas de selección (lineal, cuadrática, correlacional) y sus combinaciones. Por ese motivo, no veremos pasos como la detección de la colinealidad (concurvidad en GAMs) o la selección de modelos, ya que suponemos que los estamos utilizando para representar el resultado de un análisis de selección fenotípica previamente analizado con la metodología clásica de Lande & Arnold (1983).
Los datos de este ejemplo fueron obtenidos de:
Benitez‐Vieyra, S., Medina, A.M., Glinos, E. & Cocucci, A.A. 2006. Pollinator-mediated selection on floral traits and size of floral display in Cyclopogon elatus, a sweat bee-pollinated orchid. Functional Ecology 20, 948–957.
library(mgcv)
library(knitr)
data <- read.table("cyclop.txt", header = TRUE)
data <- na.omit(data) # evitar los datos faltantes
La distribución de la variable respuesta (la adecuación) es crucial porque los GAMs con modelo generalizados que usan muchas distribuciones además de la Gaussiana. La elección de la distribución es a priori y depende de la forma en que se generaron los datos (en nuestro caso, la medida de adecuación). Cada distribución posee una serie limitada de funciones de enlace que son utilizadas en el modelo.
Distribución | Enlace canónico | Origen |
---|---|---|
Normal | identidad | Caracteres métricos y continuos. |
Poisson | log | Valores enteros entre 0 y un número indeterminado. Ej. número de hijos, de semillas, de granos de polen, etc. |
Binomial | logit | Conteos distribuidos entre 0 y un número finito conocido. Ej. proporción de fructificación, proporicón de huevos que llegan a eclosionar, etc. |
Bernoulli | logit | Caso especial del anterior. Toman sólo valores de 0 y 1. Ej. supervivencia. |
Cada distribución se caracteriza por la relación entre su parámetro de posición y su parámetro de dispersión. Muchas veces esta relación es violada porque hay más variabilidad que la esperada para ese tipo de variables, originando sobredispersión. Veremos como determinar la presencia de sobredispersión examinando el parámetro \(\phi\) y las posibles soluciones.
Distribución | Origen |
---|---|
Poisson | Ajuste por cuasiverosimilitud. Distribución binomial negativa |
Binomial | Ajuste por cuasiverosimilitud |
Bernoulli | No presenta sobredispersión. |
Veamos las variables respuesta en nuestra tabla
kable(head(data), digits = 3)
nectary.depth | flower.number | largo.ext | frutos | fruct | exported.pollinaria | prop.pol | |
---|---|---|---|---|---|---|---|
1 | 4.790 | 64 | 2.300 | 59 | 0.922 | 26 | 0.500 |
3 | 5.490 | 91 | 2.391 | 75 | 0.824 | 38 | 0.437 |
4 | 4.410 | 32 | 2.188 | 25 | 0.781 | 14 | 0.452 |
5 | 4.345 | 78 | 2.586 | 76 | 0.974 | 25 | 0.321 |
6 | 4.905 | 47 | 2.241 | 45 | 0.957 | 30 | 0.667 |
7 | 4.630 | 49 | 2.064 | 41 | 0.837 | 21 | 0.447 |
Exported.pollinaria es un número entero, representa el número de paquetes de polen que fueron exportados por planta, por lo que esperamos que se distribuya como Poisson.
Prop.pol es una proporción: polinarios exportados / flores producidas. Por lo tanto esperamos que se distribuya como Binomial.
Los GAMs ajustan funciones no paramétricas en vez de parámetros, a través de la función gam del paquete mgcv. family = quasipoisson
indica que estimaremos un modelo para una variable respuesta de tipo Poisson y mediante cuasiverosimilitud. Haremos esto en primer lugar para checar la presencia de sobredispersión.
fit1 <- gam(exported.pollinaria ~ s(nectary.depth, bs = "cr") +
s(flower.number, bs = "cr"),
data = data, family = quasipoisson)
summary(fit1) ## revisar el parámetro Scale est (phi).
##
## Family: quasipoisson
## Link function: log
##
## Formula:
## exported.pollinaria ~ s(nectary.depth, bs = "cr") + s(flower.number,
## bs = "cr")
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.39594 0.04321 55.45 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(nectary.depth) 2.039 2.553 9.085 6.83e-05 ***
## s(flower.number) 7.264 8.007 45.695 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.822 Deviance explained = 82.7%
## GCV = 2.0077 Scale est. = 1.6725 n = 116
# Al ser Scale est < 2 podemos reajustar por Poisson
fit2 <- gam(exported.pollinaria ~ s(nectary.depth, bs = "cr") +
s(flower.number, bs = "cr"),
data = data, family = poisson)
summary(fit2)
##
## Family: poisson
## Link function: log
##
## Formula:
## exported.pollinaria ~ s(nectary.depth, bs = "cr") + s(flower.number,
## bs = "cr")
##
## Parametric coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.38853 0.03416 69.92 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df Chi.sq p-value
## s(nectary.depth) 2.391 2.989 40.99 <2e-16 ***
## s(flower.number) 7.943 8.536 610.73 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.825 Deviance explained = 83%
## UBRE = 0.83706 Scale est. = 1 n = 116
Veremos diferentes opciones
## mediante el paquete visreg
library(visreg)
layout(matrix(1:2,1,2))
visreg(fit2, xvar = "nectary.depth", scale = "response", partial = FALSE,
ylim = range(data$exported.pollinaria))
points(exported.pollinaria ~ nectary.depth, data = data, pch = 19)#puntos observados
visreg(fit2, xvar = "flower.number", scale = "response", partial = FALSE,
ylim = range(data$exported.pollinaria))
points(exported.pollinaria ~ flower.number, data = data, pch = 19)#puntos observados
layout(1)
## mediante los paquetes tidymv + ggplot2
library(patchwork)
library(tidymv)
library(ggplot2)
g1 <- plot_smooths(fit2, series = nectary.depth, transform = exp) +
geom_point(data=data, aes(x = nectary.depth, y = exported.pollinaria)) +
theme_linedraw()
g2 <- plot_smooths(fit2, series = flower.number, transform = exp) +
geom_point(data=data, aes(x = flower.number, y = exported.pollinaria)) +
theme_linedraw()
g1 + g2
## mediante la función vis.gam de mgcv
layout(matrix(1:2,1,2))
vis.gam(fit2, view = c("flower.number", "nectary.depth"), type = "response",
plot.type = "contour", color = "cm")
points(data$flower.number, data$nectary.depth, pch = 16, col ="grey")
vis.gam(fit2, view = c("flower.number", "nectary.depth"), type = "response",
plot.type = "persp", color = "cm", phi = 10, theta = -45,
ticktype = "detailed") -> pp
layout(1)
## mediante la función predict_gam de tidymv
library(tidymv)
library(viridis)
library(ggplot2)
## exp es usada para invertir el enlace típico de los modelos Poisson
ggplot(predict_gam(fit2), aes(flower.number, nectary.depth, z = exp(fit))) +
geom_raster(aes(fill = exp(fit))) +
geom_contour(colour = "white") +
scale_fill_viridis(name = "pol", option = "viridis") +
theme_minimal() + geom_point(data = data, aes(flower.number, nectary.depth, z=NULL), color = "grey")
family = quasibinomial
indica que estimaremos un modelo para una variable respuesta de tipo Poisson y mediante cuasiverosimilitud. Haremos esto en primer lugar para checar la presencia de sobredispersión.
resp <- cbind(data$exported.pollinaria, data$flower.number - data$exported.pollinaria)
fit3 <- gam(resp ~ s(nectary.depth, bs = "cr") +
s(flower.number, bs = "cr"),
data = data, family = quasibinomial)
summary(fit3) ## revisar el parámetro Scale est (phi).
##
## Family: quasibinomial
## Link function: logit
##
## Formula:
## resp ~ s(nectary.depth, bs = "cr") + s(flower.number, bs = "cr")
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.72300 0.06262 -11.54 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(nectary.depth) 1.972 2.477 9.485 5.44e-05 ***
## s(flower.number) 4.197 5.001 2.147 0.065 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.285 Deviance explained = 31.3%
## GCV = 2.9498 Scale est. = 2.6666 n = 116
# Al ser Scale est < 2 nos quedamos con el modelo que controla
# la sobredispersión
Veremos diferentes opciones
## mediante el paquete visreg
library(visreg)
layout(matrix(1:2,1,2))
visreg(fit3, xvar = "nectary.depth", scale = "response", partial = FALSE,
ylim = range(data$prop.pol))
points(prop.pol ~ nectary.depth, data = data, pch = 19)#puntos observados
visreg(fit3, xvar = "flower.number", scale = "response", partial = FALSE,
ylim = range(data$prop.pol))
points(prop.pol ~ flower.number, data = data, pch = 19)#puntos observados
layout(1)
## mediante los paquetes tidymv + ggplot2
library(patchwork)
library(tidymv)
library(ggplot2)
library(boot) #necesaria para inv.logit
g1 <- plot_smooths(fit3, series = nectary.depth, transform = inv.logit) +
geom_point(data=data, aes(x = nectary.depth, y = prop.pol)) +
theme_linedraw()
g2 <- plot_smooths(fit3, series = flower.number, transform = inv.logit) +
geom_point(data=data, aes(x = flower.number, y = prop.pol)) +
theme_linedraw()
g1 + g2
Veremos diferentes opciones
## mediante la función vis.gam de mgcv
layout(matrix(1:2,1,2))
vis.gam(fit3, view = c("flower.number", "nectary.depth"), type = "response",
plot.type = "contour", color = "cm")
points(data$flower.number, data$nectary.depth, pch = 16, col ="grey")
vis.gam(fit3, view = c("flower.number", "nectary.depth"), type = "response",
plot.type = "persp", color = "cm", phi = 10, theta = -45,
ticktype = "detailed") -> pp
layout(1)
## mediante la función predict_gam de tidymv
library(tidymv)
library(viridis)
library(ggplot2)
library(boot)
## inv.logit es usada para invertir el enlace típico de los modelos Binomiales
ggplot(predict_gam(fit3), aes(flower.number, nectary.depth, z = inv.logit(fit))) +
geom_raster(aes(fill = exp(fit))) +
geom_contour(colour = "white") +
scale_fill_viridis(name = "pol", option = "viridis") +
theme_minimal() + geom_point(data = data, aes(flower.number, nectary.depth, z=NULL), color = "grey")
Los datos de este ejercicio fueron obtenidos de:
Carmona D. 2006. Evolución fenotípica de la resistencia de Datura stramonium contra su herbívoro folívoro Epitrix sp. Tesis de Maestría. Instituto de Ecología, Universidad Nacional Autónoma de México.
Los datos de este ejercicio fueron obtenidos de:
Karn MN, Penrose LS. 1951. Birth weight and gestation time in relation to maternal age, parity and infant survival. Ann. Eugen. 16:147–164.
Los artículos publicados en el Annals of Eugenics (1925–1954) a menudo están inundados de pseudociencia para intentar justificar la existencia de desigualdades de clase, raza, etnia y género, asignando a estas diferencias un origen puramente “natural”. Esta historia de opresión nunca debe ser olvidada.