Los modelos GAM (Generalized Additive Models) suelen utilizarse en análisis de CPUE o abundancia en pesquerías para modelar relaciones no lineales entre la respuesta y las variables explicativas mediante funciones suaves (splines).
Un modelo GAM es una extensión del modelo lineal generalizado:
\(Y_i\): variable respuesta (p. ej., CPUE o biomasa),
\(g(\cdot)\): función de enlace (log, logit, etc.),
\(f_j(\cdot)\): funciones suaves estimadas a partir de los datos,
\(\beta_0\): intercepto.
Cuando se una una función de enlace logarítmica, se modela la respuesta media en escala logarítmica, muy útil para datos positivos como la CPUE.
Ejemplo
Queremos modelar la CPUE (captura por unidad de esfuerzo) de una especie pelágica en función de:
Temperatura superficial del mar (SST)
Profundidad del muestreo
En este ejemplo no incluimos efectos aleatorios (como el año o el barco). Un efecto aleatorio es una variable de la que no nos interesa evaluar el efecto particular de cada valor, sino tener en cuenta su contribución a la variabilidad general observada en los datos. Por ejemplo, si el muestreo se realiza de forma tal que los registros de capturas son realizados por una muestra de barcos del total que realiza actividades pesqueras, normalmente no nos interesa estudiar las posibles diferencias entre barcos, sino descontar la variabilidad que se pueda haber introducido por utilizar barcos distintos.
Simulación y ajuste del modelo en R
Mostrar código R
# Instalación de paquetes si es necesario#install.packages(c("mgcv", "marginaleffects", "ggplot2", "dplyr"))library(mgcv)library(marginaleffects)library(ggplot2)library(dplyr)set.seed(42)# Simular datosn <-400SST <-runif(n, 18, 28) # temperatura superficial del mar (°C)prof <-runif(n, 10, 200) # profundidad (m)# Efectos de la temperatura y profundidad que vamos a simular:f1 <-function(x) sin(x/3) +0.2*(x-23)^2/25# efecto SSTf2 <-function(x) -0.004*x +0.002*(x-100)^2/100# efecto profundidad
Visualizamos las funciones suaves “verdaderas”:
Mostrar código R
x <-seq(18, 28, length.out =100)plot(x, f1(x), type='l', main="Efecto no lineal de SST", ylab="f1(SST)", xlab="SST (°C)")
Mostrar código R
x <-seq(10, 200, length.out =100)plot(x, f2(x), type='l', main="Efecto no lineal de profundidad", ylab="f2(prof)", xlab="Profundidad (m)")
Simulamos los datos:
Mostrar código R
# Modelo verdadero (en log escala)eta <-2+f1(SST) +f2(prof)CPUE <-rlnorm(n, eta, 0.3) # respuesta positiva y asimétricadatos <-data.frame(CPUE, SST, prof)
Creamos predicciones suavizadas manteniendo las demás variables constantes en su media. De esta forma podemos interpretar el modelo ajustado:
Mostrar código R
# Predicciones para SST (manteniendo prof en su media)ef_sst <-predictions( modelo_gam,variables =list(SST =seq(18, 28, length.out =100)),newdata =datagrid(prof =mean(datos$prof)))# Predicciones para profundidad (manteniendo SST en su media)ef_prof <-predictions( modelo_gam,variables =list(prof =seq(10, 200, length.out =100)),newdata =datagrid(SST =mean(datos$SST)))
(a) Efecto de la temperatura superficial (SST)
Mostrar código R
ggplot(ef_sst, aes(x = SST, y = estimate)) +geom_point(data=datos, aes(x = SST, y =log(CPUE)),color ="gray60", alpha =0.85) +geom_line(color ="darkblue", linewidth =1.2) +geom_ribbon(aes(ymin = conf.low, ymax = conf.high),fill ="skyblue", alpha =0.3) +labs(title ="Efecto de la temperatura superficial (SST) sobre log(CPUE)",y ="log(CPUE) predicho",x ="Temperatura superficial (°C)" ) +theme_minimal(base_size =13)
(b) Efecto de la profundidad
Mostrar código R
ggplot(ef_prof, aes(x = prof, y = estimate)) +geom_point(data=datos, aes(x = prof, y =log(CPUE)),color ="gray60", alpha =0.85) +geom_line(color ="darkgreen", linewidth =1.2) +geom_ribbon(aes(ymin = conf.low, ymax = conf.high),fill ="palegreen3", alpha =0.3) +labs(title ="Efecto de la profundidad sobre log(CPUE)",y ="log(CPUE) predicho",x ="Profundidad (m)" ) +theme_minimal(base_size =13)
Pendientes locales con slopes()
Podemos explorar cómo cambia la tasa de variación local de la CPUE respecto a cada covariable.
Mostrar código R
# Pendiente local de SSTslope_sst <-slopes( modelo_gam,variables ="SST",newdata =datagrid(SST =seq(18, 28, length.out =100),prof =mean(datos$prof)))ggplot(slope_sst, aes(x = SST, y = estimate)) +geom_line(color ="firebrick", linewidth =1.2) +geom_ribbon(aes(ymin = conf.low, ymax = conf.high),fill ="salmon", alpha =0.3) +labs(title ="Pendiente local: efecto de la SST sobre log(CPUE)",y ="d(log(CPUE)) / d(SST)",x ="Temperatura superficial (°C)" ) +theme_minimal(base_size =13)
Interpretación en contexto pesquero
El modelo muestra cómo la CPUE varía suavemente con la temperatura y la profundidad, sin imponer una forma funcional específica (como lineal o cuadrática).
El pico en la curva de SST puede interpretarse como la temperatura óptima donde la especie es más abundante.
El descenso con la profundidad puede reflejar una preferencia por aguas someras.
El análisis de pendientes locales ayuda a detectar zonas de cambio rápido (por ejemplo, umbrales térmicos).
Estos resultados pueden informar estrategias de manejo pesquero, como definir áreas protegidas o temporadas de pesca basadas en condiciones ambientales favorables para la especie objetivo.
Diferencias regionales
En la práctica, la relación entre la CPUE y variables como la temperatura superficial (SST) o la profundidad puede variar según la región o zona de pesca, debido a diferencias ambientales o ecológicas.
Vamos a ver cómo tratar ese caso con GAM y marginaleffects, paso a paso.
La sintaxis de mgcv usa el argumento by= dentro de s() para permitir efectos distintos por grupo.
Mostrar código R
# Ajuste del GAM con efectos por regiónmodelo_reg <-gam(log(CPUE) ~ region +s(SST, by = region, k =10) +s(prof, by = region, k =10),data = datos)summary(modelo_reg)
Todos los p-valores son significativos, por lo que concluimos que los datos contienen evidencia suficiente para asegurar que los efectos de SST o profundidad cambian según la región.
Otra opción más formal es usar un test de razón de verosimilitud entre:
un modelo que asume mismo efecto para todas las regiones, y
otro con efectos diferentes (como el que acabamos de ajustar).
Mostrar código R
modelo_noreg <-gam(log(CPUE) ~ region +s(SST, k=10) +s(prof, k=10),data = datos)# Test de razón de verosimilitudanova(modelo_noreg, modelo_reg, test ="Chisq")
Analysis of Deviance Table
Model 1: log(CPUE) ~ region + s(SST, k = 10) + s(prof, k = 10)
Model 2: log(CPUE) ~ region + s(SST, by = region, k = 10) + s(prof, by = region,
k = 10)
Resid. Df Resid. Dev Df Deviance Pr(>Chi)
1 591.34 92.702
2 572.31 48.811 19.026 43.891 < 2.2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
El modelo con efectos por región (modelo_reg) mejora significativamente (p < 0.05), por lo que hay evidencia de diferencias entre regiones en el efecto de SST o profundidad.
Para visualizar estas diferencias, usamos marginaleffects.
Visualización con marginaleffects
Podemos visualizar las curvas predichas por región:
Efecto de la temperatura:
Mostrar código R
# Predicciones de log(CPUE) frente a SST por regiónef_sst <-predictions( modelo_reg,variables =list(SST =seq(18, 28, length.out =100)),newdata =datagrid(prof =mean(datos$prof), region =unique(datos$region)))ggplot(ef_sst, aes(x = SST, y = estimate, color = region, fill = region)) +geom_point(data=datos, aes(x = SST, y =log(CPUE), color = region),alpha =0.6, size=1) +geom_line(linewidth =1.2) +geom_ribbon(aes(ymin = conf.low, ymax = conf.high), alpha =0.2, color =NA) +labs(title ="Efecto de la temperatura superficial (SST) sobre log(CPUE) por región",y ="log(CPUE) predicho",x ="Temperatura superficial (°C)" ) +theme_minimal(base_size =13)
Efecto de la profundidad:
Mostrar código R
ef_prof <-predictions( modelo_reg,variables =list(prof =seq(10, 200, length.out =100)),newdata =datagrid(SST =mean(datos$SST), region =unique(datos$region)))ggplot(ef_prof, aes(x = prof, y = estimate, color = region, fill = region)) +geom_point(data=datos, aes(x = prof, y =log(CPUE), color = region),alpha =0.6, size=1) +geom_line(linewidth =1.2) +geom_ribbon(aes(ymin = conf.low, ymax = conf.high), alpha =0.2, color =NA) +labs(title ="Efecto de la profundidad sobre log(CPUE) por región",y ="log(CPUE) predicho",x ="Profundidad (m)" ) +theme_minimal(base_size =13)
####Comparación visual entre regiones
Para comparar directamente diferencias de predicciones entre regiones (por ejemplo, región B − región A) para cada valor de SST podemos usar el paquete gratia:
Mostrar código R
library(gratia)diff_sst <-difference_smooths( modelo_reg,select ="s(SST)",pairwise =TRUE)# Visualizardraw(diff_sst) +ggtitle("Diferencias entre suavizados de SST por región (con IC 95%)") +theme_minimal(base_size =13)
Mostrar código R
library(gratia)diff_prof <-difference_smooths( modelo_reg,select ="s(prof)",pairwise =TRUE)# Visualizardraw(diff_prof) +ggtitle("Diferencias entre suavizados de Prof por región (con IC 95%)") +theme_minimal(base_size =13)
Modelo con efectos aleatorios.
Partimos del modelo anterior, añadiendo ahora un factor barco como efecto aleatorio:
modelo_gamm2 <-gamm(log(CPUE) ~ region +s(SST, by = region, k =10) +s(prof, by = region, k =10),random =list(barco =~1 ),data = datos)summary(modelo_gamm2$gam)