unmarked
Facilited by proliferation of passive detector data (e.g. cameras)
Activity pattern analysis (e.g. Ridout and Linkie 2009, overlap R package)

Doesn't necessarily test if species are typically found in the same locations
Multispecies occupancy models
Don't necessary test if species are active at the same time
$f_1$: Term associated with occupancy of species 1 by itself
$f_2$: Term associated with occupancy of species 2 by itself
$f_{12}$: Interaction term between the two species
If $f_{12}$ is positive, presence of one species increases occupancy probability of the other
If $f_{12}$ is negative, presence of one species decreases occupancy probability of the other
Each element of $\mathbf{\psi}$ is a function of the natural parameters $f$.
$$ \begin{align} \psi_{11} & \propto exp(f_{1} + f_{2} + f_{12}) \\ \psi_{10} & \propto exp(f_{1}) \\ \psi_{01} & \propto exp(f_{2}) \\ \psi_{00} & \propto exp(0) = 1 \end{align} $$Making sure $\mathbf{\psi}$ sums to 1:
$$ \psi_{11} = \frac{exp(f_{1} + f_{2} + f_{12})}{1 + exp(f_{1}) + exp(f_{2}) + exp(f_{1} + f_{2} + f_{12})} $$$$ \psi_{10} = \frac{exp(f_{1})}{1 + exp(f_{1}) + exp(f_{2}) + exp(f_{1} + f_{2} + f_{12})} $$$$ \psi_{01} = \frac{exp(f_{2})}{1 + exp(f_{1}) + exp(f_{2}) + exp(f_{1} + f_{2} + f_{12})} $$$$ \psi_{00} = \frac{1}{1 + exp(f_{1}) + exp(f_{2}) + exp(f_{1} + f_{2} + f_{12})} $$For example, detection/nondetection of species 1 at site $i$ and occasion $j$ can be modeled as
$ y_{1,ij} \sim \mathrm{Bernoulli}(p_{1} * z_{1,i}) $
where $z_{1,i}$ is the element 1 of $Z_i$ (i.e., latent presence/absence of species 1 at site $i$) and $p_{1}$ is the species 1 detection probability

