#' Fit the Multinomial-Poisson Mixture Model
#'
#' This function fits the multinomial-Poisson mixture model, useful for
#' data collected via survey methods such as removal or double observer sampling.
#'
#' @param formula Double right-hand side formula describing covariates of
#' detection and abundance in that order
#' @param data A \code{\link[unmarked]{unmarkedFrameMPois}} object
#' @param prior_intercept_state Prior distribution for the intercept of the
#' state (abundance) model; see \code{?priors} for options
#' @param prior_coef_state Prior distribution for the regression coefficients of
#' the state model
#' @param prior_intercept_det Prior distribution for the intercept of the
#' detection probability model
#' @param prior_coef_det Prior distribution for the regression coefficients of
#' the detection model
#' @param prior_sigma Prior distribution on random effect standard deviations
#' @param log_lik If \code{TRUE}, Stan will save pointwise log-likelihood values
#' in the output. This can greatly increase the size of the model. If
#' \code{FALSE}, the values are calculated post-hoc from the posteriors
#' @param ... Arguments passed to the \code{\link[rstan]{stan}} call, such as
#' number of chains \code{chains} or iterations \code{iter}
#'
#' @return \code{ubmsFitMultinomPois} object describing the model fit.
#'
#' @examples
#' \donttest{
#' data(ovendata)
#' ovenFrame <- unmarkedFrameMPois(ovendata.list$data,
#' siteCovs=ovendata.list$covariates,
#' type="removal")
#'
#' oven_fit <- stan_multinomPois(~1~scale(ufc), ovenFrame, chains=3, iter=300)
#' }
#'
#' @seealso \code{\link[unmarked]{multinomPois}}, \code{\link[unmarked]{unmarkedFrameMPois}}
#' @export
stan_multinomPois <- function(formula,
data,
prior_intercept_state = normal(0, 5),
prior_coef_state = normal(0, 2.5),
prior_intercept_det = logistic(0, 1),
prior_coef_det = logistic(0, 1),
prior_sigma = gamma(1, 1),
log_lik = TRUE,
...){
forms <- split_formula(formula)
umf <- process_umf(data)
pifun_type <- get_pifun_type(umf)
if(has_spatial(forms)){
split_umf <- extract_missing_sites(umf)
umf <- split_umf$umf
state <- ubmsSubmodelSpatial("Abundance", "state", siteCovs(umf), forms[[2]],
"exp", prior_intercept_state, prior_coef_state,
prior_sigma,
split_umf$sites_augment, split_umf$data_aug)
} else {
state <- ubmsSubmodel("Abundance", "state", siteCovs(umf), forms[[2]], "exp",
prior_intercept_state, prior_coef_state, prior_sigma)
}
response <- ubmsResponseMultinomPois(getY(umf), pifun_type, "P")
det <- ubmsSubmodel("Detection", "det", obsCovs(umf), forms[[1]], "plogis",
prior_intercept_det, prior_coef_det, prior_sigma)
submodels <- ubmsSubmodelList(state, det)
ubmsFit("multinomPois", match.call(), data, response, submodels, log_lik, ...)
}
get_pifun_type <- function(umf){
input <- umf@piFun
if(! input %in% c("doublePiFun","removalPiFun")){
stop("Custom pi functions not supported")
}
switch(input, doublePiFun = {"double"}, removalPiFun = {"removal"})
}
#Output object-----------------------------------------------------------------
#' @include fit.R
setClass("ubmsFitMultinomPois", contains = "ubmsFitAbun")
#Response class----------------------------------------------------------------
#' @include response.R
setClass("ubmsResponseMultinomPois", contains="ubmsResponse")
ubmsResponseMultinomPois <- function(y, y_dist, z_dist, max_primary = 1, K=NULL){
stopifnot(inherits(y, "matrix"))
out <- new("ubmsResponseMultinomPois", y = y, y_dist= y_dist, z_dist = z_dist,
max_primary = max_primary, max_obs = get_max_obs(y, max_primary))
out@missing <- is.na(as_vector(out))
out@K <- get_K(out, K)
out
}
#Bespoke find_missing method for multinomPois models
#Special situation with double observer sampling
#Also remove site if any observation is missing
#' @include missing.R
setMethod("find_missing", "ubmsResponseMultinomPois",
function(object, submodels, ...){
type <- object@y_dist
y <- as_vector(object)
M <- ncol(t(object))
sc <- expand_model_matrix(submodels@submodels$state, object)
if(type == "removal"){
oc <- expand_model_matrix(submodels@submodels$det, object)
} else if (type == "double"){
oc <- model.matrix(submodels@submodels$det)
oc <- oc[rep(1:nrow(oc), rep(c(1,2), M)),]
}
comb <- cbind(y, sc, oc)
miss <- apply(comb, 1, function(x) any(is.na(x)))
miss <- matrix(miss, nrow=nrow(t(object)))
remove_sites <- apply(miss, 2, function(x) any(x))
rep(remove_sites, each=object@max_obs)
})
setMethod("update_missing", c("ubmsSubmodelList", "ubmsResponseMultinomPois"),
function(object, object2){
response <- object2
is_na <- find_missing(response, object)
na_mat <- matrix(is_na, nrow=response@max_obs)
object@submodels$state@missing <- apply(na_mat, 2, any)
if(response@y_dist == "double"){
obs_miss <- as.vector(na_mat[-3,])
} else if(response@y_dist == "removal"){
obs_miss <- as.vector(na_mat)
}
stopifnot(length(obs_miss) == length(object@submodels$det@missing))
object@submodels$det@missing <- obs_miss
object
})
#Methods to simulate posterior predictive distributions------------------------
#' @include posterior_predict.R
setMethod("sim_z", "ubmsFitMultinomPois", function(object, samples, re.form, K=NULL, ...){
resp <- object@response
y <- resp@y
lam_post <- t(sim_lp(object, "state", transform=TRUE, newdata=NULL,
re.form=re.form, samples=samples))
lam_post[object["state"]@missing] <- NA
p_post <- get_pi_for_multinom(object, samples)
K <- object@response@K
Kmin <- apply(y, 1, function(x) ifelse(all(is.na(x)), NA, max(x, na.rm=TRUE)))
kvals <- 0:K
t(simz_multinom(y, lam_post, p_post, K, Kmin, kvals))
})
setMethod("sim_y", "ubmsFitMultinomPois",
function(object, samples, re.form, z=NULL, K=NULL, ...){
nsamp <- length(samples)
M <- nrow(object@response@y)
J <- object@response@max_obs
z <- process_z(object, samples, re.form, z)
p <- get_pi_for_multinom(object, samples)
out <- array(NA, c(J, M, nsamp))
for (i in 1:nsamp){
for (m in 1:M){
if(is.na(z[m,i])) next
out[,m,i] <- stats::rmultinom(n=1, size=z[m,i], prob=p[m,,i])[1:J]
}
}
matrix(out, nrow=nsamp, byrow=TRUE)
})
get_pi_for_multinom <- function(object, samples){
resp <- object@response
nsamp <- length(samples)
M <- nrow(object@response@y)
J <- resp@max_obs
pi_fun <- switch(resp@y_dist, double = unmarked::doublePiFun,
removal = unmarked::removalPiFun)
p_raw <- t(sim_lp(object, "det", transform=TRUE, newdata=NULL,
re.form=NULL, samples=samples))
p_raw[object["det"]@missing] <- NA
p_raw <- array(p_raw, c(nrow(p_raw) / M, M, nsamp))
p_raw <- aperm(p_raw, c(2,1,3))
p_post <- array(NA, c(M, J+1, nsamp))
for (i in 1:nsamp){
p_post[,1:J,i] <- pi_fun(p_raw[,,i])
p_post[,J+1,i] <- 1 - rowSums(p_post[,1:J,i])
}
p_post
}
#Get detection probability-----------------------------------------------------
#' @include posterior_linpred.R
setMethod("sim_p", "ubmsFitMultinomPois", function(object, samples, ...){
J <- object@response@max_obs
p_array <- get_pi_for_multinom(object, samples)[,1:J,]
p_array <- aperm(p_array, c(2,1,3))
p_mat <- matrix(p_array, ncol=length(samples))
t(p_mat)
})
#Method for fitted values------------------------------------------------------
setMethod("sim_fitted", "ubmsFitMultinomPois", function(object, submodel, samples, ...){
stopifnot(submodel %in% c("state", "det"))
if(submodel == "state"){
lp <- sim_lp(object, submodel, transform=TRUE, newdata=NULL,
samples=samples, re.form=NULL)
return(lp)
}
p <- sim_p(object, samples=samples)
J <- object@response@max_obs
z <- sim_z(object, samples, re.form=NULL)
z <- z[, rep(1:ncol(z), each=J)]
out <- z * p
out
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.