En esta práctica aprenderemos a ajustar el modelo GLM de Poisson en R a través de distintos ejemplos que consideran particularidades como el uso de offset, la sobredispersión, el exceso de ceros o la interacción entre predictores. En ellos se trabajará sobre la interpretación del modelo, su validez, inferencias sobre los parámetros y detección de puntos influyentes y/o atípicos. El objetivo es que el estudiante adquiera un dominio básico para abordar problemas similares en la práctica.
Simulamos datos de una variable de respuesta \(Y \sim Poisson(\lambda)\), donde:
\[ \log(\lambda) = \beta_0 + \beta_1 x \]
con \(\beta_0 = -1\) y \(\beta_1 = 0.3\), y donde la covariable \(x \sim U(0,10)\).
# Cargar librerías necesarias
library(ggplot2)
library(boot)
library(statmod)
library(car)
## Loading required package: carData
##
## Attaching package: 'car'
## The following object is masked from 'package:boot':
##
## logit
library(glmtoolbox)
##
## Attaching package: 'glmtoolbox'
## The following object is masked from 'package:boot':
##
## envelope
# Función para generar los datos
gen_dat <- function(n, b0, b1) {
x <- runif(n=n, min=0, max=10) # Covariable x ~ Uniforme(0,1)
lambda <- exp(b0 + b1 * x) # Relación log-lineal con Poisson
y <- rpois(n=n, lambda=lambda) # Respuesta Poisson
data.frame(y=y, x=x)
}
# Generando datos para n = 150
n <- 150
datos <- gen_dat(n=n, b0=-1, b1=0.3)
# Visualizar los primeros datos
head(datos)
# Gráfico de dispersión de los datos simulados
ggplot(datos, aes(x = x, y = y)) +
geom_point(alpha = 0.6, color = "blue") + # Datos originales en azul
labs(title = "Datos simulados: y ~ Poisson(lambda)",
x = "Covariable x",
y = "Número de eventos (y)") +
theme_minimal()
# Ajustamos un modelo GLM con familia Poisson
model_pois <- glm(y ~ x, family = poisson(link="log"), data = datos)
# Resumen del modelo
summary(model_pois)
##
## Call:
## glm(formula = y ~ x, family = poisson(link = "log"), data = datos)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.95965 0.17304 -5.546 2.92e-08 ***
## x 0.29366 0.02162 13.581 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 374.23 on 149 degrees of freedom
## Residual deviance: 130.34 on 148 degrees of freedom
## AIC: 477.98
##
## Number of Fisher Scoring iterations: 5
# Extraemos los coeficientes del modelo
coef_ajustados <- coef(model_pois)
coef_exponenciales <- exp(coef_ajustados) # Exponencial de los coeficientes
# Tabla de comparación
coef_comparacion <- data.frame(
Parámetro = c("Intercept", "Pendiente"),
Simulado = c(-1, 0.3),
Estimado = coef_ajustados,
`Exp(coef)` = coef_exponenciales
)
coef_comparacion
## Parámetro Simulado Estimado Exp.coef.
## (Intercept) Intercept -1.0 -0.9596544 0.3830253
## x Pendiente 0.3 0.2936562 1.3413226
# En un modelo de Poisson, los coeficientes NO representan diferencias absolutas, sino diferencias en el logaritmo de la tasa esperada de eventos. Para interpretarlos, aplicamos la función exponencial
# El Intercept representa la tasa de incidencia esperada cuando x = 0.
#La pendiente (exp(beta1)) es la razón de tasas de incidencia a veces denominada IRR.
#Si beta1=0.29, entonces exp(0.3)=1.33, lo que significa que por cada unidad adicional en x, la tasa esperada de y aumenta en un 33%.
# Predicciones del modelo
datos$pred <- predict(model_pois, type = "response")
# Graficamos los valores observados vs. ajustados
ggplot(datos, aes(x = x, y = y)) +
geom_point(alpha = 0.5, color = "blue") +
geom_line(aes(y = pred), color = "red", size = 1) +
labs(title = "Valores Observados vs. Ajustados",
x = "Variable Predictora (x)",
y = "Conteo de Eventos (y)",
subtitle = "Los valores predichos (línea roja) siguen la tendencia de los datos") +
theme_minimal()
# Coeficiente estimado positivo
Tarea. Genera un grid de tamaños mustrales (n) y evalua cómo el tamaño de muestra afecta a la estimación del de los coeficientes del modelo. Considera medidas como el valor medio de los coeficientes estimados o el error cuadrático medio, incluye gráficos de dispersión de estas medidas en función de los valores de n.
# Calcular residuos
datos$res_pearson <- boot::glm.diag(model_pois)$rp # Residuos de Pearson
datos$res_deviance <- boot::glm.diag(model_pois)$rd # Residuos de Deviance
datos$res_quantile <- statmod::qresid(model_pois) # Residuos Cuantiles
# Ver primeras filas con residuos
head(datos)
## y x pred res_pearson res_deviance res_quantile
## 1 1 0.2452403 0.4116269 0.9224386 0.7781787 0.9864178
## 2 7 8.1791573 4.2300154 1.3542939 1.2364234 1.1538829
## 3 0 2.8948295 0.8962196 -0.9523019 -1.3467582 -1.7057854
## 4 1 6.6612498 2.7086835 -1.0423785 -1.1982992 -1.2594186
## 5 8 9.3558075 5.9759090 0.8376106 0.7959680 0.6838174
## 6 1 1.9687596 0.6828255 0.3861674 0.3609048 0.2956270
# a) Dispersión de los residuos en función de x
ggplot(datos, aes(x = x)) +
geom_point(aes(y = res_pearson), color = "blue", alpha = 0.6) +
geom_point(aes(y = res_deviance), color = "red", alpha = 0.6) +
geom_point(aes(y = res_quantile), color = "green", alpha = 0.6) +
labs(title = "Comparación de Residuos: Pearson (azul), Deviance (rojo) y Cuantiles (verde)",
x = "Covariable x", y = "Residuos") +
theme_minimal()
#Los residuos de Pearson pueden ser más grandes en valores extremos.
#Los residuos de Deviance suelen estar más ajustados en la región central.
#Los residuos Cuantiles están más distribuidos en torno a 0 y deberían parecer normalizados.
# b) QQ plots para evaluar normalidad
par(mfrow = c(1, 3))
qqPlot(datos$res_pearson, dist = "norm", mean = 0, sd = 1, main = "QQ-Plot: Residuos Pearson")
## [1] 128 76
qqPlot(datos$res_deviance, dist = "norm", mean = 0, sd = 1, main = "QQ-Plot: Residuos Deviance")
## [1] 128 92
qqPlot(datos$res_quantile, dist = "norm", mean = 0, sd = 1, main = "QQ-Plot: Residuos Cuantiles")
## [1] 128 92
#Residuos de Pearson y Deviance pueden mostrar más valores atípicos.
#Residuos Cuantiles deben seguir mejor la normalidad.
# c) Comparación de histogramas de residuos
par(mfrow = c(1, 3))
hist(datos$res_pearson, main = "Histograma: Residuos Pearson", col = "blue", breaks = 20)
hist(datos$res_deviance, main = "Histograma: Residuos Deviance", col = "red", breaks = 20)
hist(datos$res_quantile, main = "Histograma: Residuos Cuantiles", col = "green", breaks = 20)
#Residuos de Pearson pueden mostrar más variabilidad.
#Residuos de Deviance siguen la distribución de Poisson pero pueden ser sesgados.
#Residuos Cuantiles deberían parecer normales.
Analizar los accidentes de tráfico ocurridos en 30 ciudades a lo largo de un año, teniendo en cuenta el número de vehículos registrados en cada ciudad y el tipo de carretera predominante en la ciudad (urbana, autopista o secundaria).
getwd()
## [1] "C:/Users/Yolanda/OneDrive - Universidad de Valladolid/Docencia/MASTER25/Practicas/GLM"
load(file="C:/Users/Yolanda/OneDrive - Universidad de Valladolid/Docencia/MASTER25/Practicas/GLM/Datos/traffic_data.RData")
head(traffic_data)
## city vehicles road_type lambda accidents
## 1 Ciudad_1 150913 Urbana 3.5 7
## 2 Ciudad_2 396270 Autopista 1.8 9
## 3 Ciudad_3 210399 Secundaria 2.5 5
## 4 Ciudad_4 442679 Autopista 1.8 10
## 5 Ciudad_5 470829 Urbana 3.5 17
## 6 Ciudad_6 32323 Secundaria 2.5 0
# Distribución de accidentes por tipo de carretera
ggplot(traffic_data, aes(x = road_type, y = accidents)) +
geom_boxplot(fill = "skyblue", alpha = 0.6) +
labs(title = "Distribución de accidentes por tipo de carretera",
x = "Tipo de Carretera", y = "Número de Accidentes")
# Relación entre número de vehículos y número de accidentes
ggplot(traffic_data, aes(x = vehicles, y = accidents, color = road_type)) +
geom_point(size = 3) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Número de accidentes vs. Número de vehículos",
x = "Número de Vehículos Registrados", y = "Número de Accidentes")
## `geom_smooth()` using formula = 'y ~ x'
# Modelo de Poisson sin offset
mod_no_offset <- glm(accidents ~ road_type, family = poisson, data = traffic_data)
# Nota: road_type se está tratando como un factor, dondo Autopista es la categoría de referencia. Orden alfabético
# Resumen del modelo
summary(mod_no_offset)
##
## Call:
## glm(formula = accidents ~ road_type, family = poisson, data = traffic_data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.66501 0.16440 10.128 < 2e-16 ***
## road_typeSecundaria -0.01635 0.21508 -0.076 0.939
## road_typeUrbana 0.76048 0.18393 4.135 3.56e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 125.365 on 29 degrees of freedom
## Residual deviance: 91.166 on 27 degrees of freedom
## AIC: 199.26
##
## Number of Fisher Scoring iterations: 5
#Este modelo no tiene en cuenta que algunas ciudades tienen más vehículos que otras, lo que puede sesgar la interpretación. No vamos a entrar en la interpretacaión de los coeficientes en este modelo
Un offset en un GLM es un término que se incluye en la ecuación del modelo cuya contribución al predictor lineal es fija y no se estima a partir de los datos. El uso de offsets es adecuado cuando la respuesta es un conteo relativo a un denominador conocido, como tiempo de exposición o número de unidades observadas. También puede resultar útil en situaciones donde una variable ya está en escala logarítmica y no debe ser estimada, en estos casos se usa como offset para mantener la relación adecuada.
El modelo estándar de Poisson es:
\[ \log(\lambda) = \beta_0 + \beta_1 x \]
Si queremos modelizar una tasa, necesitamos incluir un offset \( (t) \), donde \( t \) es el tiempo de exposición o el denominador de la tasa:
\[ \log(\lambda) = \beta_0 + \beta_1 x + \log(t). \]
Aquí, el término \( (t) \) no necesita un coeficiente estimado porque ya es un valor conocido, porque su efecto es fijo. Estamos modelizando la tasa \(\lambda/t\), y el término \(\log(t)\) simplemente ajusta la escala de la respuesta.
mod_offset <- glm(accidents ~ road_type + offset(log(vehicles)),
family = poisson,
data = traffic_data)
# Resumen del modelo
summary(mod_offset) # Cambio en los coeficientes del modelo
##
## Call:
## glm(formula = accidents ~ road_type + offset(log(vehicles)),
## family = poisson, data = traffic_data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -10.9614 0.1644 -66.676 < 2e-16 ***
## road_typeSecundaria 0.1815 0.2151 0.844 0.399
## road_typeUrbana 0.7292 0.1839 3.964 7.36e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 53.315 on 29 degrees of freedom
## Residual deviance: 29.996 on 27 degrees of freedom
## AIC: 138.09
##
## Number of Fisher Scoring iterations: 4
#El offset ajusta por la diferencia en el número de vehículos en cada ciudad
# log(vehicles) actúa como offset para modelizar la tasa de accidentes por vehículo en lugar del número absoluto de accidentes.
# En un modelo de Poisson, los coeficientes NO representan diferencias absolutas, sino diferencias en el logaritmo de la tasa esperada de eventos. Para interpretarlos, aplicamos la función exponencial
# Para road_type=urbana (significativa):
#Interpretación en la escala logarítmica: El logaritmo de la tasa esperada de eventos es 0.73 unidades mayor que en la categoría de referencia (autopista)
#¿Interpretación en términos de tasa esperada?
anova(mod_no_offset, mod_offset, test = "LRT") # ¿Qué ocurre? ¿Menor deviance?
## Analysis of Deviance Table
##
## Model 1: accidents ~ road_type
## Model 2: accidents ~ road_type + offset(log(vehicles))
## Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1 27 91.166
## 2 27 29.996 0 61.17
AIC(mod_no_offset, mod_offset)# ¿Menor AIC?
## df AIC
## mod_no_offset 3 199.2638
## mod_offset 3 138.0940
p_value <- pchisq ( deviance ( mod_offset ) , df.residual ( mod_offset ) , lower.tail = FALSE )
cat ( " Deviance : " , deviance ( mod_offset ) , " P-value : " , p_value )
## Deviance : 29.99593 P-value : 0.3143366
# El efecto del tipo de carretera es estadísticamente significativo.
# El offset permite comparar las tasas de accidentes por vehículo, eliminando el sesgo de ciudades con mayor número de automóviles.
library(dplyr)
##
## Attaching package: 'dplyr'
## The following object is masked from 'package:car':
##
## recode
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
traffic_data <- traffic_data %>%
mutate(pred_accidents = predict(mod_offset, type = "response")) # crea nuevas variables o modifica variables existentes en un dataframe sin alterar las demás columnas.
# Gráfico de predicciones
ggplot(traffic_data, aes(x = road_type, y = pred_accidents)) +
geom_boxplot(fill = "orange", alpha = 0.6) +
labs(title = "Predicción del Modelo de Poisson con Offset",
x = "Tipo de Carretera", y = "Tasa de Accidentes Ajustada")
Durante la temporada de desove, las hembras de una especie de cangrejo migran a la costa para realizar la puesta, con un macho enganchado en su cola. Cavan en la arena, donde depositan los huevos que son fertilizados externamente, tanto por el macho enganchado a la hembra como por otros machos que se reúnen alrededor de la pareja. Estos otros machos reciben el nombre de satélites. El archivo proporcionado contiene datos de un estudio realizado sobre esta especie en el golfo de Mexico.
Se desea modelizar el número de satélites de las cangrejas en función del peso, color, ancho de caparazón y el estado de la espina dorsal. En el modelo elegido, ¿se puede concluir que el peso del caparazón es significativamente mayor a 0.1? ¿Se cumplen las asunciones del modelo? ¿Son válidos estos resultados?
# Cargar datos desde URL
url <- "http://users.stat.ufl.edu/~aa/glm/data/Crabs.dat"
Crabs <- read.table(url, header=TRUE)
#weight: Peso del cangrejo (kg)
#width: Ancho del caparazón (cm)
#color: Clasificación del color (1: Claro, 4: Oscuro)
#spine: Condición de la espina dorsal (1: Buen estado, 3: Ambas desgastadas)
#y: number of satelites
# Convertir variables categóricas a factores
Crabs$color <- as.factor(Crabs$color)
Crabs$spine <- as.factor(Crabs$spine)
# Ver primeras filas
head(Crabs)
## crab y weight width color spine
## 1 1 8 3.05 28.3 2 3
## 2 2 0 1.55 22.5 3 3
## 3 3 9 2.30 26.0 1 1
## 4 4 0 2.10 24.8 3 3
## 5 5 4 2.60 26.0 3 3
## 6 6 0 2.10 23.8 2 3
fit_poisson <- glm(y ~ weight + color + width + spine,
family = poisson(link="log"), data=Crabs)
# Resumen del modelo
summary(fit_poisson)
##
## Call:
## glm(formula = y ~ weight + color + width + spine, family = poisson(link = "log"),
## data = Crabs)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.36180 0.96655 -0.374 0.70817
## weight 0.49647 0.16626 2.986 0.00283 **
## color2 -0.26485 0.16811 -1.575 0.11515
## color3 -0.51371 0.19536 -2.629 0.00855 **
## color4 -0.53086 0.22692 -2.339 0.01931 *
## width 0.01675 0.04892 0.342 0.73207
## spine2 -0.15037 0.21358 -0.704 0.48139
## spine3 0.08728 0.11993 0.728 0.46674
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 632.79 on 172 degrees of freedom
## Residual deviance: 549.59 on 165 degrees of freedom
## AIC: 920.88
##
## Number of Fisher Scoring iterations: 6
# width y spine no resultan significativas
# ¿Cuántas observaciones hay? ¿Qué relación hay con el número de gl de residual y null deviance?
# ¿Qué indica 'Number of Fisher Scoring Iterations'?
fit_poisson2 <- glm(y ~ weight + color ,
family = poisson(link="log"), data=Crabs)
# Resumen del modelo
summary(fit_poisson2)
##
## Call:
## glm(formula = y ~ weight + color, family = poisson(link = "log"),
## data = Crabs)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.04978 0.23315 -0.214 0.8309
## weight 0.54618 0.06811 8.019 1.07e-15 ***
## color2 -0.20511 0.15371 -1.334 0.1821
## color3 -0.44980 0.17574 -2.560 0.0105 *
## color4 -0.45205 0.20844 -2.169 0.0301 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 632.79 on 172 degrees of freedom
## Residual deviance: 551.80 on 168 degrees of freedom
## AIC: 917.1
##
## Number of Fisher Scoring iterations: 6
# En un modelo de Poisson, los coeficientes NO representan diferencias absolutas, sino diferencias en el logaritmo de la tasa esperada de eventos. Para interpretarlos, aplicamos la función exponencial (tasa esperada)
exp(coef(fit_poisson2))
## (Intercept) weight color2 color3 color4
## 0.9514381 1.7266442 0.8145557 0.6377580 0.6363225
exp(confint(fit_poisson2)) # Cálculo de intervalos de confianza ¿Cómo? ¿Que información contiene cada columna de la tabla de coeficientes estimados?
## 2.5 % 97.5 %
## (Intercept) 0.6009718 1.5001076
## weight 1.5076222 1.9689147
## color2 0.6089529 1.1138967
## color3 0.4544755 0.9066422
## color4 0.4218499 0.9575813
# Por cada unidad adicional de peso, la tasa esperada de eventos aumenta en un (exp(0.55)) 73%, manteniendo constante el color.
# Para color 3:
#Interpretación en la escala logarítmica: el logaritmo de la tasa esperada de eventos es 0.45 unidades menor que para los individuos con Color1 (categoría de referencia), manteniendo constante el peso.
#Interpretación en términos de tasa esperada: Aplicamos la exponencial exp(-0.45)=0.64. Esto significa que los individuos con Color3 tienen una tasa esperada de eventos un 36% menor (1-0.64) en comparación con los de Color1, manteniendo constante el peso.
Crabs$predicted <- predict(fit_poisson2, type="response")
# Gráfico con jitter para visualizar mejor las coincidencias
ggplot(Crabs, aes(x = weight, y = predicted, color = color)) +
geom_point(aes(y = y)) +
geom_line(size = 1) +
labs(title = "Predicción del Número de Satélites",
x = "Peso",
y = "Número Esperado de Satelite") +
theme_minimal()
# Calculando el estadistico y su valor-P para contrastar H0: beta_1=0.1; H1: beta_1>0.1
z <- (0.546 - 0.1) / 0.068
z
## [1] 6.558824
pnorm(q=z, lower.tail=FALSE) # valor-P
## [1] 2.711697e-11
# El p-valor es menor grande que un nivel de significanción usual, se rechaza H0
# Test de bondad de ajuste para el modelo de Poisson
pchisq(fit_poisson2$deviance, df=fit_poisson2$df.residual, lower.tail=FALSE)
## [1] 2.08043e-42
# Posibles causas: falta de linealidad en el logaritmo de la respuesta, exceso de variabilidad en la misma, o quizás ausencia en el modelo de variables importantes que debieran tenerse en cuenta (y que el investigador ignora)
# 1. Comparación entre media y varianza
mean_sat <- mean(Crabs$y)
var_sat <- var(Crabs$y)
cat("Media de premios:", mean_sat, " - Varianza de premios:", var_sat, "\n")
## Media de premios: 2.919075 - Varianza de premios: 9.912018
#Consecuencias de la Sobredispersión:
# Estimaciones Sesgadas: En presencia de sobredispersión, las estimaciones de los parámetros del modelo de Poisson pueden estar sesgadas.
# Errores Estándar Subestimados: Los errores estándar de las estimaciones pueden subestimarse, llevando a inferencias estadísticas incorrectas, como intervalos de confianza demasiado estrechos o pruebas de hipótesis que rechazan incorrectamente la hipótesis nula.
# 2. Relación entre deviance y grados de libertad
deviance_ratio <- deviance(fit_poisson2) / df.residual(fit_poisson2)
cat("Razón Deviance/GL:", deviance_ratio, "\n")
## Razón Deviance/GL: 3.284553
if (deviance_ratio > 1.5) {
cat("Indicación de sobre-dispersión en el modelo.\n")
} else {
cat("No hay evidencia fuerte de sobre-dispersión.\n")
}
## Indicación de sobre-dispersión en el modelo.
# 3. Test de sobre-dispersión de Cameron & Trivedi. Para GLM de Poisson
# H1: Var > media
# Estadístico basado en los residuos de Pearson, bajo H0 chi1 con n-p
pearson_chisq <- sum(residuals(fit_poisson2, type = "pearson")^2)
dispersion_ratio <- pearson_chisq / df.residual(fit_poisson2)
p_value_dispersion <- pchisq(pearson_chisq, df.residual(fit_poisson2), lower.tail=FALSE)
cat("Pearson Chi2:", pearson_chisq, " - p-value:", p_value_dispersion, "\n")
## Pearson Chi2: 534.8636 - p-value: 7.564098e-40
if (p_value_dispersion < 0.05) {
cat("El test de sobre-dispersión indica que el modelo de Poisson puede no ser adecuado.\n")
}
## El test de sobre-dispersión indica que el modelo de Poisson puede no ser adecuado.
# 4. Paquete espefífico test de sobredispersión
# Test de sobredispersión
#library(AER)
#dispersiontest(modPois2)
# Cuando se detecta sobredispersión en un modelo de Poisson, se puede utilizar como alternativa la regresión binomial negativa.
¿Son válidas las inferencias e interpretación del modelo que hemos hecho?
En una escuela secundaria, se ha recopilado información sobre el número de premios académicos que han obtenido los estudiantes en un año. Se quiere analizar qué factores influyen en la cantidad de premios ganados y si ciertas características de los estudiantes pueden predecir su desempeño en la obtención de premios.
# Cargar datos
awards <- read.csv("https://stats.idre.ucla.edu/stat/data/poisson_sim.csv")
awards <- as.data.frame(awards)
# Convertir la variable de programa a factor
awards <- within(awards, {
prog <- factor(prog, levels = 1:3, labels = c("General", "Academic", "Vocational"))
})
summary(awards)
## id num_awards prog math
## Min. : 1.00 Min. :0.00 General : 45 Min. :33.00
## 1st Qu.: 50.75 1st Qu.:0.00 Academic :105 1st Qu.:45.00
## Median :100.50 Median :0.00 Vocational: 50 Median :52.00
## Mean :100.50 Mean :0.63 Mean :52.65
## 3rd Qu.:150.25 3rd Qu.:1.00 3rd Qu.:59.00
## Max. :200.00 Max. :6.00 Max. :75.00
ggplot(awards, aes(x = math, y = num_awards, color = prog)) +
geom_point(alpha = 0.7, size = 3) +
labs(title = "Número de Premios en función del Puntaje de Matemáticas",
x = "Puntaje en Matemáticas",
y = "Número de Premios",
color = "Programa") +
theme_minimal() +
theme(legend.position = "top")
# Media y desviación estándar del número de premios
mean_awards <- mean(awards$num_awards)
sd_awards <- sd(awards$num_awards)
cat("Media de premios:", mean_awards, " - Desviación estándar:", sd_awards, "\n")
## Media de premios: 0.63 - Desviación estándar: 1.052921
# Media y desviación estándar por tipo de programa
with(awards, tapply(num_awards, prog, function(x) {
sprintf("M (SD) = %1.2f (%1.2f)", mean(x), sd(x))
}))
## General Academic Vocational
## "M (SD) = 0.20 (0.40)" "M (SD) = 1.00 (1.28)" "M (SD) = 0.24 (0.52)"
# Ajuste de modelos GLM de Poisson
fit_glm_0 <- glm(num_awards ~ 1, data=awards, family=poisson)
fit_glm_1 <- glm(num_awards ~ math, data=awards, family=poisson)
fit_glm_2 <- glm(num_awards ~ math + prog, data=awards, family=poisson)
# Resumen de los modelos
summary(fit_glm_0)
##
## Call:
## glm(formula = num_awards ~ 1, family = poisson, data = awards)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.46204 0.08909 -5.186 2.14e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 287.67 on 199 degrees of freedom
## Residual deviance: 287.67 on 199 degrees of freedom
## AIC: 465.73
##
## Number of Fisher Scoring iterations: 6
summary(fit_glm_1)
##
## Call:
## glm(formula = num_awards ~ math, family = poisson, data = awards)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -5.333532 0.591261 -9.021 <2e-16 ***
## math 0.086166 0.009679 8.902 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 287.67 on 199 degrees of freedom
## Residual deviance: 204.02 on 198 degrees of freedom
## AIC: 384.08
##
## Number of Fisher Scoring iterations: 6
summary(fit_glm_2)
##
## Call:
## glm(formula = num_awards ~ math + prog, family = poisson, data = awards)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -5.24712 0.65845 -7.969 1.60e-15 ***
## math 0.07015 0.01060 6.619 3.63e-11 ***
## progAcademic 1.08386 0.35825 3.025 0.00248 **
## progVocational 0.36981 0.44107 0.838 0.40179
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 287.67 on 199 degrees of freedom
## Residual deviance: 189.45 on 196 degrees of freedom
## AIC: 373.5
##
## Number of Fisher Scoring iterations: 6
Tarea. Interpretación del modelo fit_glm_2.
# 1. Comparación entre media y varianza
mean_awards <- mean(awards$num_awards)
var_awards <- var(awards$num_awards)
cat("Media de premios:", mean_awards, " - Varianza de premios:", var_awards, "\n")
## Media de premios: 0.63 - Varianza de premios: 1.108643
# 2. Relación entre deviance y grados de libertad
deviance_ratio <- deviance(fit_glm_2) / df.residual(fit_glm_2)
cat("Razón Deviance/GL:", deviance_ratio, "\n")
## Razón Deviance/GL: 0.9665797
if (deviance_ratio > 1.5) {
cat("Indicación de sobre-dispersión en el modelo.\n")
} else {
cat("No hay evidencia fuerte de sobre-dispersión.\n")
}
## No hay evidencia fuerte de sobre-dispersión.
# 3. Test de sobre-dispersión de Cameron & Trivedi
pearson_chisq <- sum(residuals(fit_glm_2, type = "pearson")^2)
dispersion_ratio <- pearson_chisq / df.residual(fit_glm_2)
p_value_dispersion <- pchisq(pearson_chisq, df.residual(fit_glm_2), lower.tail=FALSE)
cat("Pearson Chiò:", pearson_chisq, " - p-value:", p_value_dispersion, "\n")
## Pearson Chiò: 212.1437 - p-value: 0.203986
if (p_value_dispersion < 0.05) {
cat("El test de sobre-dispersión indica que el modelo de Poisson puede no ser adecuado.\n")
}
# 4. Librería
# Test de sobredispersión
#library(AER)
#dispersiontest(modPois2)
# Comparación de modelos con LRT
anova(fit_glm_1, fit_glm_2, test="LRT")# Tamaño muestral moderado-grande convergencia asintótica a chi2
## Analysis of Deviance Table
##
## Model 1: num_awards ~ math
## Model 2: num_awards ~ math + prog
## Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1 198 204.02
## 2 196 189.45 2 14.572 0.0006852 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Prueba de bondad de ajuste usando deviance
p_value <- pchisq(deviance(fit_glm_2), df.residual(fit_glm_2), lower.tail=FALSE)
cat("Deviance:", deviance(fit_glm_2), " - p-value:", p_value, "\n")
## Deviance: 189.4496 - p-value: 0.6182274
# Cálculo de pseudo R2 de McFadden
logLik_full <- logLik(fit_glm_2)
logLik_null <- logLik(fit_glm_0)
pseudo_R2 <- 1 - (logLik_full / logLik_null)
cat("Pseudo R2 de McFadden:", pseudo_R2, "\n")
## Pseudo R2 de McFadden: 0.2118112
# Otras maneras de calcular R2
#library ( DescTools )
#PseudoR2(fit_glm_2 , which = "all" )
par(mfrow=c(2,2)) # Configurar panel de gráficos
plot(fit_glm_2) # Gráficos de diagnóstico estándar
# Extraer residuos deviance
residuos_dev <- residuals(fit_glm_2, type="deviance")
# Histogramas y gráficos de dispersión
hist(residuos_dev, main="Histograma de Residuos Deviance", col="lightblue", breaks=20)
qqnorm(residuos_dev)
qqline(residuos_dev, col="red", lwd=2)
# Identificación de valores atípicos
outliers <- which(abs(residuos_dev) > 2) # Considerar valores fuera de ñ2
cat("Observaciones con residuos altos:", outliers, "\n")
## Observaciones con residuos altos: 54 157 164 181 199
# Cálculo de medidas de influencia
cooks_d <- cooks.distance(fit_glm_2)
hat_values <- hatvalues(fit_glm_2)
# Identificación de puntos influyentes
threshold_cooks <- 4 / (nrow(awards) - length(coef(fit_glm_2)))
outliers <- which(cooks_d > threshold_cooks)
outliers
## 27 28 36 54 58 87 154 157 181 190 194 199
## 27 28 36 54 58 87 154 157 181 190 194 199
cat("Puntos influyentes identificados:", outliers, "\n")
## Puntos influyentes identificados: 27 28 36 54 58 87 154 157 181 190 194 199
# Visualización de CookâÂÂs Distance
plot(cooks_d, type="h", col="red", lwd=2, ylab="Cook's Distance", xlab="Observación",
main="Distancia de Cook")
abline(h=threshold_cooks, col="blue", lty=2)
# Eliminación de outliers y ajuste de nuevo modelo
awards_clean <- awards[-outliers, ]
fit_glm_2_clean <- glm(num_awards ~ math + prog, data=awards_clean, family=poisson)
summary(fit_glm_2_clean)
##
## Call:
## glm(formula = num_awards ~ math + prog, family = poisson, data = awards_clean)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.26324 0.80904 -7.742 9.82e-15 ***
## math 0.07913 0.01239 6.388 1.68e-10 ***
## progAcademic 1.45359 0.46829 3.104 0.00191 **
## progVocational 0.66336 0.55782 1.189 0.23437
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 235.17 on 187 degrees of freedom
## Residual deviance: 143.19 on 184 degrees of freedom
## AIC: 299.02
##
## Number of Fisher Scoring iterations: 6
Tarea. Compara los resultados del modelo fit_glm_2_clean con fit_glm_2.
awards_clean$predicted <- predict(fit_glm_2_clean, type="response")
# Gráfico con jitter para visualizar mejor las coincidencias
ggplot(awards_clean, aes(x = math, y = predicted, color = prog)) +
geom_point(aes(y = num_awards), alpha = 0.5, position = position_jitter(h = 0.2)) +
geom_line(size = 1) +
labs(title = "Predicción del Número de Premios sin Outliers",
x = "Puntaje en Matemáticas",
y = "Número Esperado de Premios") +
theme_minimal()
Se recopilaron datos sobre 250 grupos de visitantes que acudieron a un parque estatal donde se permite la pesca. A los visitantes se les realizaron algunas preguntas y se obtuvo información sobre las siguientes variables:
El objetivo del estudio es predecir el número de peces capturados por un grupo en función de su composición. Dado que la variable de interés es una variable aleatoria discreta que solo toma valores enteros no negativos, se opta por ajustar un modelo de regresión para datos de conteo.
Al analizar los datos, observamos una cantidad excesiva de ceros, lo cual puede deberse a dos razones principales:
Este fenómeno genera una varianza mucho mayor en los datos en comparación con su media, lo que viola una de las suposiciones clave del modelo de regresión de Poisson.
Debido a esta sobre-dispersión y el exceso de ceros en los datos, se considera la aplicación de modelos alternativos, tales como:
data <-
within(read.csv("C:/Users/Yolanda/OneDrive - Universidad de Valladolid/Docencia/MASTER25/Practicas/GLM/Datos/fish.csv"),
camper <- factor(camper))
attach(data)
###### Descriptive Analysis ######
n <- length(count)
xi <- sort(unique(count))
fi <- table(count)
am <- mean(count)
variance <-var(count)
print(c(am, variance))
## [1] 3.2960 135.3739
plot(
xi,
fi,
type = "h",
lwd = 2,
xlab = "No. of Fishes Caught",
ylab = "Frequency",
main = "Column Diagram of Fishing data"
)
Gotelli & Ellison (2002) analizaron los determinantes biogeográficos de la riqueza de hormigas (Srich) a escala regional (gotelli.csv). Para esto se describieron el tipo de hábitat (Habitat), la latitud (Latitude) y la altitud (Elevation). Proponer un modelo adecuado para el análisis de la riqueza de hormigas a partir de las variables disponibles.
# Cargar paquetes
if (!require(car)){install.packages("car", dependencies=TRUE)}
if (!require(MuMIn)){install.packages("MuMIn", dependencies=TRUE)}
## Loading required package: MuMIn
##
## Attaching package: 'MuMIn'
## The following object is masked from 'package:glmtoolbox':
##
## QIC
library(car)
library(MuMIn)
# Cargar datos
gotelli <- read.csv("C:/Users/Yolanda/OneDrive - Universidad de Valladolid/Docencia/MASTER25/Practicas/GLM/Datos/gotelli.csv")
head(gotelli)
## Site Srich Habitat Latitude Elevation
## 1 TPB 6 Forest 41.97 389
## 2 HBC 16 Forest 42.00 8
## 3 CKB 18 Forest 42.03 152
## 4 SKP 17 Forest 42.05 1
## 5 CB 9 Forest 42.05 210
## 6 RP 15 Forest 42.17 78
# Convertir la variable categórica Habitat a numérica antes de scatterplotMatrix
gotelli$HabitatNum <- as.numeric(as.factor(gotelli$Habitat))
# Visualización inicial
# Crear la matriz de dispersión con la nueva variable numérica
scatterplotMatrix(~ Srich + HabitatNum + Latitude + Elevation, data = gotelli)
pairs(gotelli[,c(2,4,5,6)])
# Ajuste de distribución a los datos
sim.pois <- dpois(x = 0:max(gotelli$Srich), lambda = mean(gotelli$Srich))
plot(table(gotelli$Srich), xlab = "Número de especies", ylab = "Frecuencia")
hist(gotelli$Srich, xlab = "Número de especies", ylab = "Frecuencia relativa", main = "", freq = FALSE)
lines(x = 0:max(gotelli$Srich), y = sim.pois, col = "blue", lwd = 2, type = "b")
var(gotelli$Srich)/mean(gotelli$Srich)
## [1] 2.566343
# Ajustar el modelo GLM de Poisson
gotelli.glm <- glm(Srich ~ Habitat * Latitude * Elevation, family = poisson, data = gotelli)
summary(gotelli.glm)
##
## Call:
## glm(formula = Srich ~ Habitat * Latitude * Elevation, family = poisson,
## data = gotelli)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 16.1115492 8.2821982 1.945 0.0517 .
## HabitatForest -2.1996735 10.0663651 -0.219 0.8270
## Latitude -0.3355369 0.1950119 -1.721 0.0853 .
## Elevation -0.0209554 0.0307527 -0.681 0.4956
## HabitatForest:Latitude 0.0690150 0.2369897 0.291 0.7709
## HabitatForest:Elevation 0.0137995 0.0381499 0.362 0.7176
## Latitude:Elevation 0.0004718 0.0007208 0.655 0.5127
## HabitatForest:Latitude:Elevation -0.0003348 0.0008941 -0.375 0.7080
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 102.763 on 43 degrees of freedom
## Residual deviance: 39.772 on 36 degrees of freedom
## AIC: 216.13
##
## Number of Fisher Scoring iterations: 4
# Evaluación de colinealidad mediante VIF (Factor de Inflación de la varianza): métrica utilizada para detectar la multicolinealidad en modelos de regresión. La multicolinealidad ocurre cuando dos o más variables explicativas están altamente correlacionadas, lo que puede distorsionar la estimación de los coeficientes y afectar la interpretabilidad del modelo.
vif_values <- vif(gotelli.glm)
## there are higher-order terms (interactions) in this model
## consider setting type = 'predictor'; see ?vif
print(vif_values) # Hay problemas de colinealidad si los valores son altos (>10) indican multicolinealidad severa. Se requieren < 5
## Habitat Latitude
## 7088.06105 10.55347
## Elevation Habitat:Latitude
## 7124.53754 7184.92459
## Habitat:Elevation Latitude:Elevation
## 10812.59771 7247.74145
## Habitat:Latitude:Elevation
## 10971.71840
# Centrar las variables (restar la media) puede ayudar a reducir la colinealidad en algunos casos, especialmente cuando hay interacciones entre variables.
gotelli$cLatitude <- scale(gotelli$Latitude, scale = TRUE)
gotelli$cElevation <- scale(gotelli$Elevation, scale = TRUE)
# Ajustar el modelo con variables centradas
gotelli.glm <- glm(Srich ~ Habitat * cLatitude * cElevation, family = poisson, data = gotelli,na.action = na.fail)
summary(gotelli.glm)
##
## Call:
## glm(formula = Srich ~ Habitat * cLatitude * cElevation, family = poisson,
## data = gotelli, na.action = na.fail)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.523727 0.104428 14.591 < 2e-16 ***
## HabitatForest 0.628476 0.129210 4.864 1.15e-06 ***
## cLatitude -0.241525 0.113340 -2.131 0.0331 *
## cElevation -0.105970 0.110845 -0.956 0.3391
## HabitatForest:cLatitude -0.009535 0.140664 -0.068 0.9460
## HabitatForest:cElevation -0.097558 0.137497 -0.710 0.4780
## cLatitude:cElevation 0.081362 0.124289 0.655 0.5127
## HabitatForest:cLatitude:cElevation -0.057740 0.154172 -0.375 0.7080
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 102.763 on 43 degrees of freedom
## Residual deviance: 39.772 on 36 degrees of freedom
## AIC: 216.13
##
## Number of Fisher Scoring iterations: 4
# Evaluar nuevamente la colinealidad
vif_values_new <- vif(gotelli.glm)
## there are higher-order terms (interactions) in this model
## consider setting type = 'predictor'; see ?vif
print(vif_values_new)
## Habitat cLatitude
## 1.167807 3.113812
## cElevation Habitat:cLatitude
## 3.563564 3.220434
## Habitat:cElevation cLatitude:cElevation
## 3.609016 3.477485
## Habitat:cLatitude:cElevation
## 3.644151
# La colinealidad ha disminuido considerablemente y el modelo es más estable.
# Ayuda a la interpretación en el caso de colinealidad:
# HabitatForest:Mide el efecto de estar en un hábitat forestal en comparación con un hábitat de tipo Bog (referencia), cuando Latitud y Elevación son cero. El coeficiente positvo indica que, en promedio, la riqueza de especies en un bosque es mayor que en un bog, y este efecto no es significativo.
# Latitude: Mide el efecto de la latitud en la riqueza de especies, cuando Elevación = 0 y el hábitat es Bog. Un coeficiente negativo sugiere que a mayor latitud, menor riqueza de especies, aunque el efecto es marginalmente significativo.
# HabitatForest:Elevation Representa la modificación del efecto de la elevación en la riqueza de especies cuando el hábitat es un bosque. No significativo
# Latitude:Elevation: Representa la interacción entre latitud y elevación, es decir, si el efecto de la latitud cambia según la elevación. No significativo
# HabitatForest:Latitude:Elevation Representa el efecto de la latitud y la elevación en la riqueza de especies en función del hábitat. No significativo
dispersion_ratio <- gotelli.glm$deviance / gotelli.glm$df.residual
cat("Índice de sobre-dispersión:", dispersion_ratio, "\n")
## Índice de sobre-dispersión: 1.104765
# Si el índice es significativamente mayor que 1, el modelo de Poisson no es adecuado.
summary(model.avg(dredge(gotelli.glm), fit = TRUE, subset = TRUE)) # Explora todos los modelos posibles con combinaciones de predictores a partir del modelo gotelli.glm.
## Fixed term is "(Intercept)"
## Fixed term is "(Intercept)"
## Fixed term is "(Intercept)"
##
## Call:
## model.avg(object = get.models(object = dredge(gotelli.glm), subset = TRUE))
##
## Component model call:
## glm(formula = Srich ~ <19 unique rhs>, family = poisson, data =
## gotelli, na.action = na.fail)
##
## Component models:
## df logLik AICc delta weight
## 123 4 -100.52 210.07 0.00 0.43
## 1235 5 -100.31 212.21 2.14 0.15
## 1234 5 -100.34 212.26 2.19 0.14
## 1236 5 -100.50 212.58 2.51 0.12
## 12345 6 -100.13 214.54 4.47 0.05
## 12356 6 -100.31 214.89 4.82 0.04
## 12346 6 -100.32 214.91 4.84 0.04
## 23 3 -105.32 217.24 7.17 0.01
## 123456 7 -100.13 217.38 7.31 0.01
## 236 4 -105.30 219.62 9.55 0.00
## 1234567 8 -100.06 220.24 10.17 0.00
## 13 3 -108.50 223.60 13.53 0.00
## 135 4 -108.28 225.59 15.52 0.00
## 12 3 -115.37 237.33 27.26 0.00
## 3 2 -116.72 237.72 27.65 0.00
## 124 4 -115.18 239.40 29.33 0.00
## 2 2 -120.16 244.62 34.55 0.00
## 1 2 -123.34 250.98 40.91 0.00
## (Null) 1 -131.56 265.21 55.14 0.00
##
## Term codes:
## cElevation cLatitude
## 1 2
## Habitat cElevation:cLatitude
## 3 4
## cElevation:Habitat cLatitude:Habitat
## 5 6
## cElevation:cLatitude:Habitat
## 7
##
## Model-averaged coefficients:
## (full average)
## Estimate Std. Error Adjusted SE z value
## (Intercept) 1.5292792 0.0995464 0.1026987 14.891
## cElevation -0.1647961 0.0792477 0.0813992 2.025
## cLatitude -0.2483057 0.0785800 0.0810422 3.064
## HabitatForest 0.6300487 0.1214245 0.1252695 5.030
## cElevation:HabitatForest -0.0196356 0.0704268 0.0721761 0.272
## cElevation:cLatitude 0.0107758 0.0412472 0.0423015 0.255
## cLatitude:HabitatForest -0.0048191 0.0639284 0.0659522 0.073
## cElevation:cLatitude:HabitatForest -0.0001542 0.0085061 0.0087661 0.018
## Pr(>|z|)
## (Intercept) < 2e-16 ***
## cElevation 0.04291 *
## cLatitude 0.00218 **
## HabitatForest 5e-07 ***
## cElevation:HabitatForest 0.78558
## cElevation:cLatitude 0.79893
## cLatitude:HabitatForest 0.94175
## cElevation:cLatitude:HabitatForest 0.98597
##
## (conditional average)
## Estimate Std. Error Adjusted SE z value
## (Intercept) 1.52928 0.09955 0.10270 14.891
## cElevation -0.16741 0.07709 0.07933 2.110
## cLatitude -0.24847 0.07834 0.08081 3.075
## HabitatForest 0.63005 0.12142 0.12527 5.030
## cElevation:HabitatForest -0.07942 0.12375 0.12776 0.622
## cElevation:cLatitude 0.04444 0.07430 0.07671 0.579
## cLatitude:HabitatForest -0.02218 0.13575 0.14013 0.158
## cElevation:cLatitude:HabitatForest -0.05774 0.15417 0.15953 0.362
## Pr(>|z|)
## (Intercept) < 2e-16 ***
## cElevation 0.03483 *
## cLatitude 0.00211 **
## HabitatForest 5e-07 ***
## cElevation:HabitatForest 0.53418
## cElevation:cLatitude 0.56233
## cLatitude:HabitatForest 0.87422
## cElevation:cLatitude:HabitatForest 0.71740
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# En el Full Model Average, los coeficientes tienden a ser más pequeños porque las variables que no aparecen en ciertos modelos reciben coeficientes de 0.
# En el Conditional Model Average, los coeficientes solo se calculan para los modelos donde la variable está presente, por lo que suelen ser más grandes.
Tarea. A la vista de los resultados ajustar el modelo más adecuado, interpretar los resultados, evaluar la adecuación del modelo ási como la presencia de valores outliers o influyentes.