# Multi species single season with a list of models
# Co-occurence of Jordan's salamander (Plethodon jordani) (PJ) and
# members of Plethodon glutinosus (PG) in Great Smokey
# Mountains National Park (MacKenzie et al. 2004).
# 2020-06-24 CJS real$nu and real$rho now in derived$nu and derived$rho
# 2018-09-27 Code contributed by Carl James Schwarz (cschwarz.stat.sfu.cs@gmail.com)
library(ggplot2)
library(readxl)
library(reshape2)
library(RPresence)
# get the data
PG.data <- readxl::read_excel(file.path("..","Salamander2.xls"),
sheet="RawData", na='-',
col_names=FALSE,
range = "B3:F90")
## New names:
## * `` -> ...1
## * `` -> ...2
## * `` -> ...3
## * `` -> ...4
## * `` -> ...5
PJ.data <- readxl::read_excel(file.path("..","Salamander2.xls"),
sheet="RawData", na='-',
col_names=FALSE,
range = "H3:L90")
## New names:
## * `` -> ...1
## * `` -> ...2
## * `` -> ...3
## * `` -> ...4
## * `` -> ...5
# Convert history to compressed format
# 0=neither species detected,
# 1=only species A detected,
# 2=only species B detected,
# 3=both species detected
input.history <- PG.data + 2*PJ.data
input.history
## ...1 ...2 ...3 ...4 ...5
## 1 1 0 0 0 0
## 2 0 1 0 0 0
## 3 0 0 0 0 0
## 4 1 0 0 0 0
## 5 0 0 0 0 0
## 6 0 0 1 1 1
## 7 0 0 0 0 0
## 8 0 0 0 0 0
## 9 0 0 0 0 0
## 10 1 1 0 0 0
## 11 0 0 0 0 0
## 12 0 0 1 1 0
## 13 0 0 1 0 1
## 14 0 0 1 0 1
## 15 1 1 0 1 0
## 16 1 1 1 0 1
## 17 0 1 0 1 0
## 18 1 1 1 0 1
## 19 0 0 0 0 0
## 20 0 1 0 0 0
## 21 NA NA NA 1 0
## 22 0 0 0 1 1
## 23 0 1 1 1 1
## 24 0 0 0 0 0
## 25 1 1 1 3 3
## 26 3 1 1 1 1
## 27 1 1 1 1 1
## 28 0 0 0 0 0
## 29 0 0 0 0 0
## 30 0 0 0 0 0
## 31 0 1 1 0 1
## 32 2 0 2 0 0
## 33 1 1 0 0 0
## 34 0 1 0 0 0
## 35 1 1 1 1 1
## 36 NA 1 1 1 3
## 37 NA 2 2 2 2
## 38 0 0 0 0 0
## 39 1 1 1 3 1
## 40 1 1 1 1 1
## 41 1 1 1 1 1
## 42 0 1 0 0 1
## 43 1 0 1 0 0
## 44 1 1 0 1 0
## 45 1 1 1 1 1
## 46 1 0 1 0 2
## 47 0 1 0 0 0
## 48 NA 2 2 2 2
## 49 2 2 3 2 0
## 50 1 0 0 0 0
## 51 NA 1 1 1 1
## 52 NA 1 1 0 1
## 53 2 2 2 2 2
## 54 1 3 3 3 3
## 55 0 0 1 1 0
## 56 2 0 2 2 2
## 57 NA 2 0 2 2
## 58 2 2 2 2 2
## 59 2 2 2 2 2
## 60 NA 0 0 2 0
## 61 3 2 2 2 2
## 62 NA 2 0 0 2
## 63 NA 2 2 0 2
## 64 0 0 1 2 3
## 65 0 0 2 3 2
## 66 2 2 2 3 2
## 67 2 2 2 0 2
## 68 2 2 2 2 2
## 69 0 1 1 1 1
## 70 0 0 2 2 3
## 71 2 2 2 2 2
## 72 2 2 2 2 2
## 73 2 2 2 2 2
## 74 2 0 2 2 2
## 75 NA 2 2 2 2
## 76 0 3 3 2 0
## 77 2 2 2 2 2
## 78 NA 2 2 2 2
## 79 NA 2 2 2 2
## 80 2 3 3 2 2
## 81 0 2 2 2 2
## 82 2 2 2 2 2
## 83 2 2 2 2 2
## 84 2 2 2 2 2
## 85 0 1 1 1 0
## 86 2 2 2 2 2
## 87 0 0 2 2 2
## 88 2 2 2 2 2
site.covar <- readxl::read_excel(file.path("..","Salamander2.xls"),
sheet="RawData", col_names=TRUE,
range = "N2:O90")
names(site.covar)
## [1] "Elevation (m)" "Std. Elevation"
names(site.covar) <- make.names(names(site.covar))
names(site.covar)
## [1] "Elevation..m." "Std..Elevation"
# Standardize Elevation covariates
elevation.mean <- mean(site.covar$Elevation..m.)
elevation.std <- sd (site.covar$Elevation..m.)
site.covar$Std..Elevation <- (site.covar$Elevation..m. - elevation.mean)/elevation.std
Nsites <- nrow(input.history)
Nvisits<- ncol(input.history)
# create the pao file
salamander.pao <- createPao(input.history,
unitcov=site.covar,
title="Salamander multi species - co-occurance")
summary(salamander.pao)
## paoname=pres.pao
## title=Salamander multi species - co-occurance
## Naive occ=0.8636364
## naiveR =0.4204545
## nunits nsurveys nseasons nsurveyseason nmethods
## "88" "5" "1" "5" "1"
## nunitcov nsurvcov
## "2" "1"
## unit covariates : Elevation..m. Std..Elevation
## survey covariates: SURVEY
# Use the psiAB parameterization
# Parameters of the model are
# psiA - occupancy probability of species A
# psiBA - occupancy probability of species B if species A is present
# psiBa - occupancy probability of species B if species A is absent
#
# If species are independent thatn psiBA = psiBa.
# Alternatively, nu = odds(B|A)/odds(B|a) = 1.
#
# Detection parameters
# pA - probability of detection if A is alone in the site
# pB - probability of detection if B is alone in the site
# rA - probability of detecting A only given both on the site
# rBA - probability of detecting B given that A was detected and both are on the site
# rBa - probability of detecting B given that A not detected and both are on the site
# Ifspecies do not interact, then
# rBA = rBa
# and
# rho = odds(rBA)/odds(rBa) = 1
#
# The following special variables are available for modelling PSI
# SP species effect
# INT interaction of effect on presence of species B when species A was, or was not present
#
# Model for PSI.... impact on parameters
# psi~1 psiA=psiBA=psiBa (1 parameter)
# psi~SP psiA psiBA=psiBa (2 parameters)
# psi~SP+INT psiA psiBA psiBa (3 parameters)
#
# The following special variables are available for p
# SP species effect
# INT_o is a detection-level interaction where the OCCUPANCY of one species
# changes the detection probability of the other species
# INT_d is a detection-level interaction where DETECTION of one species changes the
# detection probability of the other species in the same survey.
#
# Model for p.... impact on parameters
# p~1 pA = pB = rA = rBA = rBa (1 parameter)
# p~SP pA=rA, pB=rBA=rBa (2 parameters)
# p~SP+INT_o pA=rA, pB, rBA=rBa (3 parameters)
# p~SP+INT_o+SP:INT_o pA, rA, pB, rBA=rBa (4 parameters)
# p~SP+INT_o+SP:INT_o+INT_d pA, rA, pB, rBA, rBa (5 parameters)
# define the list of models to fit
# Notice the commas between the column and the placement of the quotes
model.list.csv <- textConnection("
p, psi
~SP+INT_o+SP:INT_o + INT_d, ~SP+INT
~SP+INT_o+SP:INT_o + INT_d, ~SP
~SP+INT_o+SP:INT_o , ~SP+INT
~SP+INT_o+SP:INT_o, ~SP + Std..Elevation+SP:Std..Elevation+INT+INT:Std..Elevation")
model.list <- read.csv(model.list.csv, header=TRUE, as.is=TRUE, strip.white=TRUE)
model.list
## p
## 1 ~SP+INT_o+SP:INT_o + INT_d
## 2 ~SP+INT_o+SP:INT_o + INT_d
## 3 ~SP+INT_o+SP:INT_o
## 4 ~SP+INT_o+SP:INT_o
## psi
## 1 ~SP+INT
## 2 ~SP
## 3 ~SP+INT
## 4 ~SP + Std..Elevation+SP:Std..Elevation+INT+INT:Std..Elevation
# fit the models
model.fits <- plyr::alply(model.list, 1, function(x,detect.pao){
cat("\n\n***** Starting ", unlist(x), "\n")
fit <- RPresence::occMod(model=list(as.formula(paste("psi",x$psi)),
as.formula(paste("p" ,x$p ))),
data=detect.pao,type="so.2sp.1", randint=10)
fit
},detect.pao=salamander.pao)
##
##
## ***** Starting ~SP+INT_o+SP:INT_o + INT_d ~SP+INT
##
##
## ***** Starting ~SP+INT_o+SP:INT_o + INT_d ~SP
##
##
## ***** Starting ~SP+INT_o+SP:INT_o ~SP+INT
##
##
## ***** Starting ~SP+INT_o+SP:INT_o ~SP + Std..Elevation+SP:Std..Elevation+INT+INT:Std..Elevation
# Look the output from a specific model
check.model <- 4
names(model.fits[[check.model]])
## [1] "modname" "model" "dmat" "data" "outfile"
## [6] "neg2loglike" "npar" "aic" "beta" "real"
## [11] "derived" "warnings"
model.fits[[check.model]]$beta
## $psi
## est se
## A1_psiA 0.372485 0.244388
## A2_psiBA -0.984296 0.451688
## A3_psiA.Std..Elevation_psiA -0.686944 0.275267
## A4_psiBa 4.503822 4.326472
## A5_psiBA.SP2:Std..Elevation_psiBA 3.142486 0.810764
## A6_psiBa.Std..Elevation:INT2_psiBa 16.043128 18.128542
##
## $psi.VC
## A1 A2 A3 A4 A5 A6
## A1 0.059726 -0.056538 -0.010194 0.046398 0.012918 0.291997
## A2 -0.056538 0.204022 0.011789 -0.132702 -0.050124 0.190452
## A3 -0.010194 0.011789 0.075772 -0.001861 -0.073781 0.014501
## A4 0.046398 -0.132702 -0.001861 18.718359 -0.014198 73.306481
## A5 0.012918 -0.050124 -0.073781 -0.014198 0.657338 -0.966796
## A6 0.291997 0.190452 0.014501 73.306481 -0.966796 328.644030
##
## $p
## est se
## B1_pA[1] 0.155253 0.168438
## B2_pB[1] 2.285572 0.461505
## B3_rA[1] -0.297429 0.315181
## B4_rBA[1] -1.949537 0.536616
##
## $p.VC
## B1 B2 B3 B4
## B1 0.028371 -0.027789 -0.029443 0.029098
## B2 -0.027789 0.212987 -0.004657 -0.186931
## B3 -0.029443 -0.004657 0.099339 -0.065900
## B4 0.029098 -0.186931 -0.065900 0.287957
##
## $VC
## A1 A2 A3 A4 A5 A6 B1
## A1 0.059726 -0.056538 -0.010194 0.046398 0.012918 0.291997 -0.003685
## A2 -0.056538 0.204022 0.011789 -0.132702 -0.050124 0.190452 0.004126
## A3 -0.010194 0.011789 0.075772 -0.001861 -0.073781 0.014501 0.005789
## A4 0.046398 -0.132702 -0.001861 18.718359 -0.014198 73.306481 -0.008110
## A5 0.012918 -0.050124 -0.073781 -0.014198 0.657338 -0.966796 -0.008045
## A6 0.291997 0.190452 0.014501 73.306481 -0.966796 328.644030 -0.008416
## B1 -0.003685 0.004126 0.005789 -0.008110 -0.008045 -0.008416 0.028371
## B2 0.019087 -0.003256 0.004419 0.202552 0.002082 1.379862 -0.027789
## B3 -0.004297 -0.002741 -0.012126 -0.075764 0.013603 -0.589965 -0.029443
## B4 -0.011609 -0.004460 0.002432 -0.135966 -0.010059 -0.929339 0.029098
## B2 B3 B4
## A1 0.019087 -0.004297 -0.011609
## A2 -0.003256 -0.002741 -0.004460
## A3 0.004419 -0.012126 0.002432
## A4 0.202552 -0.075764 -0.135966
## A5 0.002082 0.013603 -0.010059
## A6 1.379862 -0.589965 -0.929339
## B1 -0.027789 -0.029443 0.029098
## B2 0.212987 -0.004657 -0.186931
## B3 -0.004657 0.099339 -0.065900
## B4 -0.186931 -0.065900 0.287957
model.fits[[check.model]]$dmat
## $psi
## a1 a2 a3 a4 a5
## psiA "1" "0" "Std..Elevation_psiA" "0" "0"
## psiBA "1" "1" "Std..Elevation_psiBA" "0" "SP2:Std..Elevation_psiBA"
## psiBa "1" "1" "Std..Elevation_psiBa" "1" "SP2:Std..Elevation_psiBa"
## a6
## psiA "0"
## psiBA "0"
## psiBa "Std..Elevation:INT2_psiBa"
##
## $p
## b1 b2 b3 b4
## pA[1] "1" "0" "0" "0"
## pA[2] "1" "0" "0" "0"
## pA[3] "1" "0" "0" "0"
## pA[4] "1" "0" "0" "0"
## pA[5] "1" "0" "0" "0"
## pB[1] "1" "1" "0" "0"
## pB[2] "1" "1" "0" "0"
## pB[3] "1" "1" "0" "0"
## pB[4] "1" "1" "0" "0"
## pB[5] "1" "1" "0" "0"
## rA[1] "1" "0" "1" "0"
## rA[2] "1" "0" "1" "0"
## rA[3] "1" "0" "1" "0"
## rA[4] "1" "0" "1" "0"
## rA[5] "1" "0" "1" "0"
## rBA[1] "1" "1" "1" "1"
## rBA[2] "1" "1" "1" "1"
## rBA[3] "1" "1" "1" "1"
## rBA[4] "1" "1" "1" "1"
## rBA[5] "1" "1" "1" "1"
## rBa[1] "1" "1" "1" "1"
## rBa[2] "1" "1" "1" "1"
## rBa[3] "1" "1" "1" "1"
## rBa[4] "1" "1" "1" "1"
## rBa[5] "1" "1" "1" "1"
names(model.fits[[check.model]]$real)
## [1] "psiA" "psiBA" "psiBa" "pA" "pB" "rA" "rBA" "rBa"
model.fits[[check.model]]$real$psiA[1:5,]
## est se lower upper
## unit1 0.8640 0.0791 0.6292 0.9596
## unit2 0.8560 0.0800 0.6250 0.9549
## unit3 0.8560 0.0800 0.6250 0.9549
## unit4 0.8560 0.0800 0.6250 0.9549
## unit5 0.8476 0.0806 0.6208 0.9497
model.fits[[check.model]]$real$psiBA[1:5,]
## est se lower upper
## unit1 0.0028 0.0048 1e-04 0.0766
## unit2 0.0035 0.0058 1e-04 0.0837
## unit3 0.0035 0.0058 1e-04 0.0837
## unit4 0.0035 0.0058 1e-04 0.0837
## unit5 0.0044 0.0070 2e-04 0.0914
model.fits[[check.model]]$real$psiBa[1:5,]
## est se lower upper
## unit1 0 0 0 1
## unit2 0 0 0 1
## unit3 0 0 0 1
## unit4 0 0 0 1
## unit5 0 0 0 1
model.fits[[check.model]]$derived$nu[1:5,]
## est se lower upper
## unit1 1.036587e+13 3.622318e+14 1.862559e-17 5.769013e+42
## unit2 2.200140e+12 7.303357e+13 1.219893e-16 3.968065e+40
## unit3 2.200140e+12 7.303357e+13 1.219893e-16 3.968065e+40
## unit4 2.200140e+12 7.303357e+13 1.219893e-16 3.968065e+40
## unit5 4.669762e+11 1.468429e+13 7.986638e-16 2.730395e+38
model.fits[[check.model]]$real$pA[1:5,]
## est se lower upper
## unit1_1-1 0.5387 0.0419 0.4564 0.619
## unit2_1-1 0.5387 0.0419 0.4564 0.619
## unit3_1-1 0.5387 0.0419 0.4564 0.619
## unit4_1-1 0.5387 0.0419 0.4564 0.619
## unit5_1-1 0.5387 0.0419 0.4564 0.619
model.fits[[check.model]]$real$pB[1:5,]
## est se lower upper
## unit1_1-1 0.9199 0.0318 0.8315 0.9639
## unit2_1-1 0.9199 0.0318 0.8315 0.9639
## unit3_1-1 0.9199 0.0318 0.8315 0.9639
## unit4_1-1 0.9199 0.0318 0.8315 0.9639
## unit5_1-1 0.9199 0.0318 0.8315 0.9639
model.fits[[check.model]]$real$rA[1:5,]
## est se lower upper
## unit1_1-1 0.4645 0.0653 0.3416 0.5919
## unit2_1-1 0.4645 0.0653 0.3416 0.5919
## unit3_1-1 0.4645 0.0653 0.3416 0.5919
## unit4_1-1 0.4645 0.0653 0.3416 0.5919
## unit5_1-1 0.4645 0.0653 0.3416 0.5919
model.fits[[check.model]]$real$rBA[1:5,]
## est se lower upper
## unit1_1-1 0.5483 0.0593 0.4315 0.66
## unit2_1-1 0.5483 0.0593 0.4315 0.66
## unit3_1-1 0.5483 0.0593 0.4315 0.66
## unit4_1-1 0.5483 0.0593 0.4315 0.66
## unit5_1-1 0.5483 0.0593 0.4315 0.66
model.fits[[check.model]]$real$rBa[1:5,]
## est se lower upper
## unit1_1-1 0.5483 0.0593 0.4315 0.66
## unit2_1-1 0.5483 0.0593 0.4315 0.66
## unit3_1-1 0.5483 0.0593 0.4315 0.66
## unit4_1-1 0.5483 0.0593 0.4315 0.66
## unit5_1-1 0.5483 0.0593 0.4315 0.66
model.fits[[check.model]]$derived$rho[1:5,]
## est se lower upper
## unit1_1-1 1 0 1 1
## unit2_1-1 1 0 1 1
## unit3_1-1 1 0 1 1
## unit4_1-1 1 0 1 1
## unit5_1-1 1 0 1 1
names(model.fits[[check.model]]$derived)
## [1] "nu" "rho"
# Model averaging
aic.table <- RPresence::createAicTable(model.fits)
aic.table$table
## Model
## 4 psi(SP P Std..Elevation P SP T Std..Elevation P INT P INT T Std..Elevation)p(SP P INT_o P SP T INT_o)
## 3 psi(SP P INT)p(SP P INT_o P SP T INT_o)
## 1 psi(SP P INT)p(SP P INT_o P SP T INT_o P INT_d)
## 2 psi(SP)p(SP P INT_o P SP T INT_o P INT_d)
## AIC neg2ll npar warn.conv warn.VC DAIC modlike wgt
## 4 687.4714 667.4714 10 7 0 0.0000 1 1
## 3 750.6240 736.6240 7 7 0 63.1526 0 0
## 1 751.8665 735.8665 8 7 0 64.3951 0 0
## 2 760.3286 746.3286 7 7 0 72.8572 0 0
names(aic.table)
## [1] "table" "models" "ess"
ma.psiA<- RPresence::modAvg(aic.table, param="psiA")
ma.psiA$parameter <- "psiA"
ma.psiA <- cbind(ma.psiA, site.covar)
ma.psiBA<- RPresence::modAvg(aic.table, param="psiBA")
ma.psiBA$parameter <- "psiBA"
ma.psiBA <- cbind(ma.psiBA, site.covar)
ma.psiBa<- RPresence::modAvg(aic.table, param="psiBa")
ma.psiBa$parameter <- "psiBa"
ma.psiBa <- cbind(ma.psiBa, site.covar)
ma.psiB <- site.covar
ma.psiB$parameter <- 'psiB'
ma.psiB$est <- ma.psiBA$est * ma.psiA$est +
ma.psiBa$est * (1-ma.psiA$est)
# too hard to compute the se
psi.all <- plyr::rbind.fill(ma.psiA, ma.psiBA, ma.psiBa, ma.psiB)
head(psi.all)
## est se lower_0.95 upper_0.95 parameter Elevation..m. Std..Elevation
## 1 0.8640 0.0791 0.6293739 0.9596239 psiA 502.92 -2.148793
## 2 0.8560 0.0800 0.6249035 0.9549764 psiA 518.16 -2.052179
## 3 0.8560 0.0800 0.6249035 0.9549764 psiA 518.16 -2.052179
## 4 0.8560 0.0800 0.6249035 0.9549764 psiA 518.16 -2.052179
## 5 0.8476 0.0806 0.6208022 0.9497337 psiA 533.40 -1.955564
## 6 0.8388 0.0811 0.6162744 0.9440060 psiA 548.64 -1.858950
ggplot(data=psi.all, aes(x=Elevation..m., y=est))+
ggtitle("Estimated occupancy parameters vs. elevation")+
geom_point()+
geom_ribbon( aes(ymin=lower_0.95, ymax=upper_0.95),alpha=0.2)+
facet_wrap(~parameter, ncol=2)
## Warning in max(ids, na.rm = TRUE): no non-missing arguments to max; returning -
## Inf

# Why is psiBa so bad? This may be a case of complete separation.
# The detection probabilities are all fairly large so that over 5 visits, there is almost certain detection
# if the species is present. Consequently, the observed occupancy is likely a pretty good indication of the
# actual occupancy.
RPresence::modAvg(aic.table, param="pA")[1,]
## est se lower_0.95 upper_0.95
## unit1_1-1 0.5387 0.0419 0.4562721 0.6190646
RPresence::modAvg(aic.table, param="pB")[1,]
## est se lower_0.95 upper_0.95
## unit1_1-1 0.9199 0.0318 0.8313355 0.9639751
RPresence::modAvg(aic.table, param="rA")[1,]
## est se lower_0.95 upper_0.95
## unit1_1-1 0.4645 0.0653 0.3414643 0.5920139
RPresence::modAvg(aic.table, param="rBA")[1,]
## est se lower_0.95 upper_0.95
## unit1_1-1 0.5483 0.0593 0.4315627 0.6599535
RPresence::modAvg(aic.table, param="rBa")[1,]
## est se lower_0.95 upper_0.95
## unit1_1-1 0.5483 0.0593 0.4315627 0.6599535
# Compute the observed occupance of each species
obs.occ <- data.frame(Elevation..m. = site.covar$Elevation..m.,
ooPG = apply(PG.data,1,max, na.rm=TRUE),
ooPJ = apply(PJ.data,1,max, na.rm=TRUE))
head(obs.occ)
## Elevation..m. ooPG ooPJ
## 1 502.92 1 0
## 2 518.16 1 0
## 3 518.16 0 0
## 4 518.16 1 0
## 5 533.40 0 0
## 6 548.64 1 0
ggplot(data=obs.occ, aes(x=Elevation..m., y=ooPJ))+
ggtitle("Observed occupancy of PJ given PG")+
geom_point()+
geom_smooth(method="glm", method.args = list(family = quasibinomial(link = 'logit')))+
facet_wrap(~ooPG, ncol=1)
## `geom_smooth()` using formula 'y ~ x'

# and look at when the species are reversed
ggplot(data=obs.occ, aes(x=Elevation..m., y=ooPG))+
ggtitle("Observed occupancy of PG given PJ")+
geom_point()+
geom_smooth(method="glm", method.args = list(family = quasibinomial(link = 'logit')))+
facet_wrap(~ooPJ, ncol=1)
## `geom_smooth()` using formula 'y ~ x'
