Hi everyone,
I’m trying to apply an N‐mixture model using Nimble with camera trap data. We started with the standard N‐mixture model that assumes an ecological process following a Poisson distribution and an observational process modeled as binomial. However, we observed overdispersion in this model. Because of that, we plan to incorporate a random effect into the ecological process (one for each camera location).
The first model produced results identical to those from a frequentist approach, so we are confident in its performance. The problem arises when we add the random effect in the second model. In that case, we encounter two issues: first, some parameters fail to initialize (which appears to be related to NAs in the observational count data), and second, the posterior distributions show poor convergence (as evidenced by the traceplots, Rhat, and effective sample size values). Even when we set initial values for the missing counts, the convergence issues persist.
Interestingly, when we use the stan_pcount function—with a Poisson ecological process and a random effect for each camera—the model runs smoothly (i.e. no initialization issues and good convergence for all parameters). Since stan_pcount has some limitations that prevent us from implementing all desired changes, we want to continue developing the model in Nimble.
Below, I share the code for both models as applied:
• Model 1 (N‐mixture model with Poisson–Binomial)
• Model 2 (N‐mixture model with Poisson (and random effect per camera) and Binomial)
###############
#### Create the model 1 -----------------------------------------------------------
library(nimble)
#Save a list with all the constants that we will employ and other list with the data.
constants <- list(nsurveys = ncol(counts.dry), # number of surveys
nsites = nrow(counts.dry), # number of sites
ngr = 56) #number of categories of cameras (cameras ID)
data <- list(counts = counts.dry, # Our animals counts (detections)
d_rivers = cov.site$DIST.RIVER, #Covriate ecological process
effort = cov.obs_dry #Covariate observational process
)
# Define the model
Poisson01NMIX <- nimbleCode({
# Likelihood
# ECOLOGICAL PROCESS
# For each sampling site...
for(i in 1:nsites) {
N[i] ~ dpois(lambda[i])
log(lambda[i]) <- b0 + b_d.river * d_rivers[i]
}
# OBSERVATIONAL PROCESS
# For each sampling site...
for(i in 1:nsites) {
# and sampling survey...
for(j in 1:nsurveys) {
counts[i, j] ~ dbin(p[i,j], N[i])
logit(p[i,j]) <- a0 + a_effort * effort[i,j]
}
}
# PRIORS:
b_d.river ~ dnorm(mean = 0, sd = 1)
b0 ~ dnorm(mean = 0, sd = 1)
a0 ~ dnorm(mean = 0, sd = 1)
a_effort ~ dnorm(mean = 0, sd = 1)
})
# list of initial values (one for each MCMC chain)
Nst <- apply(counts.dry, 1, max, na.rm = TRUE)+1 #Important to give good initial values for latent N (not start with a lower values than present yet in any count)
inits <-function() {
list(N = Nst,
b0 = runif(1, -1, 1),
b_d.river = runif(1,-1, 1),
a0 = runif(1, -1, 1),
a_effort = runif(1, -1, 1))
}
#### Create the model 2 -----------------------------------------------------------
library(nimble)
#Save a list with all the constants that we will employ and other list with the data.
constants <- list(nsurveys = ncol(counts.dry), # number of surveys
nsites = nrow(counts.dry), # number of sites
ngr = 56, #number of categories of cameras (cameras ID)
camID = cov.site$Camera.ID) # number camera ID, employed as random factor (one category per camera)
data <- list(counts = counts.dry, # Our animals counts (detections)
d_rivers = cov.site$DIST.RIVER, #Covariate ecological process
effort = cov.obs_dry #Covariate observational process
)
# Define the model
Poisson02NMIX <- nimbleCode({
# Likelihood
# ECOLOGICAL PROCESS
# For each sampling site...
for(i in 1:nsites) {
N[i] ~ dpois(lambda[i])
log(lambda[i]) <- b0[camID[i]] + b_d.river * d_rivers[i]
}
# OBSERVATIONAL PROCESS
# For each sampling site...
for(i in 1:nsites) {
# and sampling survey...
for(j in 1:nsurveys) {
counts[i, j] ~ dbin(p[i,j], N[i])
logit(p[i,j]) <- a0 + a_effort * effort[i,j]
}
}
# PRIORS:
b_d.river ~ dnorm(mean = 0, sd = 1)
for(k in 1:ngr) {
b0[k] ~ dnorm(mean = mu.b0, sd = sd.b0)
}
mu.b0 ~ dnorm(mean = 0, sd = 1)
sd.b0 ~ dunif(0,10)
a0 ~ dnorm(mean = 0, sd = 1)
a_effort ~ dnorm(mean = 0, sd = 1)
})
# list of initial values (one for each MCMC chain)
Nst <- apply(counts.dry, 1, max, na.rm = TRUE)+1 #Important to give good initial values for latent N (not start with a lower values than present yet in any count)
inits <-function() {
list(N = Nst,
b0 = runif(56, -1, 1),
mu.b0 = runif(1, -1, 1),
sd.b0 = runif(1, 0, 10),
b_d.river = runif(1,-1, 1),
a0 = runif(1, -1, 1),
a_effort = runif(1, -1, 1))
}
###############
For the secon model, when we run
"####
modelCAM <- nimbleModel(code = PoissonNMIX,
+ data = data,
+ constants = constants,
+ inits = inits(),
+ calculate = FALSE)
####"
we obtain the following note: "[Note] This model is not fully initialized. This is not an error. To see which variables are not initialized, use model$initializeInfo(). For more information on model initialization, see help(modelInitialization)."
Checking with "modelCAM$initializeInfo()"
yields:
"[Note] Missing values (NAs) or non-finite values were found in model variables: lambda, p."
Based on that note, we attempted to set initial values for the NA counts by replacing NA with the maximum value observed at each site:
"####
# list of initial values (one for each MCMC chain)
Nst <- apply(counts.dry, 1, max, na.rm = TRUE)+1 #Important to give good initial values for latent N (not start with a lower values than present yet in any count)
# Change NA in counts.dry with the maximum value detected on the site
counts_ini <- t(apply(counts.dry, 1, function(r) {
# r is the camera corresponding vector with al the counts
r[
is.na(r)] <- max(r, na.rm = TRUE)
return(r)
}))
inits <- function() {
list(N = Nst,
counts = counts_ini,
mu.b0 = runif(1, -1, 1),
b0 = runif(56, -1, 1),
sd.b0 = runif(1, 0, 100),
b_d.river = runif(1, -1, 1),
a0 = runif(1, -1, 1),
a_effort = runif(1, -1, 1))
}
####
"
However, we still encounter problems with initialization and convergence once the random effect is added, and we are unsure how to resolve these issues.
Thank you,
David Ferrer