Skip to content

Commit

Permalink
Merge pull request #169 from kenkellner/multmixOpen
Browse files Browse the repository at this point in the history
Add multmixOpen() function
  • Loading branch information
rbchan authored Mar 20, 2020
2 parents bc2e8d4 + 268da69 commit 261fbf2
Show file tree
Hide file tree
Showing 22 changed files with 1,308 additions and 46 deletions.
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: unmarked
Version: 0.13-2.9001
Date: 2020-03-19
Version: 0.13-2.9002
Date: 2020-03-20
Type: Package
Title: Models for Data from Unmarked Animals
Author: Ian Fiske, Richard Chandler, David Miller, Andy Royle, Marc Kery, Jeff Hostetler, Rebecca Hutchinson, Adam Smith, Ken Kellner
Expand All @@ -17,7 +17,7 @@ Collate: 'classes.R' 'unmarkedEstimate.R' 'mapInfo.R' 'unmarkedFrame.R'
'pcountOpen.R' 'gdistsamp.R' 'unmarkedFitList.R' 'unmarkedLinComb.R'
'ranef.R' 'boot.R' 'occuFP.R' 'gpcount.R' 'occuPEN.R' 'pcount.spHDS.R'
'occuMS.R' 'occuTTD.R' 'unmarkedCrossVal.R' 'piFun.R' 'vif.R' 'makePiFun.R'
'distsampOpen.R'
'distsampOpen.R' 'multmixOpen.R'
LinkingTo: Rcpp, RcppArmadillo
SystemRequirements: GNU make
URL: http://groups.google.com/group/unmarked,
Expand Down
7 changes: 5 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import(lattice, methods, parallel, Rcpp, Matrix)
# Fitting functions
export(occu, occuFP, occuRN, pcount, pcountOpen, multinomPois, distsamp,
colext, gmultmix, gdistsamp, gpcount, occuPEN, occuPEN_CV, occuMulti,
occuMS, computeMPLElambda, pcount.spHDS, occuTTD, distsampOpen)
occuMS, computeMPLElambda, pcount.spHDS, occuTTD, distsampOpen,
multmixOpen)
export(removalPiFun, doublePiFun)
export(makeRemPiFun, makeCrPiFun, makeCrPiFunMb, makeCrPiFunMh)

# S4 classes
exportClasses(unmarkedFit, unmarkedFitOccu, unmarkedFitOccuFP, unmarkedFitDS,
unmarkedFitPCount, unmarkedFitMPois, unmarkedFitPCO,
unmarkedFrameDSO, unmarkedFitDSO,
unmarkedFrameMMO, unmarkedFitMMO,
unmarkedFitOccuMulti,
unmarkedFrame, unmarkedFrameOccu, unmarkedFramePCount,
unmarkedFrameMPois, unmarkedFrameDS, unmarkedMultFrame,
Expand All @@ -51,7 +53,8 @@ export(unmarkedEstimate, fitList, mapInfo, unmarkedFrame,
unmarkedFrameOccu, unmarkedFrameOccuFP, unmarkedFramePCount, unmarkedFrameMPois,
unmarkedFrameDS, unmarkedMultFrame, unmarkedFrameGMM,
unmarkedFramePCO, unmarkedFrameGDS, unmarkedFrameGPC, unmarkedFrameOccuMulti,
unmarkedFrameOccuMS, unmarkedFrameOccuTTD, unmarkedFrameDSO)
unmarkedFrameOccuMS, unmarkedFrameOccuTTD, unmarkedFrameDSO,
unmarkedFrameMMO)

# Formatting
export(csvToUMF, formatLong, formatWide, formatMult, formatDistData)
Expand Down
13 changes: 12 additions & 1 deletion R/getDesign.R
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ setMethod("getDesign", "unmarkedFrameOccuMS",
})

# pcountOpen
#setMethod("getDesign", "unmarkedFramePCOorMMO",
setMethod("getDesign", "unmarkedFramePCO",
function(umf, formula, na.rm = TRUE)
{
Expand Down Expand Up @@ -1018,6 +1019,14 @@ setMethod("getDesign", "unmarkedFramePCO",
removed.sites = out$removed.sites, go.dims = go.dims))
})

#Need to do this hacky approach because class union of PCO and MMO doesn't work
#for reasons I don't understand
setMethod("getDesign", "unmarkedFrameMMO",
function(umf, formula, na.rm=TRUE)
{
class(umf)[1] <- "unmarkedFramePCO"
getDesign(umf, formula, na.rm)
})

