Este documento contiene un ejemplo del modelo de ocupación de una sola especie y varias temporadas. Este modelo fue descrito por MacKenzie et. al (2003) en:
MacKenzie, D. I., J. D. Nichols, J. E. Hines, M. G. Knutson, and A. B. Franklin. 2003. Estimating site occupancy, colonization, and local extinction when a species is detected imperfectly. Ecology 84:2200–2207.
Este modelo incluye dos parámetros adicionales que representan la colonización y extinción de cada sitio. Estos dos parámetros adicionales pueden ser modelados con covariables que varían año a año.
Antes de entrar en materia con el modelo dinámico vale la pena que revisemos el modelo básico de ocupación.
Recordemos que el modelo básico de ocupación de MacKenzie et. al (2002), también es conocido como el modelo estático de ocupación. Este modelo se aplica a una sola especie, y por lo general en una sola temporada.
Donde
ψ es la ocupación y p la probabilidad de detección. Con
β como el coeficiente de la regresión para las co-variables de la
ocupación y
α el coeficiente de regresión para las co-variables de la detección.
Si desean conocer en detalle el modelo estático y saber más del poder y potencial de las simulaciones en ecología, les recomiendo seguir el Tutorial Ubicado en este enlace.
La distribución de Bernoulli (o distribución dicotómica), nombrada así por el matemático suizo Jacob Bernoulli (1655-1705).
Esta es una distribución de probabilidad discreta, que toma valor 1 para la probabilidad de éxito (p) y valor 0 para la probabilidad de fracaso (q=1-p).
La distribución de Bernoulli es un caso particular de la distribución binomial, pero con solo dos posibles resultados (éxito o fracaso) unos y ceros.
El proceso Bernoulli es el más simple proceso aleatorio que existe! Imagínemelo como algo tan sencillo como una secuencia de lanzamientos de una moneda. Donde un solo lanzamiento es un “trial” y muchos lanzamientos componen el proceso.
Usemos el siguiente código ejecutándolo varias veces y cambiando los parámetros para entender su efecto en el resultado.
library(ggplot2)
<-10 # numero de datos
ni<- 0.5 # probabilidad (~proporcion de unos)
pi# Generemos datos con esa informacion
<-data.frame(estimado=rbinom(ni, 1, pi))
daber# Grafiquemos
library(ggplot2)
ggplot(daber, aes(x=estimado)) +
geom_histogram(aes(y=..density..), # Histograma y densidad
binwidth=.1, # Ancho del bin
colour="black", fill="white") +
geom_vline(aes(xintercept=mean(estimado, na.rm=T)),
color="blue", linetype="dashed", size=1) # media en azul
Si desean conocer más detalles de la distribución Bernoulli les recomiendo la muy buena explicación de jbstatistics
Y si quieren aún más detalles visiten la clase del profesor Tsitsiklis del MIT.
\(z_{i} \sim Bernoulli (\psi_{i})\) Proceso Ecológico
\(y_{ij} \sim Bernoulli (z_{i} * p_{ij})\) Proceso de Observación
con sitio i durante el muestreo j
logit(Ψi) = α0 + α1xi1 + . . . + αUxiU.
logit(pij) = β0 + β1xi1 + . . . + βUxiU + βU+1yij1 + . . . + βU+V yijV.
\(z_{it} \sim Bernoulli (\psi_{i,t})\) Proceso Ecológico
\(y_{ijt} \sim Bernoulli (z_{i} * p_{i,tj})\) Proceso de Observación
con sitio i durante el muestreo j en el tiempo t (años)
\(z_{it} \sim Bernoulli (z_{i,t-1\phi it } + (1-z_{i,t-1}) \gamma_{i,t})\) Proceso Ecológico
\(y_{ijt} \sim Bernoulli (z_{it} * p_{ijt})\) Proceso de Observación
logit(Ψi,1) = α0 + α1xi1 + . . . + αUxiU.
Ψi,t|zi,t−1 = zi,t−1 × (1 −\(\epsilon_{i}\),t−1) + (1 − zi,t−1) × \(\gamma_{i}\),t−1, for t > 1.
Primero generamos un conjunto de datos simple y simulado con valores específicos de año específicos para los parámetros, así como especificaciones de diseño, es decir, número de sitios, años y encuestas por año.
Luego, veremos cómo ajustar un modelo de ocupación dinámico con dependencia del año en los parámetros de probabilidad de colonización, extinción y detección.
Para simular los datos, ejecutamos el siguiente código R. Los valores reales para estos parámetros para cada año se extraen aleatoriamente de una distribución uniforme con los límites especificados.
<- 250 # Number of sites
M <- 3 # num secondary sample periods
J <- 10 # num primary sample periods
T <- rep(NA, T) # Occupancy probability
psi <- z <- array(dim = c(M, T)) # Expected and realized occurrence
muZ <- array(NA, dim = c(M, J, T)) # Detection histories
y
set.seed(13973)
1] <- 0.4 # Initial occupancy probability
psi[<- c(0.3,0.4,0.5,0.5,0.1,0.3,0.5,0.5,0.6,0.2)
p <- runif(n=T-1, min=0.6, max=0.8) # Survival probability (1-epsilon)
phi <- runif(n=T-1, min=0.1, max=0.2) # Colonization probability
gamma
# Generate latent states of occurrence
# First year
1] <- rbinom(M, 1, psi[1]) # Initial occupancy state
z[,# Later years
for(i in 1:M){ # Loop over sites
for(k in 2:T){ # Loop over years
<- z[i, k-1]*phi[k-1] + (1-z[i, k-1])*gamma[k-1]
muZ[k] <- rbinom(1, 1, muZ[k])
z[i,k]
} # Generate detection/non-detection data
} for(i in 1:M){
for(k in 1:T){
<- z[i,k] * p[k]
prob for(j in 1:J){
<- rbinom(1, 1, prob)
y[i,j,k]
}
} # Compute annual population occupancy
} for (k in 2:T){
<- psi[k-1]*phi[k-1] + (1-psi[k-1])*gamma[k-1]
psi[k] }
Hemos generado una sola realización del sistema estocástico así definido. La Figura 1 ilustra la cuestión fundamental de la detección imperfecta: la proporción real de sitios ocupados difiere mucho de la proporción observada de sitios ocupados, y debido a que p varía entre años, los datos observados no pueden usarse como un índice válido del parámetro de interés ψi.
plot(1:T, colMeans(z), type = "b", xlab = "Year", ylab = "Proportion of sites occupied", col = "black", xlim=c(0.5, 10.5),
xaxp=c(1,10,9), ylim = c(0,0.6),
lwd = 2, lty = 1, frame.plot = FALSE,
las = 1, pch=16)
<- colMeans(apply(y, c(1,3), max))
psi.app lines(1:T, psi.app, type = "b", col = "blue",
lty=3, lwd = 2)
legend(1, 0.6, c("truth", "observed"),
col=c("black", "blue"), lty=c(1,3), pch=c(16,1))
Para analizar este conjunto de datos con un modelo de ocupación dinámico sin marcar, primero cargamos el paquete, y luego a continuación, formateamos los datos de detección / no detección de una matriz tridimensional (como se genera) en una matriz bidimensional con M filas. Es decir, colocamos las tablas de datos anuales (los segmentos de la matriz 3-D anterior) de lado para producir un diseño “amplio” de los datos. Posteriormente, creamos una matriz que indica el año en que se muestreo cada sitio.
library(unmarked)
<- matrix(y, M, J*T)
yy <- matrix(c('01','02','03','04','05','06','07','08','09','10'),
year nrow(yy), T, byrow=TRUE)
Para organizar los datos en el formato requerido por colext, utilizamos la función unmarkedMultFrame. Los únicos argumentos requeridos son y, los datos de detección / no detección, y numPrimary, el número de estaciones. Los tres tipos de covariables descritos anteriormente también se pueden suministrar utilizando los argumentos siteCovs, annualSiteCovs y obsCovs. En este caso, solo usamos el segundo tipo, que debe tener M filas y T columnas.
<- unmarkedMultFrame(y = yy,
simUMF yearlySiteCovs = list(year = year),
numPrimary=T)
summary(simUMF)
unmarkedFrame Object
250 sites Maximum number of observations per site: 30 Mean number of observations per site: 30 Number of primary survey periods: 10 Number of secondary survey periods: 3 Sites with at least one detection: 195
Tabulation of y observations: 0 1 6430 1070
Yearly-site-level covariates: year
01 : 250
02 : 250
03 : 250
04 : 250
05 : 250
06 : 250
(Other):1000
Estamos listos para adaptar algunos modelos de ocupación dinámica. Ajustaremos un modelo con valores constantes para todos los parámetros (modelo nulo) y otro con dependencia total del tiempo para la probabilidad de colonización, extinción y detección.
# Model with all constant parameters
<- colext(psiformula= ~1,
m0 gammaformula = ~ 1,
epsilonformula = ~ 1,
pformula = ~ 1,
data = simUMF,
method="BFGS")
summary(m0)
Call: colext(psiformula = ~1, gammaformula = ~1, epsilonformula = ~1, pformula = ~1, data = simUMF, method = “BFGS”)
Initial (logit-scale): Estimate SE z P(>|z|) -0.813 0.158 -5.16 2.46e-07
Colonization (logit-scale): Estimate SE z P(>|z|) -1.77 0.0807 -22 2.75e-107
Extinction (logit-scale): Estimate SE z P(>|z|) -0.59 0.102 -5.79 7.04e-09
Detection (logit-scale): Estimate SE z P(>|z|) -0.0837 0.0562 -1.49 0.137
AIC: 4972.597 Number of sites: 250 optim convergence code: 0 optim iterations: 27 Bootstrap iterations: 0
El tiempo de cálculo fue de solo unos segundos. Tenga en cuenta que todos los parámetros se estimaron en la escala logit. Para volver a transformar a la escala original, simplemente podemos usar la función de logit inverso, llamada plogis en R.
Alternativamente, podemos usar backTransform, que calcula los errores estándar usando el método delta. Los intervalos de confianza también se obtienen fácilmente utilizando la función con fi n. Primero nos recordamos los nombres de los parámetros, que pueden usarse como argumentos para estas funciones.
plogis(-0.813)
[1] 0.3072516
names(m0)
[1] “psi” “col” “ext” “det”
backTransform(m0, type="psi")
Backtransformed linear combination(s) of Initial estimate(s)
Estimate SE LinComb (Intercept) 0.307 0.0335 -0.813 1
Transformation: logistic
confint(backTransform(m0, type="psi")) # intervalos
0.025 0.975
0.2457313 0.3765804 Luego ajustamos el modelo de ocupación dinámica con dependencia total del año en los parámetros que describen la dinámica de ocupación y también en la detección. Este es el mismo modelo bajo el cual generamos el conjunto de datos, por lo que esperaríamos estimaciones precisas.
Por defecto en R, un factor como el año en este análisis, se parametriza en términos de una intersección y efectos que representan diferencias. Esto significaría que el parámetro para el primer año es la intersección y los efectos denotarían las diferencias entre los valores de los parámetros en todos los demás años, en relación con el valor del parámetro en el primer año, que sirve como nivel de referencia. Este tratamiento o la parametrización de los efectos es útil para evaluar las diferencias. Para una presentación simple, una parametrización de medias es más práctica. Se puede especificar agregando un -1 a la fórmula para los parámetros dependientes del tiempo
<- colext(psiformula = ~1, # First-year occupancy
m1 gammaformula = ~ year-1, # Colonization
epsilonformula = ~ year-1, # Extinction
pformula = ~ year-1, # Detection
data = simUMF)
m1
Call: colext(psiformula = ~1, gammaformula = ~year - 1, epsilonformula = ~year - 1, pformula = ~year - 1, data = simUMF)
Initial: Estimate SE z P(>|z|) -0.273 0.302 -0.906 0.365
Colonization: Estimate SE z P(>|z|) year01 -2.08 0.951 -2.19 2.86e-02 year02 -2.18 0.365 -5.96 2.52e-09 year03 -1.98 0.274 -7.23 4.88e-13 year04 -2.32 0.678 -3.42 6.37e-04 year05 -1.89 0.478 -3.95 7.78e-05 year06 -1.76 0.294 -5.97 2.44e-09 year07 -1.55 0.230 -6.73 1.75e-11 year08 -1.43 0.228 -6.29 3.19e-10 year09 -2.35 0.470 -5.00 5.64e-07
Extinction: Estimate SE z P(>|z|) year01 -1.4209 0.418 -3.401 6.72e-04 year02 -0.4808 0.239 -2.009 4.45e-02 year03 -1.2606 0.366 -3.440 5.83e-04 year04 -0.0907 0.650 -0.139 8.89e-01 year05 -0.6456 0.599 -1.078 2.81e-01 year06 -0.9586 0.378 -2.539 1.11e-02 year07 -1.2279 0.365 -3.362 7.74e-04 year08 -1.1894 0.292 -4.076 4.58e-05 year09 -0.6292 0.635 -0.991 3.22e-01
Detection: Estimate SE z P(>|z|) year01 -1.0824 0.244 -4.434 9.26e-06 year02 -0.2232 0.148 -1.508 1.32e-01 year03 0.2951 0.154 1.918 5.52e-02 year04 0.0662 0.161 0.412 6.81e-01 year05 -2.0396 0.433 -4.706 2.52e-06 year06 -0.6982 0.232 -3.005 2.66e-03 year07 0.2413 0.165 1.466 1.43e-01 year08 0.0847 0.155 0.548 5.84e-01 year09 0.6052 0.140 4.338 1.44e-05 year10 -1.1699 0.306 -3.828 1.29e-04
AIC: 4779.172
<- fitList(
models 'psi(.)gam(.)eps(.)p(.)' = m0,
'psi(.)gam(year)eps(year)p(year)' = m1
)
<- modSel(models)
ms ms
nPars AIC delta AICwt cumltvWt
psi(.)gam(year)eps(year)p(year) 29 4779.17 0.00 1e+00 1.00 psi(.)gam(.)eps(.)p(.) 4 4972.60 193.43 1e-42 1.00
Nuevamente, todas las estimaciones se muestran en la escala logit. Las estimaciones de transformación inversa cuando hay covariables, como el año, implican un paso adicional. Específicamente, necesitamos decir sin marcar los valores de nuestra covariable en los que queremos una estimación. Esto se puede hacer usando backTransform en combinación con linearComb, aunque puede ser más fácil de usar predict.
predic le permite al usuario proporcionar un marco de datos en el que cada fila representa una combinación de valores covariables de interés. A continuación, creamos los data.frames llamados nd y cada fila representa un año. Luego solicitamos estimaciones anuales de la probabilidad de extinción, colonización y detección, y las comparamos con la “verdad”, es decir, los valores con los que simulamos el conjunto de datos. Tenga en cuenta que hay parámetros de extinción y colonización T-1 en este caso, por lo que no es necesario incluir el año “10” en nd.
Predict es mas versatil y devuelve las predicciones junto con errores estándar e intervalos de confianza. Estos se pueden usar para crear graficas. La función with se usa para simplificar el proceso de solicitud de las columnas de data.frame devueltas por predic.
# Crear nuevo data frame
<- data.frame(year=c('01','02','03','04','05','06','07','08','09'))
nd # predecir
<- predict(m1, type='ext', newdata=nd)
E.ext <- predict(m1, type='col', newdata=nd)
E.col
<- data.frame(year=c('01','02','03','04','05','06','07','08','09','10'))
nd <- predict(m1, type='det', newdata=nd)
E.det
## Graficas
### Extinction
# op <- par(mfrow=c(3,1), mai=c(0.6, 0.6, 0.1, 0.1))
with(E.ext,{ # Plot for extinction probability
plot(1:9, Predicted, pch=1, xaxt='n', xlab='Year',
ylab=expression(paste('Extinction probability ( ', epsilon, ' )')),
ylim=c(0,1), col=4)
axis(1, at=1:9, labels=nd$year[1:9])
arrows(1:9, lower, 1:9, upper, code=3, angle=90, length=0.03, col=4)
points((1:9)-0.1, 1-phi, col=1, lwd = 1, pch=16)
legend(7, 1, c('Parametro verdadero', 'Estimado'), col=c(1,4), pch=c(16, 1), cex=0.8)
})
### colonization
with(E.col, { # Plot for colonization probability
plot(1:9, Predicted, pch=1, xaxt='n', xlab='Year', ylab=expression(paste('Colonization probability ( ', gamma, ' )')), ylim=c(0,1), col=4)
axis(1, at=1:9, labels=nd$year[1:9])
arrows(1:9, lower, 1:9, upper, code=3, angle=90, length=0.03, col=4)
points((1:9)-0.1, gamma, col=1, lwd = 1, pch=16)
legend(7, 1, c('Parameter', 'Estimate'), col=c(1,4), pch=c(16, 1), cex=0.8)
})
### Detection
with(E.det, { # Plot for detection probability: note 10 years
plot(1:10, Predicted, pch=1, xaxt='n', xlab='Year',
ylab=expression(paste('Detection probability ( ', p, ' )')),
ylim=c(0,1), col=4)
axis(1, at=1:10, labels=nd$year)
arrows(1:10, lower, 1:10, upper, code=3, angle=90, length=0.03, col=4)
points((1:10)-0.1, p, col=1, lwd = 1, pch=16)
legend(7.5, 1, c('Parameter','Estimate'), col=c(1,4), pch=c(16, 1), cex=0.8)
})
# par(op)
Además de estimar la varianza de una estimación, la rutina de bootstrap paramétrica se puede usar para evaluar la bondad del ajuste. Para este propósito, una estadística de ajuste, es decir, una que compara los valores observados y esperados se evalúan utilizando el modelo ajustado original y muchos otros modelos ajustados a conjuntos de datos simulados. La simulación produce una aproximación de la distribución de la estadística de ajuste, y un valor P puede calcularse como la proporción de valores simulados mayor que el valor observado. Hosmer y col. (1997) encontraron que un estadístico χ2 funcionó razonablemente bien al evaluar la falta de ajuste para los modelos de regresión logística. No conocemos estudios que evalúen formalmente el desempeño de varias estadísticas de ajuste para modelos de ocupación dinámica, por lo que este enfoque debe considerarse experimental. Las estadísticas de ajuste aplicadas a los historiales de encuentros agregados ofrecen un enfoque alternativo (MacKenzie y Bailey 2004), pero son difíciles de implementar cuando J * T es alto y hay valores faltantes o covariables continuas.
<- function(fm) {
chisq <- getData(fm)
umf <- getY(umf)
y <- fm@sitesRemoved
sr if(length(sr)>0)
<- y[-sr,,drop=FALSE]
y <- fitted(fm, na.rm=TRUE)
fv is.na(fv)] <- NA
y[sum((y-fv)^2/(fv*(1-fv)))
}
set.seed(344)
<- parboot(m0, statistic=chisq, nsim=100)
pb.gof plot(pb.gof)
La figura indica que, como se esperaba, el modelo de parámetro constante no se ajusta bien a los datos.
Este analisis es un tutorial del modelo de ocupación dinámico y usa parte de los datos y el analisis que forman parte del articulo:
Utilizamos datos de trampa de cámara que la Red de Tropical Ecology Assessment and Monitoring (TEAM) recolectados de forma regular a lo largo de un transecto del Volcán Barva en Costa Rica.
Mostramos cómo estos datos pueden usarse para calcular los indicadores temporales de las especies de mamíferos de interés en el área.
Se encontraron descensos en la ocupación de algunas especies.
Para este tutorial nos enfocaremos en Dasiprocta punctata
print(sessionInfo(), locale = FALSE)
## R version 4.0.3 (2020-10-10)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 14393)
##
## Matrix products: default
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] unmarked_1.1.1 lattice_0.20-44 ggplot2_3.3.6 markdown_1.1
##
## loaded via a namespace (and not attached):
## [1] tidyselect_1.1.2 terra_1.5-21 xfun_0.31 bslib_0.4.0
## [5] purrr_0.3.4 splines_4.0.3 colorspace_2.0-3 vctrs_0.4.1
## [9] generics_0.1.3 htmltools_0.5.3 yaml_2.3.5 utf8_1.2.2
## [13] rlang_1.0.4 nloptr_2.0.1 jquerylib_0.1.4 pillar_1.8.0
## [17] glue_1.6.2 withr_2.5.0 DBI_1.1.2 sp_1.4-5
## [21] lifecycle_1.0.1 plyr_1.8.7 stringr_1.4.0 munsell_0.5.0
## [25] gtable_0.3.0 raster_3.5-15 codetools_0.2-18 evaluate_0.15
## [29] labeling_0.4.2 knitr_1.39 fastmap_1.1.0 parallel_4.0.3
## [33] fansi_1.0.3 highr_0.9 Rcpp_1.0.8.3 scales_1.2.0
## [37] cachem_1.0.6 jsonlite_1.8.0 lme4_1.1-29 farver_2.1.0
## [41] digest_0.6.29 stringi_1.7.8 dplyr_1.0.9 grid_4.0.3
## [45] cli_3.3.0 tools_4.0.3 magrittr_2.0.3 sass_0.4.2
## [49] tibble_3.1.8 pkgconfig_2.0.3 Matrix_1.3-4 MASS_7.3-54
## [53] minqa_1.2.4 assertthat_0.2.1 rmarkdown_2.14 rstudioapi_0.13
## [57] boot_1.3-28 R6_2.5.1 nlme_3.1-152 compiler_4.0.3