Look at the data
library(unmarked)
data(MesoCarnivores)
lapply(MesoCarnivores, head) # look at raw data
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 1 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| Dist_5km | HDens_5km | Latitude | Longitude | People_site | Trail | |
|---|---|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | <dbl> | <int> | |
| 1 | 0.04 | 9.345258 | 0.3899441 | -0.7723958 | 0.857 | 1 |
| 2 | 0.03 | 9.499197 | 0.3899250 | -0.7723920 | 0.002 | 0 |
| 3 | 0.03 | 9.647173 | 0.3899111 | -0.7723954 | 0.387 | 1 |
| 4 | 0.03 | 9.598066 | 0.3899166 | -0.7723972 | 0.003 | 0 |
| 5 | 0.03 | 9.607825 | 0.3899179 | -0.7724004 | 0.000 | 0 |
| 6 | 0.03 | 9.748791 | 0.3899058 | -0.7724046 | 0.443 | 1 |
For now, we will fit a 2-species model (coyote and red fox) with no covariates
jags_data <- list(
y_coy = MesoCarnivores$coyote, # coyote matrix
y_fox = MesoCarnivores$redfox, # red fox matrix
N = nrow(MesoCarnivores$coyote), # num sites
J = ncol(MesoCarnivores$coyote) # num samples
)
In BUGS code:
Z[i] ~ dcat(Psi[1:4]) # Takes on values 1-4
In BUGS code:
Psi[1] <- exp(f1 + f2 + f12) # {11}
Psi[2] <- exp(f1) # {10}
Psi[3] <- exp(f2) # {01}
Psi[4] <- 1 # {00}
# Priors on natural parameters
f1 ~ dnorm(0, 0.01)
f2 ~ dnorm(0, 0.01)
f12 ~ dnorm(0, 0.01)
JAGS dcat automatically divides Psi by sum(Psi) to make sure Psi sums to 1
In BUGS code:
for (i in 1:N){
z1[i] <- (Z[i] == 1) + (Z[i] == 2) # is coyote present?
z2[i] <- (Z[i] == 1) + (Z[i] == 3) # is red fox present?
for (j in 1:J){
y_coy[i, j] ~ dbern(z1[i] * p1)
y_fox[i, j] ~ dbern(z2[i] * p2)
}
}
p1 ~ dunif(0, 1)
p2 ~ dunif(0, 1)
cat(file="jags_multispecies_null.txt", "
model {
# State process
for (i in 1:N){
# latent occupancy state
Z[i] ~ dcat(Psi[1:4]) # Takes on values 1-4
}
# Occupancy probabilities / multinomial logit
Psi[1] <- exp(f1 + f2 + f12) # {11}
Psi[2] <- exp(f1) # {10}
Psi[3] <- exp(f2) # {01}
Psi[4] <- 1 # {00}
# Priors on natural parameters
f1 ~ dnorm(0, 0.01)
f2 ~ dnorm(0, 0.01)
f12 ~ dnorm(0, 0.01)
# Detection process
for (i in 1:N){
z1[i] <- (Z[i] == 1) + (Z[i] == 2) # is coyote present?
z2[i] <- (Z[i] == 1) + (Z[i] == 3) # is red fox present?
for (j in 1:J){
y_coy[i, j] ~ dbern(z1[i] * p1)
y_fox[i, j] ~ dbern(z2[i] * p2)
}
}
# Priors on p
p1 ~ dunif(0, 1)
p2 ~ dunif(0, 1)
}
")
library(jagsUI)
inits <- function(){
list(Z = rep(1, jags_data$N))
}
params <- c("f1", "f2", "f12", "p1", "p2")
fit <- jags(data = jags_data, inits = inits, parameters.to.save = params,
model.file = 'jags_multispecies_null.txt', n.chains = 3, n.iter = 1000,
n.burnin = 500, n.thin = 1, parallel = TRUE)
Processing function input....... Done. Beginning parallel processing using 3 cores. Console output will be suppressed. Parallel processing completed. Calculating statistics....... Done.
fit
traceplot(fit)
JAGS output for model 'jags_multispecies_null.txt', generated by jagsUI.
Estimates based on 3 chains of 1000 iterations,
adaptation = 100 iterations (sufficient),
burn-in = 500 iterations and thin rate = 1,
yielding 1500 total samples from the joint posterior.
MCMC ran in parallel for 0.399 minutes at time 2023-07-18 11:52:54.207778.
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
f1 -0.791 0.080 -0.952 -0.790 -0.629 FALSE 1 1.044 51
f2 -2.321 0.149 -2.601 -2.318 -2.035 FALSE 1 1.006 342
f12 1.094 0.212 0.658 1.090 1.495 FALSE 1 1.010 194
p1 0.417 0.018 0.381 0.417 0.452 FALSE 1 1.031 76
p2 0.435 0.029 0.380 0.434 0.492 FALSE 1 1.008 813
deviance 2851.580 63.369 2736.060 2848.895 2983.615 FALSE 1 1.026 97
Successful convergence based on Rhat values (all < 1.1).
Rhat is the potential scale reduction factor (at convergence, Rhat=1).
For each parameter, n.eff is a crude measure of effective sample size.
overlap0 checks if 0 falls in the parameter's 95% credible interval.
f is the proportion of the posterior with the same sign as the mean;
i.e., our confidence that the parameter is positive or negative.
DIC info: (pD = var(deviance)/2)
pD = 1968.7 and DIC = 4820.273
DIC is an estimate of expected predictive error (lower is better).
We now need to index Psi and the natural parameters f by site, i.
for (i in 1:N){
Z[i] ~ dcat(Psi[i, 1:4]) # Takes on values 1-4
Psi[i,1] <- exp(f1[i] + f2[i] + f12[i]) # {11}
Psi[i,2] <- exp(f1[i]) # {10}
Psi[i,3] <- exp(f2[i]) # {01}
Psi[i,4] <- 1 # {00}
# ... truncated
}
In BUGS code:
for (i in 1:N){
# ... truncated
f1[i] <- alpha_f1 + beta_f1 * hdens[i]
f2[i] <- alpha_f2 + beta_f2 * hdens[i]
f12[i] <- alpha_f12
}
cat(file="jags_multispecies_covs.txt", "
model {
# State process
for (i in 1:N){
# latent occupancy state
Z[i] ~ dcat(Psi[i, 1:4]) # Takes on values 1-4
# Occupancy probabilities / multinomial logit
Psi[i,1] <- exp(f1[i] + f2[i] + f12[i]) # {11}
Psi[i,2] <- exp(f1[i]) # {10}
Psi[i,3] <- exp(f2[i]) # {01}
Psi[i,4] <- 1 # {00}
# Natural parameters
f1[i] <- alpha_f1 + beta_f1 * hdens[i]
f2[i] <- alpha_f2 + beta_f2 * hdens[i]
f12[i] <- alpha_f12
}
# Priors on state parameters
alpha_f1 ~ dunif(-5, 5)
beta_f1 ~ dnorm(0, 0.01)
alpha_f2 ~ dunif(-5, 5)
beta_f2 ~ dnorm(0, 0.01)
alpha_f12 ~ dunif(-5, 5)
# Detection process
for (i in 1:N){
z1[i] <- (Z[i] == 1) + (Z[i] == 2) # is coyote present?
z2[i] <- (Z[i] == 1) + (Z[i] == 3) # is red fox present?
for (j in 1:J){
y_coy[i, j] ~ dbern(z1[i] * p1)
y_fox[i, j] ~ dbern(z2[i] * p2)
}
}
# Priors on p
p1 ~ dunif(0,1)
p2 ~ dunif(0,1)
}
")
jags_data <- list(
y_coy = MesoCarnivores$coyote, # coyote matrix
y_fox = MesoCarnivores$redfox, # red fox matrix
hdens = as.numeric(scale(MesoCarnivores$sitecovs$HDens_5km)), # covariate
N = nrow(MesoCarnivores$coyote), # num sites
J = ncol(MesoCarnivores$coyote) # num samples
)
inits <- function(){
list(Z = rep(1, jags_data$N))
}
params <- c("alpha_f1", "beta_f1", "alpha_f2", "beta_f2",
"alpha_f12", "p1", "p2")
fit <- jags(data = jags_data, inits = inits, parameters.to.save = params,
model.file = 'jags_multispecies_covs.txt', n.chains = 3, n.iter = 100,
n.burnin = 50, n.thin = 1, parallel = TRUE)
fit
Processing function input....... Done. Beginning parallel processing using 3 cores. Console output will be suppressed. Parallel processing completed. Calculating statistics....... Done.
JAGS output for model 'jags_multispecies_covs.txt', generated by jagsUI.
Estimates based on 3 chains of 100 iterations,
adaptation = 100 iterations (sufficient),
burn-in = 50 iterations and thin rate = 1,
yielding 150 total samples from the joint posterior.
MCMC ran in parallel for 0.285 minutes at time 2023-07-18 11:57:12.867507.
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
alpha_f1 -0.776 0.084 -0.942 -0.776 -0.629 FALSE 1.00 1.171 15
beta_f1 0.006 0.063 -0.121 0.005 0.114 TRUE 0.54 1.025 150
alpha_f2 -2.341 0.171 -2.762 -2.333 -2.066 FALSE 1.00 1.095 45
beta_f2 0.549 0.108 0.371 0.524 0.778 FALSE 1.00 1.090 30
alpha_f12 0.989 0.215 0.601 0.977 1.486 FALSE 1.00 1.121 114
p1 0.421 0.021 0.382 0.419 0.460 FALSE 1.00 1.142 19
p2 0.433 0.037 0.362 0.430 0.497 FALSE 1.00 1.026 80
deviance 2849.594 69.478 2715.173 2850.116 2981.134 FALSE 1.00 1.120 27
**WARNING** Rhat values indicate convergence failure.
Rhat is the potential scale reduction factor (at convergence, Rhat=1).
For each parameter, n.eff is a crude measure of effective sample size.
overlap0 checks if 0 falls in the parameter's 95% credible interval.
f is the proportion of the posterior with the same sign as the mean;
i.e., our confidence that the parameter is positive or negative.
DIC info: (pD = var(deviance)/2)
pD = 2257 and DIC = 5106.6
DIC is an estimate of expected predictive error (lower is better).
JAGS output for model 'jags_multispecies_covs.txt', generated by jagsUI.
Estimates based on 3 chains of 10000 iterations,
adaptation = 100 iterations (sufficient),
burn-in = 8000 iterations and thin rate = 5,
yielding 1200 total samples from the joint posterior.
MCMC ran in parallel for 12.987 minutes at time 2023-07-07 08:57:50.122961.
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
alpha_f1 -0.783 0.083 -0.947 -0.782 -0.617 FALSE 1.000 1.002 1200
beta_f1 -0.001 0.074 -0.153 -0.003 0.150 TRUE 0.508 1.003 621
alpha_f2 -2.399 0.158 -2.720 -2.395 -2.105 FALSE 1.000 1.002 711
beta_f2 0.529 0.092 0.365 0.523 0.728 FALSE 1.000 1.000 1200
alpha_f12 1.075 0.230 0.643 1.066 1.534 FALSE 1.000 1.001 996
deviance 2856.690 62.011 2738.322 2855.443 2975.947 FALSE 1.000 1.001 1200
Successful convergence based on Rhat values (all < 1.1).
Rhat is the potential scale reduction factor (at convergence, Rhat=1).
For each parameter, n.eff is a crude measure of effective sample size.
overlap0 checks if 0 falls in the parameter's 95% credible interval.
f is the proportion of the posterior with the same sign as the mean;
i.e., our confidence that the parameter is positive or negative.
DIC info: (pD = var(deviance)/2)
pD = 1924.6 and DIC = 4781.304
DIC is an estimate of expected predictive error (lower is better).
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
beta_f1 -0.001 0.074 -0.153 -0.003 0.150 TRUE 0.508 1.003 621
No evidence housing density affects coyote occupancy (remember, species 1 = coyote)
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
beta_f2 0.529 0.092 0.365 0.523 0.728 FALSE 1.000 1.000 1200
Positive relationship between housing density and red fox occupancy
mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
alpha_f12 1.075 0.230 0.643 1.066 1.534 FALSE 1.000 1.001 996
Positive interaction between coyote and red fox occupancy; that is, if coyote is present occupancy of red fox is higher (and vice-versa)
What if we force $f_{12} = 0$?
$$ \begin{align} \psi_{11} & \propto exp(f_{1} + f_{2}) \\ \psi_{10} & \propto exp(f_{1}) \\ \psi_{01} & \propto exp(f_{2}) \\ \psi_{00} & \propto 1 \end{align} $$This is equivalent to fitting two separate occupancy models. I'll show this later.
unmarked makes things easier to scalelibrary(unmarked)
data(MesoCarnivores)
lapply(MesoCarnivores, head) # look at raw data
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 1 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| Dist_5km | HDens_5km | Latitude | Longitude | People_site | Trail | |
|---|---|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | <dbl> | <int> | |
| 1 | 0.04 | 9.345258 | 0.3899441 | -0.7723958 | 0.857 | 1 |
| 2 | 0.03 | 9.499197 | 0.3899250 | -0.7723920 | 0.002 | 0 |
| 3 | 0.03 | 9.647173 | 0.3899111 | -0.7723954 | 0.387 | 1 |
| 4 | 0.03 | 9.598066 | 0.3899166 | -0.7723972 | 0.003 | 0 |
| 5 | 0.03 | 9.607825 | 0.3899179 | -0.7724004 | 0.000 | 0 |
| 6 | 0.03 | 9.748791 | 0.3899058 | -0.7724046 | 0.443 | 1 |
We will create an unmarkedFrameOccuMulti.
The main difference from unmarkedFrameOccu is that you have >1 matrix of observations: we now have one y per species.

Get some more detail by looking at the help file for unmarkedFrameOccuMulti:
?unmarkedFrameOccuMulti
The first three elements of MesoCarnivores are the y-matrices. To keep things simple, we'll just use coyote and red fox.
Notice that the resulting list is named: this is important!
ylist <- MesoCarnivores[c(2:3)]
lapply(ylist, head) # look at first few rows
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| X1 | X2 | X3 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 1 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 1 | 1 | 1 |
The last element of the MesoCarnivores list is a data frame of site covariates:
site_covs <- MesoCarnivores$sitecovs
head(site_covs)
| Dist_5km | HDens_5km | Latitude | Longitude | People_site | Trail | |
|---|---|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | <dbl> | <int> | |
| 1 | 0.04 | 9.345258 | 0.3899441 | -0.7723958 | 0.857 | 1 |
| 2 | 0.03 | 9.499197 | 0.3899250 | -0.7723920 | 0.002 | 0 |
| 3 | 0.03 | 9.647173 | 0.3899111 | -0.7723954 | 0.387 | 1 |
| 4 | 0.03 | 9.598066 | 0.3899166 | -0.7723972 | 0.003 | 0 |
| 5 | 0.03 | 9.607825 | 0.3899179 | -0.7724004 | 0.000 | 0 |
| 6 | 0.03 | 9.748791 | 0.3899058 | -0.7724046 | 0.443 | 1 |
umf <- unmarkedFrameOccuMulti(y=ylist, siteCovs=site_covs)
head(umf)
Data frame representation of unmarkedFrame object. Only showing observation matrix for species 1. y.1 y.2 y.3 Dist_5km HDens_5km Latitude Longitude People_site Trail 1 0 0 0 0.04 9.345258 0.3899441 -0.7723958 0.857 1 2 0 0 0 0.03 9.499197 0.3899250 -0.7723920 0.002 0 3 0 0 0 0.03 9.647173 0.3899111 -0.7723954 0.387 1 4 0 0 0 0.03 9.598066 0.3899166 -0.7723972 0.003 0 5 0 0 0 0.03 9.607825 0.3899179 -0.7724004 0.000 0 6 0 0 0 0.03 9.748791 0.3899058 -0.7724046 0.443 1 7 0 0 0 0.03 9.715359 0.3899097 -0.7724065 0.000 0 8 0 0 0 0.03 9.685091 0.3899135 -0.7724103 0.000 0 9 0 0 0 0.04 9.051558 0.3899955 -0.7724561 0.002 0 10 0 0 0 0.04 9.031836 0.3899971 -0.7724504 0.000 0
summary(umf)
unmarkedFrame Object
1437 sites
2 species: coyote redfox
Maximum number of observations per site: 3
Mean number of observations per site:
coyote: 3 redfox: 3
Sites with at least one detection:
coyote: 401 redfox: 161
Tabulation of y observations:
coyote:
0 1
3685 626
redfox:
0 1
4054 257
Site-level covariates:
Dist_5km HDens_5km Latitude Longitude
Min. :0.00000 Min. : 0.0000 Min. :0.3454 Min. :-0.8539
1st Qu.:0.00000 1st Qu.: 0.2073 1st Qu.:0.3567 1st Qu.:-0.8009
Median :0.00000 Median : 0.7310 Median :0.3753 Median :-0.7940
Mean :0.01419 Mean : 8.4532 Mean :0.3726 Mean :-0.7969
3rd Qu.:0.02000 3rd Qu.: 3.0219 3rd Qu.:0.3863 3rd Qu.:-0.7801
Max. :0.13000 Max. :186.6694 Max. :0.3967 Max. :-0.7690
People_site Trail
Min. :0.00000 Min. :0.0000
1st Qu.:0.00000 1st Qu.:0.0000
Median :0.00000 Median :0.0000
Mean :0.05807 Mean :0.3229
3rd Qu.:0.00900 3rd Qu.:1.0000
Max. :2.44100 Max. :1.0000
plot(umf)

While occu had a single formula for occupancy, occuMulti requires one formula per natural parameter $f$, organized into a stateformulas vector.
Similarly, each species has its own detection probability formula organized into a detformulas vector.
stateformulas¶It can be hard to keep track of how many natural parameters there are and what each one represents.
It can be helpful to look at the $f$-design matrix, which is generated by unmarkedFrameOccuMulti.
umf@fDesign
| f1[coyote] | f2[redfox] | f3[coyote:redfox] | |
|---|---|---|---|
| psi[11] | 1 | 1 | 1 |
| psi[10] | 1 | 0 | 0 |
| psi[01] | 0 | 1 | 0 |
| psi[00] | 0 | 0 | 0 |
The number and order of the formulas in the stateformulas vector should match the column names of this matrix.
Therefore we'll need 3 formulas total.
For this model we'll set the 1st-order $f$ parameters to be a function of (standardized) housing density. The interaction term will be intercept-only.
Our stateformulas should look like this:
stateformulas <- c("~scale(HDens_5km)","~scale(HDens_5km)","~1")
stateformulas
Notice that the formulas are strings (each wrapped in ""). This is required and is just working around some R limitations.
detformulas¶This one is easier, there is just one formula per species, so there should be 2 total.
detformulas <- c("~1","~1")
detformulas
occuMulti¶?occuMulti
We now have all the pieces we need (unmarkedFrameOccuMulti, stateformulas, detformulas) needed to run a model.
mod_hdens <- occuMulti(detformulas=detformulas, stateformulas=stateformulas, data=umf)
mod_hdens
Call:
occuMulti(detformulas = detformulas, stateformulas = stateformulas,
data = umf, maxOrder = 2L)
Occupancy:
Estimate SE z P(>|z|)
[coyote] (Intercept) -0.786370 0.0815 -9.64306 5.26e-22
[coyote] scale(HDens_5km) -0.000668 0.0691 -0.00966 9.92e-01
[redfox] (Intercept) -2.391812 0.1598 -14.96684 1.21e-50
[redfox] scale(HDens_5km) 0.504456 0.0813 6.20113 5.61e-10
[coyote:redfox] (Intercept) 1.062921 0.2322 4.57859 4.68e-06
Detection:
Estimate SE z P(>|z|)
[coyote] (Intercept) -0.333 0.0764 -4.36 1.31e-05
[redfox] (Intercept) -0.251 0.1180 -2.13 3.34e-02
AIC: 4800.859
To get the expected probability $\boldsymbol\psi$ for each occupancy state at each site, use predict.
This gives you the probabilities along with standard errors and a 95% CI.
occ_prob <- predict(mod_hdens, type="state")
lapply(occ_prob, head)
Bootstrapping confidence intervals with 100 samples
| psi[11] | psi[10] | psi[01] | psi[00] |
|---|---|---|---|
| 0.07332333 | 0.2725955 | 0.05560920 | 0.5984720 |
| 0.07349727 | 0.2724991 | 0.05574131 | 0.5982624 |
| 0.07366480 | 0.2724062 | 0.05586856 | 0.5980605 |
| 0.07360916 | 0.2724370 | 0.05582630 | 0.5981275 |
| 0.07362022 | 0.2724309 | 0.05583470 | 0.5981142 |
| 0.07378003 | 0.2723423 | 0.05595609 | 0.5979216 |
| psi[11] | psi[10] | psi[01] | psi[00] |
|---|---|---|---|
| 0.009299954 | 0.01372185 | 0.008239449 | 0.01640244 |
| 0.009318341 | 0.01372988 | 0.008250701 | 0.01641179 |
| 0.009336111 | 0.01373803 | 0.008261590 | 0.01642111 |
| 0.009330204 | 0.01373527 | 0.008257969 | 0.01641798 |
| 0.009331377 | 0.01373582 | 0.008258688 | 0.01641860 |
| 0.009348368 | 0.01374387 | 0.008269109 | 0.01642771 |
| psi[11] | psi[10] | psi[01] | psi[00] |
|---|---|---|---|
| 0.05842006 | 0.2492798 | 0.04196283 | 0.5677498 |
| 0.05855664 | 0.2491984 | 0.04205732 | 0.5674990 |
| 0.05868817 | 0.2491199 | 0.04214834 | 0.5672575 |
| 0.05864450 | 0.2491460 | 0.04211811 | 0.5673377 |
| 0.05865317 | 0.2491408 | 0.04212412 | 0.5673218 |
| 0.05877864 | 0.2490659 | 0.04221094 | 0.5670914 |
| psi[11] | psi[10] | psi[01] | psi[00] |
|---|---|---|---|
| 0.09231603 | 0.2957022 | 0.07049045 | 0.6261836 |
| 0.09252915 | 0.2955471 | 0.07064506 | 0.6260361 |
| 0.09273439 | 0.2953979 | 0.07079535 | 0.6258938 |
| 0.09266624 | 0.2954474 | 0.07074545 | 0.6259411 |
| 0.09267978 | 0.2954376 | 0.07075537 | 0.6259317 |
| 0.09287556 | 0.2952953 | 0.07089872 | 0.6257959 |
head(apply(occ_prob$Predicted, 1, sum))
It's often more interesting to look at individual species. For example, you might want to know the marginal occupancy of species 2 (red fox) at a site, which is the probability of red fox occupancy regardless of the occupancy states of other species.
This is calculated by summing all $\psi$ states in which red fox is present:
$$ \psi_{marg,i2} = \psi_{11}+\psi_{01} $$You can do this by specifying the species argument in predict.
redfox_marginal <- predict(mod_hdens, type="state", species="redfox")
head(redfox_marginal)
Bootstrapping confidence intervals with 100 samples
| Predicted | SE | lower | upper | |
|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | |
| 1 | 0.1289325 | 0.01032871 | 0.1090829 | 0.1505733 |
| 2 | 0.1292386 | 0.01035021 | 0.1093578 | 0.1509186 |
| 3 | 0.1295334 | 0.01037112 | 0.1096227 | 0.1512512 |
| 4 | 0.1294355 | 0.01036416 | 0.1095347 | 0.1511407 |
| 5 | 0.1294549 | 0.01036554 | 0.1095522 | 0.1511627 |
| 6 | 0.1297361 | 0.01038561 | 0.1098049 | 0.1514799 |
Compare across species with a plot:
coy_marginal <- predict(mod_hdens, type='state', species="coyote") # get coyote
marg_plot_dat <- rbind(redfox_marginal[1,], coy_marginal[1,])
marg_plot_dat$Species <- c("Red fox", "Coyote")
marg_plot_dat
Bootstrapping confidence intervals with 100 samples
| Predicted | SE | lower | upper | Species |
|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | <chr> |
| 0.1289325 | 0.01032871 | 0.1090829 | 0.1505733 | Red fox |
| 0.3459189 | 0.01633198 | 0.3107790 | 0.3757660 | Coyote |
plot(1:2, marg_plot_dat$Predicted, ylim=c(0.1,0.4),
xlim=c(0.5,2.5), pch=19, cex=1.5, xaxt='n',
xlab="", ylab="Marginal occupancy and 95% CI")
axis(1, at=1:2, labels=marg_plot_dat$Species)
# CIs
top <- 0.1
for (i in 1:2){
segments(i, marg_plot_dat$lower[i], i, marg_plot_dat$upper[i])
segments(i-top, marg_plot_dat$lower[i], i+top)
segments(i-top, marg_plot_dat$upper[i], i+top)
}
Alternatively, you might want to know the probability of occupancy of one species, conditional on the presence of another.
For example, the probability of red fox occupancy, conditional on coyote presence:
$$ \psi_{i, fox, coy} = \frac{\psi_{i{11}}}{\psi_{i{10}} + \psi_{i{11}}}$$And on coyote absence:
$$ \psi_{i, fox, -coy} = \frac{\psi_{i{01}}}{\psi_{i{01}} + \psi_{i{00}}}$$With predict, use the species and cond arguments together for this.
redfox_coy <- predict(mod_hdens, type="state", species="redfox", cond="coyote")
head(redfox_coy)
Bootstrapping confidence intervals with 100 samples
| Predicted | SE | lower | upper | |
|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | |
| 1 | 0.2119669 | 0.02406226 | 0.1689318 | 0.2612879 |
| 2 | 0.2124221 | 0.02410983 | 0.1692723 | 0.2618617 |
| 3 | 0.2128604 | 0.02415575 | 0.1696001 | 0.2624140 |
| 4 | 0.2127149 | 0.02414049 | 0.1694913 | 0.2622307 |
| 5 | 0.2127438 | 0.02414352 | 0.1695129 | 0.2622671 |
| 6 | 0.2131617 | 0.02418739 | 0.1698255 | 0.2627938 |
What about conditional on coyote absence?
redfox_nocoy <- predict(mod_hdens, type="state", species="redfox", cond="-coyote")
head(redfox_nocoy)
Bootstrapping confidence intervals with 100 samples
| Predicted | SE | lower | upper | |
|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | |
| 1 | 0.08501880 | 0.01172126 | 0.06439961 | 0.1066717 |
| 2 | 0.08523088 | 0.01174802 | 0.06457354 | 0.1068801 |
| 3 | 0.08543520 | 0.01177391 | 0.06474114 | 0.1070808 |
| 4 | 0.08536735 | 0.01176530 | 0.06468548 | 0.1070142 |
| 5 | 0.08538083 | 0.01176701 | 0.06469654 | 0.1070274 |
| 6 | 0.08557577 | 0.01179178 | 0.06485647 | 0.1072196 |
You can use this output from predict to generate comparison plots.
plot_data <- rbind(redfox_coy[1,], redfox_nocoy[1,])
plot_data$Coyote_status <- c("Present","Absent")
head(plot_data)
| Predicted | SE | lower | upper | Coyote_status | |
|---|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | <chr> | |
| 1 | 0.2119669 | 0.02406226 | 0.16893179 | 0.2612879 | Present |
| 2 | 0.0850188 | 0.01172126 | 0.06439961 | 0.1066717 | Absent |
plot(1:2, plot_data$Predicted, ylim=c(0, 0.3),
xlim=c(0.5,2.5), pch=19, cex=1.5, xaxt='n',
xlab="Coyote status", ylab="Fox conditional occupancy and 95% CI")
axis(1, at=1:2, labels=plot_data$Coyote_status)
# CIs
top <- 0.1
for (i in 1:2){
segments(i, plot_data$lower[i], i, plot_data$upper[i])
segments(i-top, plot_data$lower[i], i+top)
segments(i-top, plot_data$upper[i], i+top)
}
To plot the effect of housing density on marginal occupancy, we again use predict.
Hdens_5km values for X-axis.hdens_range <- range(siteCovs(umf)$HDens_5km)
hdens_range
hdens_seq <- seq(hdens_range[1], hdens_range[2], length.out=100)
predict occupancy at each value of Hdens_5km along our sequence. Supply the sequence as a data.frame to the newdata argument.nd <- data.frame(HDens_5km = hdens_seq)
occ_hdens_coy <- predict(mod_hdens, type="state", species="coyote", newdata=nd)
occ_hdens_fox <- predict(mod_hdens, type="state", species="redfox", newdata=nd)
head(occ_hdens_coy)
dim(occ_hdens_coy)
Bootstrapping confidence intervals with 100 samples Bootstrapping confidence intervals with 100 samples
| Predicted | SE | lower | upper | |
|---|---|---|---|---|
| <dbl> | <dbl> | <dbl> | <dbl> | |
| 1 | 0.3415035 | 0.01779357 | 0.3115242 | 0.3717624 |
| 2 | 0.3423496 | 0.01755841 | 0.3129794 | 0.3727728 |
| 3 | 0.3432180 | 0.01736981 | 0.3144531 | 0.3738033 |
| 4 | 0.3441090 | 0.01722933 | 0.3159458 | 0.3748543 |
| 5 | 0.3450230 | 0.01713821 | 0.3174575 | 0.3763669 |
| 6 | 0.3459604 | 0.01709731 | 0.3186015 | 0.3779619 |
plot(hdens_seq, occ_hdens_coy$Predicted, type = 'l', col = 'red',
xlab = "Housing density", ylab = "Marginal occupancy", ylim = c(0,0.8))
lines(hdens_seq, occ_hdens_fox$Predicted, col = "blue")
legend("bottomright", col=c("red", "blue"), lty=1, legend=c("coyote", "red fox"))
stateformulas <- c("~scale(HDens_5km)", "~scale(HDens_5km)")
mod_noint <- occuMulti(detformulas=detformulas, stateformulas=stateformulas, data=umf, maxOrder=1)
mod_noint
Call:
occuMulti(detformulas = detformulas, stateformulas = stateformulas,
data = umf, maxOrder = 1)
Occupancy:
Estimate SE z P(>|z|)
[coyote] (Intercept) -0.631 0.0745 -8.47 2.49e-17
[coyote] scale(HDens_5km) 0.108 0.0628 1.72 8.50e-02
[redfox] (Intercept) -1.921 0.0998 -19.25 1.52e-82
[redfox] scale(HDens_5km) 0.514 0.0811 6.34 2.27e-10
Detection:
Estimate SE z P(>|z|)
[coyote] (Intercept) -0.332 0.0763 -4.36 1.33e-05
[redfox] (Intercept) -0.261 0.1195 -2.19 2.87e-02
AIC: 4822.196
umf_coy <- unmarkedFrameOccu(y=ylist$coyote, siteCovs=site_covs)
occu(~1~scale(HDens_5km), umf_coy)
Call:
occu(formula = ~1 ~ scale(HDens_5km), data = umf_coy)
Occupancy:
Estimate SE z P(>|z|)
(Intercept) -0.631 0.0745 -8.47 2.51e-17
scale(HDens_5km) 0.108 0.0628 1.72 8.49e-02
Detection:
Estimate SE z P(>|z|)
-0.332 0.0763 -4.36 1.32e-05
AIC: 3245.169
MesoCarnivores dataset (i.e., add bobcat)unmarkedunmarked hints: set maxOrder argument to occuMulti to 2. stateformulas: for f1, f2, f3, f12, f13, f23cat(file="jags_multispecies_3.txt", "
model {
# State process
for (i in 1:N){
# latent occupancy state
Z[i] ~ dcat(Psi[i, 1:8]) # Takes on values 1-8
# Occupancy probabilities
Psi[i, 1] <- exp(f1[i] + f2[i] + f3[i] + f12[i] + f13[i] + f23[i]) # {111}
Psi[i, 2] <- exp(f1[i] + f2[i] + f12[i]) # {110}
Psi[i, 3] <- exp(f1[i] + f3[i] + f13[i]) # {101}
Psi[i, 4] <- exp(f2[i] + f3[i] + f23[i]) # {011}
Psi[i, 5] <- exp(f1[i]) # {100}
Psi[i, 6] <- exp(f2[i]) # {010}
Psi[i, 7] <- exp(f3[i]) # {001}
Psi[i, 8] <- 1 # {000}
# Natural parameters
f1[i] <- alpha_f1 + beta_f1 * hdens[i]
f2[i] <- alpha_f2 + beta_f2 * hdens[i]
f3[i] <- alpha_f3 + beta_f3 * hdens[i]
f12[i] <- alpha_f12
f13[i] <- alpha_f13
f23[i] <- alpha_f23
}
# State priors
alpha_f1 ~ dunif(-5, 5)
beta_f1 ~ dnorm(0, 0.01)
alpha_f2 ~ dunif(-5, 5)
beta_f2 ~ dnorm(0, 0.01)
alpha_f3 ~ dunif(-5, 5)
beta_f3 ~ dnorm(0, 0.01)
alpha_f12 ~ dunif(-5,5)
alpha_f13 ~ dunif(-5,5)
alpha_f23 ~ dunif(-5,5)
# Detection process
for (i in 1:N){
z1[i] <- (Z[i] == 1) + (Z[i] == 2) + (Z[i] == 3) + (Z[i] == 5) # is coyote present?
z2[i] <- (Z[i] == 1) + (Z[i] == 2) + (Z[i] == 4) + (Z[i] == 6) # is red fox present?
z3[i] <- (Z[i] == 1) + (Z[i] == 3) + (Z[i] == 4) + (Z[i] == 7) # is bobcat present?
for (j in 1:J){
y_coy[i, j] ~ dbern(z1[i] * p1)
y_fox[i, j] ~ dbern(z2[i] * p2)
y_bob[i, j] ~ dbern(z3[i] * p3)
}
}
# Detection priors
p1 ~ dunif(0,1)
p2 ~ dunif(0,1)
p3 ~ dunif(0,1)
}
")
unmarked code¶ylist <- MesoCarnivores[c(1:3)]
umf <- unmarkedFrameOccuMulti(y=ylist, siteCovs=site_covs)
stateformulas <- c("~scale(HDens_5km)","~scale(HDens_5km)","~scale(HDens_5km)","~1","~1","~1")
detformulas <- rep("~1", 3)
(mod_3species <- occuMulti(detformulas=detformulas, stateformulas=stateformulas, data=umf, maxOrder = 2))
Call:
occuMulti(detformulas = detformulas, stateformulas = stateformulas,
data = umf, maxOrder = 2)
Occupancy:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -2.5364 0.2842 -8.92 4.53e-19
[bobcat] scale(HDens_5km) -3.6008 0.9191 -3.92 8.94e-05
[coyote] (Intercept) -1.3439 0.1470 -9.14 6.04e-20
[coyote] scale(HDens_5km) 0.0884 0.0742 1.19 2.34e-01
[redfox] (Intercept) -2.2555 0.1566 -14.41 4.65e-47
[redfox] scale(HDens_5km) 0.4392 0.0831 5.29 1.24e-07
[bobcat:coyote] (Intercept) 1.8173 0.2714 6.70 2.16e-11
[bobcat:redfox] (Intercept) -1.3385 0.3869 -3.46 5.42e-04
[coyote:redfox] (Intercept) 1.3740 0.2597 5.29 1.22e-07
Detection:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -1.139 0.1421 -8.02 1.07e-15
[coyote] (Intercept) -0.328 0.0759 -4.32 1.53e-05
[redfox] (Intercept) -0.252 0.1179 -2.14 3.26e-02
AIC: 6530.476
state_complex <- c(rep("~scale(Dist_5km)+scale(HDens_5km)", 6), 0)
det_complex <- rep("~Trail",3)
mod_complex <- occuMulti(stateformulas=state_complex, detformulas=det_complex, umf)
mod_complex
Call:
occuMulti(detformulas = det_complex, stateformulas = state_complex,
data = umf, maxOrder = 3L)
Occupancy:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -23.0171 5.787 -3.977 6.97e-05
[bobcat] scale(Dist_5km) -2.4249 0.689 -3.518 4.34e-04
[bobcat] scale(HDens_5km) -82.3836 19.799 -4.161 3.17e-05
[coyote] (Intercept) -0.6789 0.225 -3.017 2.55e-03
[coyote] scale(Dist_5km) -0.0176 0.139 -0.127 8.99e-01
[coyote] scale(HDens_5km) -0.5534 0.748 -0.740 4.59e-01
[redfox] (Intercept) -1.3946 0.257 -5.425 5.79e-08
[redfox] scale(Dist_5km) -0.5293 0.250 -2.114 3.45e-02
[redfox] scale(HDens_5km) 0.2108 0.261 0.808 4.19e-01
[bobcat:coyote] (Intercept) 6.7598 6.390 1.058 2.90e-01
[bobcat:coyote] scale(Dist_5km) 1.6979 0.695 2.444 1.45e-02
[bobcat:coyote] scale(HDens_5km) 17.9202 21.460 0.835 4.04e-01
[bobcat:redfox] (Intercept) 15.3983 3.461 4.449 8.64e-06
[bobcat:redfox] scale(Dist_5km) 0.8836 0.439 2.014 4.40e-02
[bobcat:redfox] scale(HDens_5km) 64.1330 12.375 5.182 2.19e-07
[coyote:redfox] (Intercept) 1.1084 0.363 3.050 2.29e-03
[coyote:redfox] scale(Dist_5km) 0.1149 0.340 0.338 7.35e-01
[coyote:redfox] scale(HDens_5km) 0.9046 0.781 1.159 2.47e-01
Detection:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -2.83 0.1419 -19.91 3.12e-88
[bobcat] Trail 1.74 0.1542 11.26 2.10e-29
[coyote] (Intercept) -1.96 0.0984 -19.88 5.90e-88
[coyote] Trail 2.17 0.1220 17.75 1.63e-70
[redfox] (Intercept) -1.59 0.1601 -9.93 2.97e-23
[redfox] Trail 1.78 0.1997 8.93 4.12e-19
AIC: 5958.196
optimizePenalty function (Clipp et al. 2021)"Bayes-inspired" penalty (Hutchinson et al. 2015):
$$ -\lambda\frac{1}{2}\sum_i{}\theta_i^2 $$Where $\theta$ is the vector of parameter estimates.
Size of penalty increases as values of $\theta$ increase.
Tradeoff:
mod_penalty <- occuMulti(stateformulas=state_complex, detformulas=det_complex, umf, penalty=1)
mod_penalty
Bootstraping covariance matrix
Call:
occuMulti(detformulas = det_complex, stateformulas = state_complex,
data = umf, penalty = 1, maxOrder = 3L)
Occupancy:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -1.7810 0.269 -6.627 3.43e-11
[bobcat] scale(Dist_5km) -1.3143 0.293 -4.491 7.08e-06
[bobcat] scale(HDens_5km) -2.8200 0.539 -5.230 1.70e-07
[coyote] (Intercept) -0.6049 0.214 -2.830 4.66e-03
[coyote] scale(Dist_5km) 0.0285 0.106 0.270 7.87e-01
[coyote] scale(HDens_5km) -1.0908 0.378 -2.887 3.88e-03
[redfox] (Intercept) -1.5659 0.305 -5.140 2.75e-07
[redfox] scale(Dist_5km) -0.3068 0.157 -1.958 5.02e-02
[redfox] scale(HDens_5km) 0.4730 0.546 0.867 3.86e-01
[bobcat:coyote] (Intercept) 1.1871 0.434 2.736 6.22e-03
[bobcat:coyote] scale(Dist_5km) 0.9347 0.346 2.705 6.84e-03
[bobcat:coyote] scale(HDens_5km) -0.3218 1.008 -0.319 7.50e-01
[bobcat:redfox] (Intercept) -0.8831 0.347 -2.545 1.09e-02
[bobcat:redfox] scale(Dist_5km) 0.0364 0.305 0.119 9.05e-01
[bobcat:redfox] scale(HDens_5km) 2.5609 1.074 2.384 1.71e-02
[coyote:redfox] (Intercept) 1.0001 0.310 3.227 1.25e-03
[coyote:redfox] scale(Dist_5km) 0.0236 0.167 0.141 8.87e-01
[coyote:redfox] scale(HDens_5km) 1.3920 0.345 4.030 5.57e-05
Detection:
Estimate SE z P(>|z|)
[bobcat] (Intercept) -2.44 0.1755 -13.89 7.63e-44
[bobcat] Trail 1.74 0.1966 8.87 7.25e-19
[coyote] (Intercept) -1.89 0.0926 -20.45 5.64e-93
[coyote] Trail 2.10 0.1179 17.84 3.31e-71
[redfox] (Intercept) -1.49 0.1782 -8.35 6.64e-17
[redfox] Trail 1.72 0.1714 10.04 9.73e-24
AIC: 6135.555
How to choose penalty value? optimizePenalty() function.
mod_penalty <- optimizePenalty(mod_complex, penalties=c(0.5,1))
Clipp, H.L., Evans, A.L., Kessinger, B.E., Kellner, K. and Rota, C.T. (2021). A penalized likelihood for multispecies occupancy models improves predictions of species interactions. Ecology, p.e03520.
MacKenzie, D. I., Bailey, L. L., & Nichols, J. D. (2004). Investigating species co‐occurrence patterns when species are detected imperfectly. Journal of Animal Ecology, 73(3), 546-555.
Richmond, O.M., Hines, J.E. and Beissinger, S.R. (2010). Two‐species occupancy models: a new parameterization applied to co‐occurrence of secretive rails. Ecological Applications, 20(7), pp.2036-2046.
Ridout, M. S., & Linkie, M. (2009). Estimating overlap of daily activity patterns from camera trap data. Journal of Agricultural, Biological, and Environmental Statistics, 14, 322-337.
Rota, C.T., Ferreira, M.A., Kays, R.W., Forrester, T.D., Kalies, E.L., McShea, W.J., Parsons, A.W. and Millspaugh, J.J. (2016). A multispecies occupancy model for two or more interacting species. Methods in Ecology and Evolution, 7(10), pp.1164-1173.
Waddle, J.H., Dorazio, R.M., Walls, S.C., Rice, K.G., Beauchamp, J., Schuman, M.J. and Mazzotti, F.J. (2010). A new parameterization for estimating co‐occurrence of interacting species. Ecological Applications, 20(5), pp.1467-1475.