# need a getDesign for distsampOpen.... not sure how to set this up
# pcountOpenDS
Expand Down Expand Up @@ -1170,6 +1179,7 @@ setMethod("getDesign", "unmarkedFrameDSO",



#setMethod("handleNA", "unmarkedFramePCOorMMO",
setMethod("handleNA", "unmarkedFramePCO",
function(umf, Xlam, Xgam, Xom, Xp, Xiota, Xlam.offset, Xgam.offset,
Xom.offset, Xp.offset, Xiota.offset, delta)
Expand All @@ -1181,12 +1191,13 @@ setMethod("handleNA", "unmarkedFramePCO",
T <- umf@numPrimary
y <- getY(umf)
J <- ncol(y) / T
R <- obsNum(umf)

Xlam.long <- Xlam[rep(1:M, each = J*T),]
Xlam.long.na <- is.na(Xlam.long)

long.na <- function(x) {
x.mat <- matrix(x, M, J*T, byrow = TRUE)
x.mat <- matrix(x, M, R, byrow = TRUE)
x.mat <- is.na(x.mat)
x.mat <- x.mat %*% obsToY
x.long <- as.vector(t(x.mat))
Expand Down
264 changes: 264 additions & 0 deletions R/multmixOpen.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
multmixOpen <- function(lambdaformula, gammaformula, omegaformula, pformula,
data, mixture=c("P", "NB", "ZIP"), K,
dynamics=c("constant", "autoreg", "notrend", "trend", "ricker", "gompertz"),
fix=c("none", "gamma", "omega"), immigration=FALSE, iotaformula = ~1,
starts, method="BFGS", se=TRUE, ...)
{

#Check data source
if(!is(data, "unmarkedFrameMMO"))
stop("Data is not of class unmarkedFrameMMO.")

piFun <- data@piFun

#Check state model arguments
mixture <- match.arg(mixture)
dynamics <- match.arg(dynamics)
if(identical(dynamics, "notrend") &
!identical(lambdaformula, omegaformula))
stop("lambdaformula and omegaformula must be identical for notrend model")
fix <- match.arg(fix)

formlist <- mget(c("lambdaformula", "gammaformula", "omegaformula",
"pformula", "iotaformula"))
formula <- as.formula(paste(unlist(formlist), collapse=" "))

D <- getDesign(data, formula)
y <- D$y

M <- nrow(y)
T <- data@numPrimary
J <- ncol(getY(data)) / T

y <- array(y, c(M, J, T))
yt <- apply(y, c(1,3), function(x) {
if(all(is.na(x)))
return(NA)
else return(sum(x, na.rm=TRUE))
})

ytna <- apply(is.na(y), c(1,3), all)
ytna <- matrix(ytna, nrow=M)
ytna[] <- as.integer(ytna)

first <- apply(!ytna, 1, function(x) min(which(x)))
last <- apply(!ytna, 1, function(x) max(which(x)))
first1 <- which(first==1)[1]

Xlam.offset <- D$Xlam.offset
Xgam.offset <- D$Xgam.offset
Xom.offset <- D$Xom.offset
Xp.offset <- D$Xp.offset
Xiota.offset <- D$Xiota.offset
if(is.null(Xlam.offset)) Xlam.offset <- rep(0, M)
if(is.null(Xgam.offset)) Xgam.offset <- rep(0, M*(T-1))
if(is.null(Xom.offset)) Xom.offset <- rep(0, M*(T-1))
if(is.null(Xp.offset)) Xp.offset <- rep(0, M*T*J)
if(is.null(Xiota.offset)) Xiota.offset <- rep(0, M*(T-1))

#K stuff
if(missing(K)) {
K <- max(y, na.rm=T) + 20
warning("K was not specified and was set to ", K, ".")
}
if(K <= max(y, na.rm = TRUE))
stop("specified K is too small. Try a value larger than any observation")
k <- 0:K
lk <- length(k)
#Some k-related indices to avoid repeated calculations in likelihood
lfac.k <- lgamma(k+1)
kmyt <- array(0, c(lk, T, M))
lfac.kmyt <- array(0, c(M, T, lk))
fin <- array(NA, c(M, T, lk)) #Indicator if given k is possible given y
for(i in 1:M) {
for(t in 1:T) {
fin[i,t,] <- k - yt[i,t] >= 0
if(sum(ytna[i,t])==0) {
kmyt[,t,i] <- k - yt[i,t]
lfac.kmyt[i,t, ] <- lgamma(kmyt[,t,i] + 1)
}
}
}

lamParms <- colnames(D$Xlam)
gamParms <- colnames(D$Xgam)
omParms <- colnames(D$Xom)
detParms <- colnames(D$Xp)
nAP <- ncol(D$Xlam)
nGP <- ncol(D$Xgam)
nOP <- ncol(D$Xom)
nDP <- ncol(D$Xp)

nIP <- ifelse(immigration, ncol(D$Xiota), 0)
iotaParms <- character(0)
if(immigration) iotaParms <- colnames(D$Xiota)

if(identical(fix, "gamma")) {
if(!identical(dynamics, "constant"))
stop("dynamics must be constant when fixing gamma or omega")
if(nGP > 1){
stop("gamma covariates not allowed when fix==gamma")
}else {
nGP <- 0
gamParms <- character(0)
}
} else if(identical(dynamics, "notrend")) {
if(nGP > 1){
stop("gamma covariates not allowed when dyamics==notrend")
} else {
nGP <- 0
gamParms <- character(0)
}
}

if(identical(fix, "omega")) {
if(!identical(dynamics, "constant"))
stop("dynamics must be constant when fixing gamma or omega")
if(nOP > 1)
stop("omega covariates not allowed when fix==omega")
else {
nOP <- 0
omParms <- character(0)
}
} else if(identical(dynamics, "trend")) {
if(nOP > 1)
stop("omega covariates not allowed when dynamics='trend'")
else {
nOP <- 0
omParms <- character(0)
}
}

nP <- nAP + nGP + nOP + nDP + nIP + (mixture!="P")
if(!missing(starts) && length(starts) != nP)
stop(paste("The number of starting values should be", nP))

nbParm <- character(0)
if(identical(mixture,"NB"))
nbParm <- "alpha"
else if(identical(mixture, "ZIP"))
nbParm <- "psi"

paramNames <- c(lamParms, gamParms, omParms, detParms,
iotaParms, nbParm)

#Create indices, all possible combinations of survivors and recruits,
#finding all unique likelihood transitions
I <- cbind(rep(k, times=lk), rep(k, each=lk))
I1 <- I[I[,1] <= I[,2],]
lik_trans <- .Call("get_lik_trans", I, I1, PACKAGE="unmarked")

beta_ind <- matrix(NA, 6, 2)
beta_ind[1,] <- c(1, nAP) #Abundance
beta_ind[2,] <- c(1, nGP) + nAP #Gamma
beta_ind[3,] <- c(1, nOP) + nAP + nGP #Omega
beta_ind[4,] <- c(1, nDP) + nAP + nGP + nOP #Detection
beta_ind[5,] <- c(1, nIP) + nAP + nGP + nOP + nDP #Iota
beta_ind[6,] <- c(1, 1) + nAP + nGP + nOP + nDP + nIP #2nd abun param

#Adjustments to objects to facilitate use in c++
fin <- fin*1 #convert to numeric
yperm <- aperm(y, c(1,3,2))
yna <- is.na(yperm)*1

nll <- function(parms) {
.Call("nll_multmixOpen",
yperm, yt,
D$Xlam, D$Xgam, D$Xom, D$Xp, D$Xiota,
parms, beta_ind - 1,
Xlam.offset, Xgam.offset, Xom.offset, Xp.offset, Xiota.offset,
ytna, yna,
lk, mixture, first - 1, last - 1, first1 - 1, M, T, J,
D$delta, dynamics, fix, D$go.dims, immigration,
I, I1, lik_trans$Ib, lik_trans$Ip,
piFun, lfac.k, kmyt, lfac.kmyt, fin,
PACKAGE = "unmarked")
}

if(missing(starts)){
starts <- rep(0, nP)
}

fm <- optim(starts, nll, method=method, hessian=se, ...)
ests <- fm$par
names(ests) <- paramNames
covMat <- invertHessian(fm, nP, se)
fmAIC <- 2*fm$value + 2*nP

lamEstimates <- unmarkedEstimate(name = "Abundance", short.name = "lam",
estimates = ests[1:nAP], covMat = as.matrix(covMat[1:nAP,1:nAP]),
invlink = "exp", invlinkGrad = "exp")
estimateList <- unmarkedEstimateList(list(lambda=lamEstimates))

gamName <- switch(dynamics, constant = "gamConst", autoreg = "gamAR",
notrend = "", trend = "gamTrend",
ricker="gamRicker", gompertz = "gamGomp")
if(!(identical(fix, "gamma") | identical(dynamics, "notrend"))){
estimateList@estimates$gamma <- unmarkedEstimate(name =
ifelse(identical(dynamics, "constant") | identical(dynamics, "autoreg"),
"Recruitment", "Growth Rate"), short.name = gamName,
estimates = ests[(nAP+1) : (nAP+nGP)], covMat = as.matrix(covMat[(nAP+1) :
(nAP+nGP), (nAP+1) : (nAP+nGP)]),
invlink = "exp", invlinkGrad = "exp")
}

if(!(identical(fix, "omega") | identical(dynamics, "trend"))) {
if(identical(dynamics, "constant") | identical(dynamics, "autoreg") |
identical(dynamics, "notrend")){
estimateList@estimates$omega <- unmarkedEstimate( name="Apparent Survival",
short.name = "omega", estimates = ests[(nAP+nGP+1) :(nAP+nGP+nOP)],
covMat = as.matrix(covMat[(nAP+nGP+1) : (nAP+nGP+nOP),
(nAP+nGP+1) : (nAP+nGP+nOP)]),
invlink = "logistic", invlinkGrad = "logistic.grad")
} else if(identical(dynamics, "ricker")){
estimateList@estimates$omega <- unmarkedEstimate(name="Carrying Capacity",
short.name = "omCarCap", estimates = ests[(nAP+nGP+1) :(nAP+nGP+nOP)],
covMat = as.matrix(covMat[(nAP+nGP+1) : (nAP+nGP+nOP),
(nAP+nGP+1) : (nAP+nGP+nOP)]),
invlink = "exp", invlinkGrad = "exp")
} else{
estimateList@estimates$omega <- unmarkedEstimate(name="Carrying Capacity",
short.name = "omCarCap", estimates = ests[(nAP+nGP+1) :(nAP+nGP+nOP)],
covMat = as.matrix(covMat[(nAP+nGP+1) : (nAP+nGP+nOP),
(nAP+nGP+1) : (nAP+nGP+nOP)]),
invlink = "exp", invlinkGrad = "exp")
}
}

estimateList@estimates$det <- unmarkedEstimate(
name = "Detection", short.name = "p",
estimates = ests[(nAP+nGP+nOP+1) : (nAP+nGP+nOP+nDP)],
covMat = as.matrix(covMat[(nAP+nGP+nOP+1) : (nAP+nGP+nOP+nDP),
(nAP+nGP+nOP+1) : (nAP+nGP+nOP+nDP)]),
invlink = "logistic", invlinkGrad = "logistic.grad")

if(immigration) {
estimateList@estimates$iota <- unmarkedEstimate(
name="Immigration", short.name = "iota",
estimates = ests[(nAP+nGP+nOP+nDP+1) :(nAP+nGP+nOP+nDP+nIP)],
covMat = as.matrix(covMat[(nAP+nGP+nOP+nDP+1) : (nAP+nGP+nOP+nDP+nIP),
(nAP+nGP+nOP+nDP+1) : (nAP+nGP+nOP+nDP+nIP)]),
invlink = "exp", invlinkGrad = "exp")
}

if(identical(mixture, "NB")) {
estimateList@estimates$alpha <- unmarkedEstimate(name = "Dispersion",
short.name = "alpha", estimates = ests[nP],
covMat = as.matrix(covMat[nP, nP]), invlink = "exp",
invlinkGrad = "exp")
}
if(identical(mixture, "ZIP")) {
estimateList@estimates$psi <- unmarkedEstimate(name = "Zero-inflation",
short.name = "psi", estimates = ests[nP],
covMat = as.matrix(covMat[nP, nP]), invlink = "logistic",
invlinkGrad = "logistic.grad")
}

umfit <- new("unmarkedFitMMO", fitType = "multmixOpen",
call = match.call(), formula = formula, formlist = formlist, data = data,
sitesRemoved=D$removed.sites, estimates = estimateList, AIC = fmAIC,
opt = fm, negLogLike = fm$value, nllFun = nll, K = K, mixture = mixture,
dynamics = dynamics, fix = fix, immigration=immigration)

return(umfit)
}
23 changes: 20 additions & 3 deletions R/ranef.R
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,9 @@ setMethod("ranef", "unmarkedFitOccuTTD",
})


setMethod("ranef", "unmarkedFitDSO",
function(object, ...)
{
#Common function for DSO and MMO
postMultinomOpen <- function(object){

dyn <- object@dynamics
formlist <- object@formlist
formula <- as.formula(paste(unlist(formlist), collapse=" "))
Expand Down Expand Up @@ -974,6 +974,23 @@ setMethod("ranef", "unmarkedFitDSO",
post[i,,t] <- g / sum(g)
}
}
post

}


setMethod("ranef", "unmarkedFitDSO",
function(object, ...)
{
post <- postMultinomOpen(object)
new("unmarkedRanef", post=post)
})


setMethod("ranef", "unmarkedFitMMO",
function(object, ...)
{
post <- postMultinomOpen(object)
new("unmarkedRanef", post=post)
})

Expand Down
Loading

0 comments on commit 261fbf2

Please sign in to comment.