Skip to contents

Introduction

This vignette provides worked examples and explanations for fitting univariate and multivariate generalized linear mixed models in the spAbundance R package. We will provide step by step examples on how to fit the following models:

  1. Univariate GLMM using abund().
  2. Spatial univariate GLMM using spAbund().
  3. Multivariate GLMM using msAbund().
  4. Multivariate GLMM with residual correlations using lfMsAbund().
  5. Spatial multivariate GLMM with residual correlations using sfMsAbund().

In this vignette we are only describing spAbundance functionality to fit generalized linear (mixed) models (GLMMs), with separate vignettes on fitting hierarchical distance sampling models and N-mixture models. We fit all models in a Bayesian framework using custom Markov chain Monte Carlo (MCMC) samplers written in C/C++ and called through R’s foreign language interface. Here we will provide a brief description of each model, with full statistical details provided in a separate vignette. As with all model types in spAbundance, we will show how to perform posterior predictive checks as a Goodness of Fit assessment, model comparison/selection using the Widely Applicable Information Criterion (WAIC), and out-of-sample predictions using standard R helper functions (e.g., predict()). Note that syntax of GLMMs in spAbundance closely follows syntax for fitting occupancy models in spOccupancy (Doser et al. 2022), and that this vignette closely follows the documentation on the spOccupancy website.

Note that when we discuss GLMMs, we use the terms “univariate” and “multivariate” instead of “single-species” and “multi-species” as we do when discussing N-mixture models, hierarchical distance sampling models, and occupancy models. We use these potentially less-straightforward terms to highlight the fact that the GLMM functionality in spAbundance is not restricted to working only with data on counts of “species”. Rather, the GLMM functionality in spAbundance can be used to model any sort of response that you could imagine fitting in a GLMM. Despite this, we will often use the term “species” when referring to the different response variables that we can model using the GLMM functionality in spAbundance, since modeling patterns in abundance is the primary purpose of the package.

To get started, we load the spAbundance package, as well as the coda package, which we will use for some MCMC summary and diagnostics. We will also use the stars and ggplot2 packages to create some basic plots of our results. We then set a seed so you can reproduce the same results as we do.

Example data set: Six warbler species from the Breeding Bird Survey

As an example data set throughout this vignette, we will use count data from the North American Breeding Bird Survey collected in 2018 in Pennsylvania, USA (Pardieck et al. 2020). Briefly, these data consist of the total number of individuals for six bird species (American Redstart, Blackburnian Warbler, Black-throated Blue Warbler, Black-throated Green Warbler, Hooded Warbler, Magnolia Warbler) at 95 routes (about 40km long) across Pennsylvania. Additional details on the data set can be found on the USGS Science Base website. The data are included as part of the spAbundance package and are loaded with data(bbsData). See the manual page using help(bbsData) for more information.

# Load the data set.
data(bbsData)
# Get an overview of what's in the data
str(bbsData)
List of 3
 $ y     : num [1:6, 1:95] 1 0 0 0 0 0 3 0 1 3 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:6] "AMRE" "BLBW" "BTBW" "BTNW" ...
  .. ..$ : NULL
 $ covs  :'data.frame': 95 obs. of  8 variables:
  ..$ bio2  : num [1:95] 11.6 12.1 10.4 10.4 11.7 ...
  ..$ bio8  : num [1:95] 20.2 17.8 21.4 21 18.4 ...
  ..$ bio18 : num [1:95] 473 395 422 361 378 ...
  ..$ forest: num [1:95] 0.485 0.959 0.717 0.491 0.867 ...
  ..$ devel : num [1:95] 0.01116 0.00159 0.00319 0.01275 0.00239 ...
  ..$ day   : num [1:95] 147 148 147 148 149 148 149 150 153 154 ...
  ..$ tod   : num [1:95] 534 513 508 518 513 511 516 513 510 505 ...
  ..$ obs   : num [1:95] 51 32 12 10 32 33 15 32 11 32 ...
 $ coords: num [1:95, 1:2] 1319 1395 1559 1488 1386 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "X" "Y"

The object bbsData is a list that is structured in the format needed for multivariate GLMMs in spAbundance. Specifically, bbsData is a list comprised of the count data for the six species (y), covariates (covs), and the spatial coordinates for each site (coords). Note the coordinates are only required for spatailly-explicit GLMMs. The matrix y consists of the count data for all 6 species in the data set, where the rows correspond to species and the columns correspond to sites. For single-species GLMMs, we will only use data for one species (Hooded Warbler; HOWA), so we next subset the bbsData list to only include data from HOWA in a new object data.HOWA.

sp.names <- dimnames(bbsData$y)[[1]]
data.HOWA <- bbsData
data.HOWA$y <- data.HOWA$y[which(sp.names == "HOWA"), ]
# Observed number of HOWA at each site
data.HOWA$y
 [1]  0  4 14  1  9  0 11  4  1  6  5  3  4  3  2 26  0 16 12 14  0  3  0  1  1
[26]  5  2  3  4  1  0  2  0  3  1  0  4  2  6  0  0  0  0  0  6  1  0 11  4  0
[51]  0  0  0  0  0  0  4  0  0  0 13  0  0  0  3  0  0  2 11  0  0  4 25  0  0
[76]  0  1  0  5  1  6  0  0  0  0  0  1  1  2  1  1  3  0  2  0

We see that HOWA appears to be quite common across the 95 sites, but that there is clear variation in the counts across the state.

Univariate GLMMs

Let \(y_j\) denote the observed count of a species of interest at site \(j = 1, \dots, J\). We model \(y_j\) according to

\[\begin{equation} y_j \sim f(\mu_j, \cdot), \end{equation}\]

where \(f()\) denotes some probability distribution with mean \(\mu_j\). The \(\cdot\) represents additional dispersion parameter(s) that are only relevant for certain distributions. In spAbundance, we allow for \(f()\) to be a Poisson distribution, a negative binomial (NB) distribution, or a Gaussian (normal) distribution. The Poisson distribution does not have any additional parameters. The negative binomial distribution has an additional positive dispersion parameter \(\kappa\), which controls the amount of overdispersion in the count data. Smaller values of \(\kappa\) indicate overdispersion in the count data, while higher values indicate minimal overdispersion in the counts relative to the Poisson distribution. Note that as \(\kappa \rightarrow \infty\), a NB model “reverts” back to the simpler Poisson model. The Gaussian distribution has a variance parameter \(\tau^2\) that controls the amount of variation in the observed data around the mean \(\mu_j\).

Following the classic GLM framework, we allow for variation in the mean \(\mu_j\) through the use of a link function \(g()\) following

\[\begin{equation} g(\mu_j) = \boldsymbol{x}_j^\top\boldsymbol{\beta}, \end{equation}\]

where \(\boldsymbol{\beta}\) is a vector of regression coefficients for a set of covariates \(\boldsymbol{x}_j\) (including an intercept). When working with positive integer counts and using the Poisson or negative binomial distributions, we use a log link function. For Gaussian data, we use the identity link function, such that the right hand side of the previous equation simplifies to \(\mu_j\) and covariates are directly related to the mean. Note that while not shown, unstructured random intercepts and slopes can be included in the equation for expected abundance. This may for instance be required for accommodating some sorts of “blocks”, such as when sites are nested in a number of different regions.

To complete the Bayesian specification of the model, we assign Gaussian priors for the regression coefficients (\(\boldsymbol{\beta}\)), a uniform prior for the negative binomial dispersion parameter \(\kappa\) (when applicable), and an inverse-Gamma prior for the Gaussian variance parameter \(\tau^2\) (when applicable).

Fitting univariate GLMMs with abund()

The abund() function fits univariate abundance models. abund has the following arguments.

abund(formula, data, inits, priors, tuning,
      n.batch, batch.length, accept.rate = 0.43, family = 'Poisson',
      n.omp.threads = 1, verbose = TRUE,
      n.report = 100, n.burn = round(.10 * n.batch * batch.length), n.thin = 1, 
      n.chains = 1, save.fitted = TRUE, ...)

The first argument formula uses standard R model syntax to denote the covariates to be included in the model. Only the right hand side of the formula is included. Random intercepts and slopes can be included in the model using lme4 syntax (Bates et al. 2015). For example, to include a random intercept for different observers in the model to account for observational variability, we would include (1 | observer) in formula, where observer indicates the specific observer for each data point. The names of variables given in the formulas should correspond to those found in data, which is a list consisting of the following tags: y (count data) and covs (covariates). y is the vector of count data with length equal to the number of sites in the data set and covs is a matrix or data frame with site-specific covariate values. Note the tag offset can also be specified to include an offset in the model when using a negative binomial or Poisson distribution.

The data.HOWA list is already in the required format for use with the abund() function. Here we will model abundance as a function of three “bioclim” bioclimatic variables and the proportion of forest and developed land within 5km of the route starting location. We will also include a few variables that we believe may relate to observational variability (e.g., imperfect detection) in the count data. Including such variables in a GLMM is a common approach for modeling relative abundance, particularly when using BBS data (Link and Sauer 2002). Here we include linear and quadratic effects of the day of year, a linear effect of time of day, and a random effect of observer, all of which we think may influence relative abundance. We standardize all continuous covariates by using the scale() function in our model specification (note that standardizing continuous covariates is highly recommended as it helps aid convergence of the underlying MCMC algorithms):

howa.formula <- ~ scale(bio2) + scale(bio8) + scale(bio18) + scale(forest) + 
                  scale(devel) + scale(day) + I(scale(day)^2) + scale(tod) + 
                  (1 | obs)

The family argument is used to specify the specific family we will use to model the data. Valid options are Poisson, NB (negative binomial), and Gaussian. Here we are working with count data, and so both the Poisson and negative binomial distributions make sense. We will start working with a Poisson distribution, but later we will compare this to a negative binomial distribution to determine the amount of overdispersion in the data.

howa.family <- 'Poisson'

Next, we specify the initial values for the MCMC sampler in inits. abund() (and all other spAbundance model fitting functions) will set initial values by default, but here we will do this explicitly, since in more complicated cases setting initial values close to the presumed solutions can be vital for success of an MCMC-based analysis (for instance, this is the case when fitting distance sampling models in spAbundance). However, for all models described in this vignette (in particular the non-spatial models), choice of the initial values is largely inconsequential, with the exception being that specifying initial values close to the presumed solutions can decrease the amount of samples you need to run to arrive at convergence of the MCMC chains. Thus, when first running a model in spAbundance, we recommend fitting the model using the default initial values that spAbundance provides. The initial values that spAbundance chooses will be reported to the screen when setting verbose = TRUE. After running the model for a reasonable period, if you find the chains are taking a long time to reach convergence, you then may wish to set the initial values to the mean estimates of the parameters from the initial model fit, as this will likely help reduce the amount of time you need to run the model.

The default initial values for regression coefficients (including the intercepts) are random values from a standard normal distribution. When fitting a GLMM with a negative binomial distribution, the initial value for the overdispersion parameter is drawn from the prior distribution. When using a Gaussian distribution, the initial value for the variance parameter is a random value between 0.5 and 10. Initial values are specified in a list with the following tags: beta (intercept and regression coefficients), kappa (negative binomial overdispersion parameter), tau.sq (Gaussian variance parameter). For the regression coefficients beta, the initial values are passed either as a vector of length equal to the number of estimated parameters (including an intercept, and in the order specified in the model formula), or as a single value if setting the same initial value for all parameters (including the intercept). Below we take the latter approach. For the negative binomial overdispersion parameter and Gaussian variance parameter, the initial value is simply a single numeric value. For any random effects that are included in the model, we can also specify the initial values for the random effect variances (sigma.sq.mu). By default, these will be drawn as random values between 0.5 and 10. Here we specify the initial value for the random effect variance to 1.

inits <- list(beta = 0, kappa = 1, sigma.sq.mu = 1)

We next specify the priors for the regression coefficients, as well as the negative binomial overdispersion parameter. We assume normal priors for regression coefficients. These priors are specified in a list with tags beta.normal (including intercepts). Each list element is then itself a list, with the first element of the list consisting of the hypermeans for each coefficient and the second element of the list consisting of the hypervariances for each coefficient. Alternatively, the hypermeans and hypervariances can be specified as a single value if the same prior is used for all regression coefficients. By default, spAbundance will set the hypermeans to 0 and the hypervariances to 100. For the negative binomial overdispersion parameter, we will use a uniform prior. This prior is specified as a tag in the prior list called kappa.unif, which should be a vector with two values indicating the lower and upper bound of the uniform distribution. The default prior is to set the lower bound to 0 and the upper bound to 100. Recall that lower values of kappa indicate substantial overdispersion and high values of kappa indicate minimal overdispersion. If there is little support for overdispersion when fitting a negative binomial model, we will likely see the estimates of kappa be close to the upper bound of the uniform prior distribution. For the default prior distribution, if the estimates of kappa are very close to 100, this indicates little support for overdispersion in the model, and we can likely switch to using a Poisson distribution (which would also likely be favored by model comparison approaches). For models with random effects, we can also specify the prior for the random effect variance parameter (sigma.sq.mu). We assume inverse-Gamma priors for these variance parameters and specify them with the tags sigma.sq.mu.ig. These priors are set as a list with two components, where the first element is the shape parameter and the second element is the scale parameter. The shape and scale parameters can be specified as a single value or as vectors with length equal to the number of random effects included in the model. The default prior distribution for random effect variances is 0.1 for both the shape and scale parameters. When fitting GLMMs with a Gaussian distribution, the tag tau.sq.ig is used to specify the inverse-Gamma prior for the variance parameter of the Gaussian distribution. The prior is specified as a vector of length two, with the first element being the inverse-Gamma shape parameter and second element being the inverse-Gamma scale parameter. By default, these values are both set to 0.01. Below we use default priors for all parameters, but specify them explicitly for clarity.

priors <- list(beta.normal = list(mean = 0, var = 100),
               kappa.unif = c(0, 100),
               sigma.sq.mu.ig = list(0.1, 0.1))

The next four arguments (tuning, n.batch, batch.length, and accept.rate) are all related to the specific type of MCMC sampler we use when we fit GLMMs in spAbundance. Most parameters in GLMMs are estimated using a Metropolis-Hastings step, which can often be slow and inefficient, leading to slow mixing and convergence of the MCMC chains. To try and mitigate the slow mixing and convergence issues, we update all parameters in GLMMs using an algorithm called an adaptive Metropolis-Hastings algorithm (see Roberts and Rosenthal (2009) for more details on this algorithm). In this approach, we break up the total number of MCMC samples into a set of “batches”, where each batch has a specific number of MCMC samples. Thus, we must specify the total number of batches (n.batch) as well as the number of MCMC samples each batch contains (batch.length) when specifying the function arguments. The total number of MCMC samples is n.batch * batch.length. Typically, we set batch.length = 25 and then play around with n.batch until convergence of all model parameters is reached. We generally recommend setting batch.length = 25, but in certain situations this can be increased to a larger number of samples (e.g., 100), which can result in moderate decreases in run time. Here we set n.batch = 800 for a total of 20,000 MCMC samples for each MCMC chain we run.

batch.length <- 25
n.batch <- 800
# Total number of MCMC samples per chain
batch.length * n.batch
[1] 20000

Importantly, we also need to specify a target acceptance rate and initial tuning parameters for the regression coefficients (and the negative binomial overdispersion parameter and any latent random effects if applicable). These are both features of the adaptive algorithm we use to sample these parameters. In this adaptive Metropolis-Hastings algorithm, we propose new values for the parameters from some proposal distribution, compare them to our previous values, and use a statistical algorithm to determine if we should accept the new proposed value or keep the old one. The accept.rate argument specifies the ideal proportion of times we will accept the newly proposed values for these parameters. Roberts and Rosenthal (2009) show that if we accept new values around 43% of the time, then this will lead to optimal mixing and convergence of the MCMC chains. Following these recommendations, we should strive for an algorithm that accepts new values about 43% of the time. Thus, we recommend setting accept.rate = 0.43 unless you have a specific reason not to (this is the default value). The values specified in the tuning argument help control the initial values we will propose for the abundance/detection coefficients and the negative binomial overdispersion parameter. These values are supplied as input in the form of a list with tags beta and kappa. The initial tuning value can be any value greater than 0, but we generally recommend starting the value out around 0.5. These tuning values can also be thought of as tuning “variances”, as it is these values that control the variance of the distribution we use to generate newly proposed values for the parameters we are trying to estimate with our MCMC algorithm. In short, the new values that we propose for the parameters beta and kappa come from a normal distribution with mean equal to the current value for the given parameter and the variance equal to the tuning parameter (with a transformation for kappa since it can only take positive values). Thus, the smaller this tuning parameter/variance is, the closer our proposed values will be to the current value, and vise versa for large values of the tuning parameter. The “ideal” value of the tuning variance will depend on the data set, the parameter, and how much uncertainty there is in the estimate of the parameter. This initial tuning value that we supply is the first tuning variance that will be used for the given parameter, and our adaptive algorithm will adjust this tuning parameter after each batch to yield acceptance rates of newly proposed values that are close to our target acceptance rate that we specified in the accept.rate argument. Information on the acceptance rates for a few of the parameters in your model will be displayed when setting verbose = TRUE. After some initial runs of the model, if you notice the final acceptance rate is much larger or smaller than the target acceptance rate (accept.rate), you can then change the initial tuning value to get closer to the target rate. While use of this algorithm requires us to specify more arguments than if we didn’t “adaptively tune” our proposal variances, this leads to much shorter run times compared to a more simplistic approach where we do not have an “adaptive” sampling approach, and it should thus save us time in the long haul when waiting for these models to run. For our example here, we set the initial tuning values to 0.5 for beta and kappa. For models with random effects in either the abundance or detection portions of the model, we also need to specify tuning parameters for the latent random effect values (beta.star). We similarly set these to 0.5. Note that for Gaussian GLMMs, we use much more efficient algorithms (Gibbs updates).

tuning <- list(beta = 0.5, kappa = 0.5, beta.star = 0.5)
# accept.rate = 0.43 by default, so we do not specify it.

We also need to specify the length of burn-in (n.burn), the rate at which we want to thin the posterior samples (n.thin), and the number of MCMC chains to run (n.chains). Note that currently spAbundance runs multiple chains sequentially and does not allow chains to be run simultaneously in parallel across multiple threads, which is something we hope to implement in future package development. Instead, we allow for within-chain parallelization using the n.omp.threads argument. We can set n.omp.threads to a number greater than 1 and smaller than the number of threads on the computer you are using. Generally, setting n.omp.threads > 1 will not result in decreased run times for non-spatial models in spAbundance, but can substantially decrease run time when fitting spatial models (Finley, Datta, and Banerjee 2020). Here we set n.omp.threads = 1.

For a simple single-species GLMM, we shouldn’t need too many samples and will only need a moderate amount of burn-in and thinning. We will run the model using three chains to assess convergence using the Gelman-Rubin diagnostic (Rhat; Brooks and Gelman (1998)).

n.burn <- 10000
n.thin <- 10
n.chains <- 3

We are now almost set to run the model. The verbose argument is a logical value indicating whether or not MCMC sampler progress is reported to the screen. If verbose = TRUE, sampler progress is reported to the screen. The argument n.report specifies the interval to report the Metropolis-Hastings sampler acceptance rate. Note that n.report is specified in terms of batches, not the overall number of samples. Below we set n.report = 200, which will result in information on the acceptance rate and tuning parameters every 200th batch (not sample).

We now are set to fit the model.

out <- abund(formula = howa.formula,
             data = data.HOWA,
             inits = inits,
             priors = priors,
             n.batch = n.batch,
             batch.length = batch.length,
             tuning = tuning,
             n.omp.threads = 1,
             n.report = 200,
             family = howa.family,
             verbose = TRUE,
             n.burn = n.burn,
             n.thin = n.thin,
             n.chains = n.chains)
----------------------------------------
    Preparing to run the model
----------------------------------------
----------------------------------------
    Model description
----------------------------------------
Poisson abundance model fit with 95 sites.

Samples per Chain: 20000 (800 batches of length 25)
Burn-in: 10000 
Thinning Rate: 10 
Number of Chains: 3 
Total Posterior Samples: 3000 

Source compiled with OpenMP support and model fit using 1 thread(s).

Adaptive Metropolis with target acceptance rate: 43.0
----------------------------------------
    Chain 1
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     52.0        0.16152
    beta[2]     40.0        0.17150
    beta[3]     56.0        0.15518
    beta[4]     64.0        0.17850
    beta[5]     36.0        0.19728
    beta[6]     20.0        0.14615
    beta[7]     36.0        0.16152
    beta[8]     32.0        0.14042
    beta[9]     44.0        0.31250
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     28.0        0.15518
    beta[2]     40.0        0.18954
    beta[3]     32.0        0.17150
    beta[4]     48.0        0.16152
    beta[5]     40.0        0.18211
    beta[6]     40.0        0.12962
    beta[7]     64.0        0.16478
    beta[8]     32.0        0.14615
    beta[9]     24.0        0.26630
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     32.0        0.15518
    beta[2]     60.0        0.17850
    beta[3]     36.0        0.17497
    beta[4]     60.0        0.14910
    beta[5]     52.0        0.18954
    beta[6]     44.0        0.14910
    beta[7]     36.0        0.17497
    beta[8]     44.0        0.14042
    beta[9]     40.0        0.28847
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 2
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     48.0        0.15211
    beta[2]     52.0        0.17850
    beta[3]     44.0        0.17150
    beta[4]     52.0        0.16152
    beta[5]     24.0        0.19337
    beta[6]     32.0        0.14615
    beta[7]     44.0        0.18579
    beta[8]     52.0        0.12962
    beta[9]     48.0        0.30631
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     44.0        0.14910
    beta[2]     52.0        0.16478
    beta[3]     64.0        0.16152
    beta[4]     40.0        0.16152
    beta[5]     44.0        0.18954
    beta[6]     44.0        0.14910
    beta[7]     48.0        0.18211
    beta[8]     56.0        0.13224
    beta[9]     56.0        0.30631
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     36.0        0.14042
    beta[2]     44.0        0.17497
    beta[3]     44.0        0.16811
    beta[4]     56.0        0.14910
    beta[5]     40.0        0.20533
    beta[6]     36.0        0.15518
    beta[7]     44.0        0.18954
    beta[8]     44.0        0.12962
    beta[9]     40.0        0.27716
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 3
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     48.0        0.15832
    beta[2]     44.0        0.17850
    beta[3]     44.0        0.18211
    beta[4]     40.0        0.14615
    beta[5]     36.0        0.19337
    beta[6]     40.0        0.14615
    beta[7]     36.0        0.17850
    beta[8]     36.0        0.14325
    beta[9]     40.0        0.30025
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     40.0        0.15518
    beta[2]     44.0        0.15518
    beta[3]     44.0        0.16811
    beta[4]     28.0        0.16478
    beta[5]     52.0        0.19337
    beta[6]     56.0        0.14910
    beta[7]     52.0        0.17150
    beta[8]     60.0        0.14042
    beta[9]     32.0        0.25585
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     32.0        0.14325
    beta[2]     40.0        0.16152
    beta[3]     32.0        0.17150
    beta[4]     40.0        0.16811
    beta[5]     28.0        0.18579
    beta[6]     48.0        0.14910
    beta[7]     44.0        0.18954
    beta[8]     48.0        0.14042
    beta[9]     40.0        0.27168
-------------------------------------------------
Batch: 800 of 800, 100.00%

abund() returns a list of class abund with a suite of different objects, many of them being coda::mcmc objects of posterior samples. The “Preparing to run the model” section will print information on default priors or initial values that are used when they are not specified in the function call. Here we specified everything explicitly so no information was reported.

We next use the summary() function on the resulting abund() object for a concise, informative summary of the regression parameters and convergence of the MCMC chains.

summary(out)

Call:
abund(formula = howa.formula, data = data.HOWA, inits = inits, 
    priors = priors, tuning = tuning, n.batch = n.batch, batch.length = batch.length, 
    family = howa.family, n.omp.threads = 1, verbose = TRUE, 
    n.report = 200, n.burn = n.burn, n.thin = n.thin, n.chains = n.chains)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000
Run Time (min): 0.1439

Abundance (log scale): 
                   Mean     SD    2.5%     50%   97.5%   Rhat  ESS
(Intercept)     -0.2307 0.3418 -0.9519 -0.2144  0.3983 1.0223  130
scale(bio2)     -0.2743 0.1144 -0.5063 -0.2754 -0.0534 1.0034 1024
scale(bio8)     -0.0837 0.1597 -0.3917 -0.0871  0.2406 1.0014  530
scale(bio18)     0.0744 0.1259 -0.1719  0.0742  0.3220 1.0187  639
scale(forest)    0.7173 0.1454  0.4320  0.7131  1.0037 1.0043  735
scale(devel)    -0.1723 0.1381 -0.4488 -0.1689  0.0915 1.0106  544
scale(day)      -0.5673 0.1561 -0.8920 -0.5628 -0.2775 1.0073  760
I(scale(day)^2) -0.3732 0.1384 -0.6497 -0.3688 -0.1161 1.0336  652
scale(tod)       1.4681 0.3777  0.8197  1.4307  2.2867 1.0161  315

Abundance Random Effect Variances (log scale): 
                  Mean    SD   2.5%    50%  97.5%   Rhat ESS
(Intercept)-obs 3.0363 1.131 1.4259 2.8583 5.7909 1.0341 267

We see the variable with the largest magnitude effect is time of day with a strong positive effect. Since we believe this variable may relate to the probability of detecting HOWA at a location (or the probability HOWA is singing and thus available for detection), this suggests a larger number of HOWA are counted later in the morning relative to early in the morning. There is also a strong positive relationship with forest cover, suggesting larger HOWA relative abundance in more forested areas.

The model summary also provides information on convergence of the MCMC chains in the form of the Gelman-Rubin diagnostic (Brooks and Gelman 1998) and the effective sample size (ESS) of the posterior samples. Here we find all Rhat values are less than 1.1 and the ESS values are decently large for all parameters.

We can use the plot() function to generate a simple trace plot of the MCMC chains to provide additional confidence in the convergence (or non-convergence) of the model. The plotting functionality for each model type in spAbundance takes three arguments: x (the resulting object from fitting the model), param (the parameter name that you want to display), and density (a logical value indicating whether to also generate a density plot in addition to the traceplot). To see the parameter names available to use with plot() for a given model type, you can look at the manual page for the function, which for models generated from abund() can be accessed with ?plot.abund.

# Regression coefficients
plot(out, param = 'beta', density = FALSE)

Posterior predictive checks

The function ppcAbund() performs a posterior predictive check on all spAbundance model objects as a Goodness-of-Fit (GOF) assessment. The fundamental idea of GoF testing is that a good model should generate data that closely align with the observed data. If there are drastic differences in the true data from the data generated under the model, our model is likely not very useful (Hobbs and Hooten 2015). For details on posterior predictive checks, please see this section in the N-mixture model vignette. Below we perform a posterior predictive check using a Freeman-Tukey test statistic, and summarize it with a Bayesian p-value.

ppc.out <- ppcAbund(out, fit.stat = 'freeman-tukey', group = 0)
summary(ppc.out)

Call:
ppcAbund(object = out, fit.stat = "freeman-tukey", group = 0)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000

Bayesian p-value:  0.001 
Fit statistic:  freeman-tukey 

Here our Bayesian p-value is very close to 0, indicating that the current model does not adequately represent the variability in the observed data. We can further look at a plot of the fitted values versus the trues to get a better sense of how our model performed.

# Extract fitted values
y.rep.samples <- fitted(out)
# Get means of fitted values
y.rep.means <- apply(y.rep.samples, 2, mean)
# Simple plot of True vs. fitted values
plot(data.HOWA$y, y.rep.means, pch = 19, xlab = 'True', ylab = 'Fitted')
abline(0, 1)

Looking at this plot, we see our model actually does a decent job of identifying locations with high relative abundance, but there are a few sites with low observed relative abundance for which the model seems to be overestimating abundance (i.e., points to the left of 5 on the x-axis in the above plot). This is likely what is causing the low Bayesian p-value.

Model selection using WAIC

The function waicAbund() calculates the Widely Applicable Information Criterion as a model selection crtieria. This can be used to compare a series of candidate models and select the best-performing model for final analysis. See this section in the N-mixture model vignette for additional details on how we calculate WAIC in spAbundance.

We first fit a second model that uses a negative binomial distribution for abundance, and then compare the two models using WAIC.

out.nb <- abund(formula = howa.formula,
                data = data.HOWA,
                inits = inits,
                priors = priors,
                n.batch = n.batch,
                batch.length = batch.length,
                tuning = tuning,
                n.omp.threads = 1,
                n.report = 200,
                family = 'NB',
                verbose = FALSE,
                n.burn = n.burn,
                n.thin = n.thin,
                n.chains = n.chains)
# Poisson model
waicAbund(out)
      elpd         pD       WAIC 
-133.44441   52.16574  371.22031 
# NB model
waicAbund(out.nb)
      elpd         pD       WAIC 
-164.09803   22.47421  373.14447 

Here we see the WAIC values are essentially identical, and so we select the simpler model (the Poisson model) as the prefered model.

Prediction

All model objects from a call to spAbundance model-fitting functions can be used with predict() to generate a series of posterior predictive samples at new locations, given the values of all covariates used in the model fitting process. Here we will predict relative abundance of HOWA across the state of Pennsylvania at a 12km resolution. The prediction data are stored in the bbsPredData object, which is available in spAbundance.

data(bbsPredData)
str(bbsPredData)
'data.frame':   816 obs. of  7 variables:
 $ bio2  : num  10.44 10.28 10.42 9.41 10.72 ...
 $ bio8  : num  17.8 17.5 18.3 18.2 17.9 ...
 $ bio18 : num  383 392 341 425 490 ...
 $ forest: num  0.898 0.906 0.665 0.737 0.79 ...
 $ devel : num  0.000797 0.002392 0 0.002392 0 ...
 $ x     : num  1669 1681 1609 1621 1633 ...
 $ y     : num  682 682 670 670 670 ...

The prediction data consist of 816 12km cells in which we will predict HOWA relative abundance. The data frame consists of the spatial coordinates for each cell and the bioclimatic and landcover covariates we used to fit the model. We will set the values of the covariates related to observational variability to their mean value to generate our relative abundance estimates, and also will set the random observer effect to 0 at each prediction location.

Given that we standardized the covariate values when we fit the model, we need to standardize the covariate values for prediction using the exact same values of the mean and standard deviation of the covariate values used to fit the data.

# Center and scale covariates by values used to fit model
bio2.pred <- (bbsPredData$bio2 - mean(data.HOWA$covs$bio2)) / 
              sd(data.HOWA$covs$bio2)
bio8.pred <- (bbsPredData$bio8 - mean(data.HOWA$covs$bio8)) / 
              sd(data.HOWA$covs$bio8)
bio18.pred <- (bbsPredData$bio18 - mean(data.HOWA$covs$bio18)) / 
               sd(data.HOWA$covs$bio18)
forest.pred <- (bbsPredData$forest - mean(data.HOWA$covs$forest)) / 
                sd(data.HOWA$covs$forest)
devel.pred <- (bbsPredData$devel - mean(data.HOWA$covs$devel)) / 
               sd(data.HOWA$covs$devel)
day.pred <- 0
tod.pred <- 0

For abund(), the predict() function takes four arguments:

  1. object: the abund fitted model object.
  2. X.0: a matrix or data frame consisting of the design matrix for the prediction locations (which must include an intercept if our model contained one).
  3. ignore.RE: a logical value indicating whether or not to remove random effects from the predicted values (this is equivalent to setting the random effect value to 0 at each location). By default, this is set to FALSE, and so prediction will include the random effects (if any are specified).

Below we form the design matrix and predict relative abundance across the grid.

X.0 <- cbind(1, bio2.pred, bio8.pred, bio18.pred, forest.pred, 
             devel.pred, day.pred, day.pred^2, tod.pred)
colnames(X.0) <- c('(Intercept)', 'scale(bio2)', 'scale(bio8)', 'scale(bio18)', 
                   'scale(forest)', 'scale(devel)', 'scale(day)', 
                   'I(scale(day)^2)', 'scale(tod)')
out.pred <- predict(out, X.0, ignore.RE = TRUE)
str(out.pred)
List of 3
 $ mu.0.samples: num [1:3000, 1:816, 1] 2.49 2.47 3.49 3.96 4.09 ...
 $ y.0.samples : int [1:3000, 1:816, 1] 2 3 5 2 4 2 4 2 7 4 ...
 $ call        : language predict.abund(object = out, X.0 = X.0, ignore.RE = TRUE)
 - attr(*, "class")= chr "predict.abund"

The resulting object consists of posterior predictive samples for the expected relative abundances (mu.0.samples) and relative abundance values (y.0.samples). The beauty of the Bayesian paradigm, and the MCMC computing machinery, is that these predictions all have fully propagated uncertainty. Below, we produce a map of HOWA relative abundance across the state, as well as a map of the uncertainty, where here we represent uncertainty with the width of the 95% credible interval. We will also plot the coordinates of the actual data locations to show where the data we used to fit the model relate to the overall predictions across the state.

mu.0.quants <- apply(out.pred$mu.0.samples, 2, quantile, c(0.025, 0.5, 0.975))
plot.df <- data.frame(Easting = bbsPredData$x,
                      Northing = bbsPredData$y,
                      mu.0.med = mu.0.quants[2, ],
                      mu.0.ci.width = mu.0.quants[3, ] - mu.0.quants[1, ])
# proj4string for the coordinate reference system
my.crs <- "+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
coords.stars <- st_as_stars(plot.df, crs = my.crs)
coords.sf <- st_as_sf(as.data.frame(data.HOWA$coords), coords = c('X', 'Y'), 
                      crs = my.crs)
# Plot of median estimate
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.0.med)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Hooded Warbler Relative Abundance')

# Plot of 95% CI width
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.0.ci.width)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Hooded Warbler Relative Abundance')

Univariate spatial GLMMs

Basic model description

When working across large spatial domains, accounting for residual spatial autocorrelation in species distributions can often improve predictive performance of a model, leading to more accurate predictions of species abundance patterns (Guélat and Kéry 2018). We here extend the previous GLMM to incorporate a spatial random effect that accounts for unexplained spatial variation in species abundance across a region of interest (Diggle 1998). Let \(\boldsymbol{s}_j\) denote the geographical coordinates of site \(j\) for \(j = 1, \dots, J\). In all spatially-explicit models, we include \(\boldsymbol{s}_j\) directly in the notation of spatially-indexed variables to indicate the model is spatially-explicit. More specifically, the expected abundance at site \(j\) with coordinates \(\boldsymbol{s}_j\), \(\mu(\boldsymbol{s}_j)\), now takes the form

\[\begin{equation} g(\mu(\boldsymbol{s}_j)) = \boldsymbol{x}(\boldsymbol{s}_j)^{\top}\boldsymbol{\beta} + \text{w}(\boldsymbol{s}_j), \end{equation}\]

where \(\text{w}(\boldsymbol{s}_j)\) is a spatial random effect modeled with a Nearest Neighbor Gaussian Process (NNGP; Datta et al. (2016)). More specifically, we have

\[\begin{equation} \textbf{w}(\boldsymbol{s}) \sim N(\boldsymbol{0}, \boldsymbol{\tilde{\Sigma}}(\boldsymbol{s}, \boldsymbol{s}', \boldsymbol{\theta})), \end{equation}\]

where \(\boldsymbol{\tilde{\Sigma}}(\boldsymbol{s}, \boldsymbol{s}', \boldsymbol{\theta})\) is the NNGP-derived spatial covariance matrix that originates from the full \(J \times J\) covariance matrix \(\boldsymbol{\Sigma}(\boldsymbol{s}, \boldsymbol{s}', \boldsymbol{\theta})\) that is a function of the distances between any pair of site coordinates \(\boldsymbol{s}\) and \(\boldsymbol{s}'\) and a set of parameters \((\boldsymbol{\theta})\) that govern the spatial process. The vector \(\boldsymbol{\theta}\) is equal to \(\boldsymbol{\theta} = \{\sigma^2, \phi, \nu\}\), where \(\sigma^2\) is a spatial variance parameter, \(\phi\) is a spatial decay parameter, and \(\nu\) is a spatial smoothness parameter. \(\nu\) is only specified when using a Matern correlation function. The detection portion of the N-mixture model remains unchanged from the non-spatial N-mixture model. The NNGP is a computationally efficient alternative to working with a full Gaussian process model, which is notoriously slow for even moderately large data sets. See Datta et al. (2016) and Finley et al. (2019) for complete statistical details on the NNGP.

Fitting univariate spatial GLMMs with spAbund()

We will fit the same univariate GLMM that we fit previously using abund(), but we will now make the model spatially-explicit by incorporating a spatial process with spAbund(), which has the following arguments:

spAbund(formula, data, inits, priors, tuning,
        cov.model = 'exponential', NNGP = TRUE, 
        n.neighbors = 15, search.type = 'cb',
        n.batch, batch.length, accept.rate = 0.43, family = 'Poisson',
        n.omp.threads = 1, verbose = TRUE, n.report = 100, 
        n.burn = round(.10 * n.batch * batch.length), n.thin = 1, 
        n.chains = 1, save.fitted = TRUE, ...)

The arguments to spAbund() are very similar to those we saw with abund(), with a few additional components. The formula and data arguments are the same as before, with random slopes and intercepts allowed in both the abundance and detection models. Notice the coords matrix in the data.HOWA list of data. We did not use this for abund(), but specifying the spatial coordinates in data is necessary for all spatially explicit models in spAbundance.

howa.formula <- ~ scale(bio2) + scale(bio8) + scale(bio18) + scale(forest) +
                  scale(devel) + scale(day) + I(scale(day)^2) + scale(tod) +
                  (1 | obs)
str(data.HOWA) # coords is required for spAbund()
List of 3
 $ y     : num [1:95] 0 4 14 1 9 0 11 4 1 6 ...
 $ covs  :'data.frame': 95 obs. of  8 variables:
  ..$ bio2  : num [1:95] 11.6 12.1 10.4 10.4 11.7 ...
  ..$ bio8  : num [1:95] 20.2 17.8 21.4 21 18.4 ...
  ..$ bio18 : num [1:95] 473 395 422 361 378 ...
  ..$ forest: num [1:95] 0.485 0.959 0.717 0.491 0.867 ...
  ..$ devel : num [1:95] 0.01116 0.00159 0.00319 0.01275 0.00239 ...
  ..$ day   : num [1:95] 147 148 147 148 149 148 149 150 153 154 ...
  ..$ tod   : num [1:95] 534 513 508 518 513 511 516 513 510 505 ...
  ..$ obs   : num [1:95] 51 32 12 10 32 33 15 32 11 32 ...
 $ coords: num [1:95, 1:2] 1319 1395 1559 1488 1386 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "X" "Y"

The initial values (inits) are again specified in a list. Valid tags for initial values now additionally include the parameters associated with the spatial random effects. These include: sigma.sq (spatial variance parameter), phi (spatial decay parameter), w (the latent spatial random effects at each site), and nu (spatial smoothness parameter), where the latter is only specified if adopting a Matern covariance function (i.e., cov.model = 'matern'). spAbundance supports four spatial covariance models (exponential, spherical, gaussian, and matern), which are specified in the cov.model argument. Throughout this vignette, we will use an exponential covariance model, which we often use as our default covariance model when fitting spatially-explicit models and is commonly used throughout ecology. To determine which covariance function to use, we can fit models with the different covariance functions and compare them using WAIC to select the best performing function. We will note that the Matern covariance function has the additional spatial smoothness parameter \(\nu\) and thus can often be more flexible than the other functions. However, because we need to estimate an additional parameter, this also tends to require more data (i.e., a larger number of sites) than the other covariance functions, and so we encourage use of the three simpler functions if your data set is sparse. We note that model estimates are generally fairly robust to the different covariance functions, although certain functions may provide substantially better estimates depending on the specific form of the underlying spatial autocorrelation in the data.

The default initial values for phi, and nu are set to random values from the prior distribution, while the default initial value for sigma.sq is set to a random value between 0.05 and 3. In all spatially-explicit models described in this vignette, the spatial decay parameter phi is often the most sensitive to initial values. In general, the spatial decay parameter will often have poor mixing and take longer to converge than the rest of the parameters in the model, so specifying an initial value that is reasonably close to the resulting value can really help decrease run times for complicated models. As an initial value for the spatial decay parameter phi, we compute the mean distance between points in our coordinates matrix and then set it equal to 3 divided by this mean distance. When using an exponential covariance function, \(\frac{3}{\phi}\) is the effective range, or the distance at which the residual spatial correlation between two sites drops to 0.05 (Banerjee, Carlin, and Gelfand 2003). Thus our initial guess for this effective range is the average distance between sites across the simulated region. As with all other parameters, we generally recommend using the default initial values for an initial model run, and if the model is taking a very long time to converge you can rerun the model with initial values based on the posterior means of estimated parameters from the initial model fit. For the spatial variance parameter sigma.sq, we set the initial value to 1. This corresponds to a moderate amount of spatial variance. Further, we set the initial values of the latent spatial random effects at each site to 0. The initial values for these random effects has an extremely small influence on the model results, so we generally recommend setting their initial values to 0 as we have done here (this is also the default). However, if you are running your model for a very long time and are seeing very slow convergence of the MCMC chains, setting the initial values of the spatial random effects to the mean estimates from a previous run of the model could help reach convergence faster.

# Pair-wise distances between all sites
dist.mat <- dist(data.HOWA$coords)
# Exponential covariance model
cov.model <- 'exponential'
# Specify list of inits
inits <- list(beta = 0, kappa = 0.5, sigma.sq.mu = 0.5, sigma.sq = 1,
              phi = 3 / mean(dist.mat),
              w = rep(0, length(data.HOWA$y)))

The parameter NNGP is a logical value that specifies whether we want to use an NNGP to fit the model. Currently, only NNGP = TRUE is supported, but we may eventually add functionality to fit full Gaussian Process models. The arguments n.neighbors and search.type specify the number of neighbors used in the NNGP and the nearest neighbor search algorithm, respectively, to use for the NNGP model. Generally, the default values of these arguments will be adequate. Datta et al. (2016) showed that setting n.neighbors = 15 is usually sufficient, although for certain data sets a good approximation can be achieved with as few as five neighbors, which could substantially decrease run time for the model. We generally recommend leaving search.type = "cb", as this results in a fast code book nearest neighbor search algorithm. However, details on when you may want to change this are described in Finley, Datta, and Banerjee (2020). We will run an NNGP model using the default value for search.type and setting n.neighbors = 15 (both the defaults).

NNGP <- TRUE
n.neighbors <- 15
search.type <- 'cb'

Priors are again specified in a list in the argument priors. We follow standard recommendations for prior distributions from the spatial statistics literature (Banerjee, Carlin, and Gelfand 2003). We assume an inverse gamma prior for the spatial variance parameter sigma.sq (the tag of which is sigma.sq.ig), and uniform priors for the spatial decay parameter phi and smoothness parameter nu (if using the Matern correlation function), with the associated tags phi.unif and nu.unif. The hyperparameters of the inverse Gamma are passed as a vector of length two, with the first and second elements corresponding to the shape and scale, respectively. The lower and upper bounds of the uniform distribution are passed as a two-element vector for the uniform priors. We also allow users to restrict the spatial variance further by specifying a uniform prior (with the tag sigma.sq.unif), which can potentially be useful to place a more informative prior on the spatial parameters. Generally, we use an inverse-Gamma prior.

Note that the priors for the spatial parameters in a spatially-explicit model must be at least weakly informative for the model to converge (Banerjee, Carlin, and Gelfand 2003). For the inverse-Gamma prior on the spatial variance, we typically set the shape parameter to 2 and the scale parameter equal to our best guess of the spatial variance. The default prior hyperparameter values for the spatial variance \(\sigma^2\) are a shape parameter of 2 and a scale parameter of 1. This weakly informative prior suggests a prior mean of 1 for the spatial variance, which is a moderately small amount of spatial variation. Here we will use this default prior. For the spatial decay parameter, our default approach is to set the lower and upper bounds of the uniform prior based on the minimum and maximum distances between sites in the data. More specifically, by default we set the lower bound to 3 / max and the upper bound to 3 / min, where min and max are the minimum and maximum distances between sites in the data set, respectively. This equates to a vague prior that states the spatial autocorrelation in the data could only exist between sites that are very close together, or could span across the entire observed study area. If additional information is known on the extent of the spatial autocorrelation in the data, you may place more restrictive bounds on the uniform prior, which would reduce the amount of time needed for adequate mixing and convergence of the MCMC chains. Here we use this default approach, but will explicitly set the values for transparency.

min.dist <- min(dist.mat)
max.dist <- max(dist.mat)
priors <- list(beta.normal = list(mean = 0, var = 100),
               kappa.unif = c(0, 100),
               sigma.sq.mu.ig = list(0.1, 0.1),
               sigma.sq.ig = c(2, 1),
               phi.unif = c(3 / max.dist, 3 / min.dist))

We again split our MCMC algorithm up into a set of batches and use an adaptive sampler to adaptively tune the variances that we propose new values from. We specify the initial tuning values again in the tuning argument, and now need to add phi and w to the parameters that must be tuned. Note that we do not need to add sigma.sq, as this parameter can be sampled with a more efficient approach (i.e., it’s full conditional distribution is available in closed form).

tuning <- list(beta = 0.5, kappa = 0.5, beta.star = 0.5, w = 0.5, phi = 0.5)

The argument n.omp.threads specifies the number of threads to use for within-chain parallelization, while verbose specifies whether or not to print the progress of the sampler. As before, the argument n.report specifies the interval to report the Metropolis-Hastings sampler acceptance rate. Below we set n.report = 200, which will result in information on the acceptance rate and tuning parameters every 200th batch.

verbose <- TRUE
batch.length <- 25
n.batch <- 800
# Total number of MCMC samples per chain
batch.length * n.batch
[1] 20000
n.report <- 200
n.omp.threads <- 1

We will use the same amount of burn-in and thinning as we did with the non-spatial model, and we’ll also first fit a model with a Poisson distribution. We next fit the model and summarize the results using the summary() function.

n.burn <- 10000
n.thin <- 10
n.chains <- 3
# Approx run time: 1 minute
out.sp <- spAbund(formula = howa.formula,
                  data = data.HOWA,
                  inits = inits,
                  priors = priors,
                  n.batch = n.batch,
                  batch.length = batch.length,
                  tuning = tuning,
                  cov.model = cov.model,
                  NNGP = NNGP,
                  n.neighbors = n.neighbors,
                  search.type = search.type,
                  n.omp.threads = n.omp.threads,
                  n.report = n.report,
                  family = 'Poisson',
                  verbose = TRUE,
                  n.burn = n.burn,
                  n.thin = n.thin,
                  n.chains = n.chains)
----------------------------------------
    Preparing to run the model
----------------------------------------
----------------------------------------
    Building the neighbor list
----------------------------------------
----------------------------------------
Building the neighbors of neighbors list
----------------------------------------
----------------------------------------
    Model description
----------------------------------------
Spatial NNGP Poisson Abundance model fit with 95 sites.

Samples per Chain: 20000 (800 batches of length 25)
Burn-in: 10000 
Thinning Rate: 10 
Number of Chains: 3 
Total Posterior Samples: 3000 

Using the exponential spatial correlation model.

Using 15 nearest neighbors.

Source compiled with OpenMP support and model fit using 1 thread(s).

Adaptive Metropolis with target acceptance rate: 43.0
----------------------------------------
    Chain 1
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     36.0        0.15832
    beta[2]     52.0        0.17850
    beta[3]     52.0        0.17850
    beta[4]     36.0        0.17150
    beta[5]     36.0        0.20533
    beta[6]     44.0        0.14910
    beta[7]     28.0        0.17150
    beta[8]     40.0        0.14042
    beta[9]     44.0        0.27168
    phi     76.0        2.50141
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     32.0        0.14325
    beta[2]     48.0        0.18954
    beta[3]     60.0        0.15832
    beta[4]     60.0        0.16478
    beta[5]     32.0        0.19337
    beta[6]     36.0        0.16152
    beta[7]     40.0        0.18579
    beta[8]     48.0        0.12705
    beta[9]     36.0        0.26102
    phi     60.0        2.60349
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     32.0        0.14910
    beta[2]     48.0        0.16478
    beta[3]     52.0        0.16811
    beta[4]     48.0        0.15832
    beta[5]     40.0        0.17497
    beta[6]     44.0        0.15211
    beta[7]     48.0        0.17150
    beta[8]     52.0        0.13224
    beta[9]     40.0        0.30025
    phi     60.0        3.37654
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 2
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     28.0        0.14910
    beta[2]     52.0        0.17150
    beta[3]     48.0        0.15518
    beta[4]     52.0        0.16478
    beta[5]     40.0        0.17850
    beta[6]     44.0        0.15211
    beta[7]     48.0        0.17850
    beta[8]     32.0        0.14042
    beta[9]     56.0        0.26102
    phi     16.0        3.17991
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     44.0        0.14325
    beta[2]     40.0        0.17850
    beta[3]     48.0        0.16811
    beta[4]     28.0        0.15518
    beta[5]     52.0        0.18954
    beta[6]     48.0        0.14325
    beta[7]     52.0        0.17150
    beta[8]     52.0        0.14615
    beta[9]     52.0        0.27716
    phi     44.0        3.30968
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     40.0        0.14615
    beta[2]     56.0        0.16811
    beta[3]     44.0        0.16478
    beta[4]     52.0        0.15211
    beta[5]     32.0        0.18579
    beta[6]     56.0        0.14615
    beta[7]     28.0        0.17150
    beta[8]     28.0        0.13764
    beta[9]     52.0        0.27168
    phi     44.0        3.24415
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 3
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Parameter   Acceptance  Tuning
    beta[1]     52.0        0.14910
    beta[2]     48.0        0.17497
    beta[3]     40.0        0.15832
    beta[4]     40.0        0.16478
    beta[5]     32.0        0.19337
    beta[6]     52.0        0.14910
    beta[7]     48.0        0.19337
    beta[8]     44.0        0.13764
    beta[9]     40.0        0.26630
    phi     44.0        3.24415
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Parameter   Acceptance  Tuning
    beta[1]     24.0        0.13764
    beta[2]     32.0        0.16478
    beta[3]     44.0        0.16811
    beta[4]     32.0        0.14615
    beta[5]     48.0        0.18579
    beta[6]     32.0        0.15211
    beta[7]     36.0        0.18211
    beta[8]     52.0        0.15832
    beta[9]     36.0        0.25079
    phi     48.0        3.24415
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Parameter   Acceptance  Tuning
    beta[1]     44.0        0.16478
    beta[2]     36.0        0.16152
    beta[3]     48.0        0.16478
    beta[4]     48.0        0.15832
    beta[5]     48.0        0.18579
    beta[6]     44.0        0.14910
    beta[7]     32.0        0.16811
    beta[8]     36.0        0.14042
    beta[9]     48.0        0.28276
    phi     24.0        3.11694
-------------------------------------------------
Batch: 800 of 800, 100.00%
summary(out.sp)

Call:
spAbund(formula = howa.formula, data = data.HOWA, inits = inits, 
    priors = priors, tuning = tuning, cov.model = cov.model, 
    NNGP = NNGP, n.neighbors = n.neighbors, search.type = search.type, 
    n.batch = n.batch, batch.length = batch.length, family = "Poisson", 
    n.omp.threads = n.omp.threads, verbose = TRUE, n.report = n.report, 
    n.burn = n.burn, n.thin = n.thin, n.chains = n.chains)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000
Run Time (min): 0.7522

Abundance (log scale): 
                   Mean     SD    2.5%     50%   97.5%   Rhat ESS
(Intercept)     -0.1236 0.3074 -0.7874 -0.1108  0.4440 1.0058 166
scale(bio2)      0.0306 0.1939 -0.3449  0.0254  0.4192 1.0058 432
scale(bio8)     -0.0696 0.2031 -0.4547 -0.0778  0.3548 1.0172 538
scale(bio18)    -0.0608 0.1988 -0.4445 -0.0652  0.3315 1.0091 388
scale(forest)    0.8874 0.2337  0.4454  0.8815  1.3687 1.0204 361
scale(devel)     0.1285 0.2021 -0.2788  0.1283  0.5406 1.0344 282
scale(day)      -0.5628 0.2165 -1.0033 -0.5599 -0.1503 1.0266 434
I(scale(day)^2) -0.2963 0.1924 -0.6826 -0.2881  0.0617 1.0637 284
scale(tod)       1.0473 0.3391  0.4517  1.0133  1.7670 1.0174 392

Abundance Random Effect Variances (log scale): 
                  Mean     SD   2.5%    50% 97.5%   Rhat ESS
(Intercept)-obs 1.0609 0.8358 0.0778 0.8575 3.284 1.0133  99

Spatial Covariance: 
           Mean     SD   2.5%    50%  97.5%   Rhat  ESS
sigma.sq 1.0870 0.4361 0.4447 1.0216 2.1074 1.0026  246
phi      1.2555 0.6977 0.0628 1.3380 2.2950 1.0069 1161

Posterior predictive checks

Posterior predictive checks proceed exactly as before using the ppcAbund() function.

ppc.out.sp <- ppcAbund(out.sp, fit.stat = 'freeman-tukey', group = 0)
summary(ppc.out.sp)

Call:
ppcAbund(object = out.sp, fit.stat = "freeman-tukey", group = 0)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000

Bayesian p-value:  0.4107 
Fit statistic:  freeman-tukey 

Here we see a striking contrast to the Bayesian p-value from the non-spatial GLMM, which was essentially 0. Here, our estimate is close to 0.5, indicating our model is adequately representing the variability in the data with the addition of the spatial random effect.

Model selection using WAIC

We next compare the non-spatial Poisson GLMM to the spatial Poisson GLM.

# Non-spatial Poisson GLMM
waicAbund(out)
      elpd         pD       WAIC 
-133.44441   52.16574  371.22031 
# Spatial Poisson GLMM
waicAbund(out.sp)
      elpd         pD       WAIC 
-114.64619   34.63496  298.56230 

We see a substantial decrease in WAIC in the spatial model compared to the non-spatial model.

Prediction

We can similarly predict across a region of interest using the predict() function as we saw with the non-spatial GLMM. Here we again generate predictions across Pennsylvania. The primary arguments for prediction here are identical to those we saw for the non-spatial model, with the addition of the coords argument to specify the spatial coordinates of the prediction locations. These are necessary to generate the predictions of the spatial random effects. We use the same design matrix that we previously formed for the non-spatial predictions (X.0), which contains the covariate values standardized by the values used when we fit the model. There are also arguments for parallelization (n.omp.threads), reporting sampler progress (verbose and n.report), and predicting without the spatial random effects (include.sp). We generally only recommend setting include.sp = FALSE when generating predictions for a marginal probability plot (see the N-mixture model vignette for an example of this).

coords.0 <- as.matrix(bbsPredData[, c('x', 'y')])
out.sp.pred <- predict(out.sp, X.0, coords.0, ignore.RE = TRUE, verbose = TRUE)
----------------------------------------
    Prediction description
----------------------------------------
NNGP spatial GLMM fit with 95 observations.

Number of covariates 9 (including intercept if specified).

Using the exponential spatial correlation model.

Using 15 nearest neighbors.

Number of MCMC samples 3000.

Predicting at 816 locations.

Source compiled with OpenMP support and model fit using 1 threads.
-------------------------------------------------
        Predicting
-------------------------------------------------
Location: 100 of 816, 12.25%
Location: 200 of 816, 24.51%
Location: 300 of 816, 36.76%
Location: 400 of 816, 49.02%
Location: 500 of 816, 61.27%
Location: 600 of 816, 73.53%
Location: 700 of 816, 85.78%
Location: 800 of 816, 98.04%
Location: 816 of 816, 100.00%
Generating abundance predictions
mu.0.quants <- apply(out.sp.pred$mu.0.samples, 2, quantile, c(0.025, 0.5, 0.975))
plot.df <- data.frame(Easting = bbsPredData$x,
                      Northing = bbsPredData$y,
                      mu.0.med = mu.0.quants[2, ],
                      mu.0.ci.width = mu.0.quants[3, ] - mu.0.quants[1, ])
# proj4string for the coordinate reference system
my.crs <- "+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs"
coords.stars <- st_as_stars(plot.df, crs = my.crs)
coords.sf <- st_as_sf(as.data.frame(data.HOWA$coords), coords = c('X', 'Y'), 
                      crs = my.crs)
# Plot of median estimate
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.0.med)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Hooded Warbler Relative Abundance')

# Plot of 95% CI width
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.0.ci.width)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Hooded Warbler Relative Abundance')

Note in our spatially-explicit predictions, there is extremely large uncertainy in certain parts of the study region. This is not too surprising given the fairly small number of spatial locations we used to fit the model.

Multivariate GLMMs

Basic model description

Now consider the case where count data, \(y_{i, j}\), are collected for multiple species \(i = 1, \dots, I\) (or some other set of multiple variables) at each survey location \(j\). We model \(y_{i, j}\) analogous to the univariate GLMM, with expected abundance now varying by species and site according to

\[\begin{equation}\label{mu-Abund} g(\mu_{i, j}) = \boldsymbol{x}_j^\top\boldsymbol{\beta}_i, \end{equation}\]

where \(\boldsymbol{\beta}_i\) are the species-specific effects of covariates \(\boldsymbol{x}_j\) (including an intercept) . As in univariate models, for all multivariate GLMMs in spAbundance we use a log link function when modeling integer count data with the Poisson or negative binomial distributions, while for Gaussian data, we use the identity link function. When \(y_{i, j}\) is modeled using a negative binomial distribution, we estimate a separate dispersion parameter \(\kappa_i\) for each species. We model \(\boldsymbol{\beta}_i\) as random effects arising from a common, community-level normal distribution, which often leads to increased precision of species-specific effects compared to single-species models. For example, the species-specific intercept \(\beta_{0, i}\) is modeled according to

\[\begin{equation}\label{betaComm} \beta_{0, i} \sim \text{Normal}(\mu_{\beta_0}, \tau^2_{\beta_0}), \end{equation}\]

where \(\mu_{\beta_0}\) is the community-level abundance intercept, and \(\tau^2_{\beta_0}\) is the variance of the intercept across all \(I\) species.

We assign normal priors to the community-level (\(\boldsymbol{\mu}_{\beta}\)) mean parameters and inverse-Gamma priors to the community-level variance parameters (\(\boldsymbol{\tau^2}_{\beta}\)). We assign independent uniform priors to the species specific dispersion parameters \(\kappa_i\) when using a negative binomial distribution. When fitting a Gaussian GLMM (a linear mixed model or LMM), we assign independent inverse-Gamma priors to the species-specific residual variance parameters \(\tau^2_i\).

Fitting multivariate GLMMs with msAbund()

spAbundance uses nearly identical syntax for fitting multivariate GLMMs as it does for univariate models and provides the same functionality for posterior predictive checks, model assessment and selection using WAIC, and prediction. The msAbund() function fits the multivariate abundance models. msAbund() has the following syntax

msAbund(formula, data, inits, priors, tuning, 
        n.batch, batch.length, accept.rate = 0.43, family = 'Poisson',
        n.omp.threads = 1, verbose = TRUE, n.report = 100, 
        n.burn = round(.10 * n.batch * batch.length), n.thin = 1, n.chains = 1,
        save.fitted = TRUE, ...)

Notice these are the exact same arguments we saw with abund(). We will again use data from the North American Breeding Bird Survey in Pennsylvania, USA, but now we will use data from all six species. Recall this is stored in the bbsData object.

# Remind ourselves of the structure of bbsData
str(bbsData)
List of 3
 $ y     : num [1:6, 1:95] 1 0 0 0 0 0 3 0 1 3 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:6] "AMRE" "BLBW" "BTBW" "BTNW" ...
  .. ..$ : NULL
 $ covs  :'data.frame': 95 obs. of  8 variables:
  ..$ bio2  : num [1:95] 11.6 12.1 10.4 10.4 11.7 ...
  ..$ bio8  : num [1:95] 20.2 17.8 21.4 21 18.4 ...
  ..$ bio18 : num [1:95] 473 395 422 361 378 ...
  ..$ forest: num [1:95] 0.485 0.959 0.717 0.491 0.867 ...
  ..$ devel : num [1:95] 0.01116 0.00159 0.00319 0.01275 0.00239 ...
  ..$ day   : num [1:95] 147 148 147 148 149 148 149 150 153 154 ...
  ..$ tod   : num [1:95] 534 513 508 518 513 511 516 513 510 505 ...
  ..$ obs   : num [1:95] 51 32 12 10 32 33 15 32 11 32 ...
 $ coords: num [1:95, 1:2] 1319 1395 1559 1488 1386 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "X" "Y"

We will model relative abundance for all species using the same variables as we saw previously. For multi-species models, the multi-species detection-nondetection data y is now a matrix with dimensions corresponding to species, and sites.

ms.formula <- ~ scale(bio2) + scale(bio8) + scale(bio18) + scale(forest) + 
                scale(devel) + scale(day) + I(scale(day)^2) + scale(tod) + 
                (1 | obs)

Next we specify the initial values in inits. For multivariate GLMMs, we supply initial values for community-level and species-level parameters. In msAbund(), we will supply initial values for the following parameters: beta.comm (community-level coefficients), beta (species-level coefficients), tau.sq.beta (community-level variance parameters), kappa (species-level negative binomial dispersion parameters), sigma.sq.mu (random effect variances), and tau.sq (species-level Gaussian variance parameters). These are all specified in a single list. Initial values for community-level parameters (including the random effect variances) are either vectors of length corresponding to the number of community-level parameters in the model (including the intercepts) or a single value if all parameters are assigned the same initial values. Initial values for species level coefficients are either matrices with the number of rows indicating the number of species, and each column corresponding to a different regression parameter, or a single value if the same initial value is used for all species and parameters. Similarly, initial values for the species-specific NB dispersion parameter and the Gaussian variance parameter is either a vector with a different initial value for each species, or a single value if the same initial value is used for all species.

# Number of species
n.sp <- dim(bbsData$y)[1]
ms.inits <- list(beta.comm = 0, beta = 0, tau.sq.beta = 1, kappa = 1, 
                 sigma.sq.mu = 0.5)

In multivariate models, we specify priors on the community-level coefficients (or hyperparameters) rather than the species-level effects, with the exception that we still assign uniform priors for the species-specific negative binomial overdispersion parameter and species-specific Gaussian variance parameter. For nonspatial models, these priors are specified with the following tags: beta.comm.normal (normal prior on the community-level mean effects), tau.sq.beta.ig (inverse-Gamma prior on the community-level variance parameters), sigma.sq.mu.ig (inverse-Gamma prior on the random effect variances), kappa.unif (uniform prior on the species-specific overdispersion parameters), tau.sq.ig (inverse-gamma prior on the species-specific Gaussian variance parameters). For all parameters except the species-specific NB overdispersion parameters, each tag consists of a list with elements corresponding to the mean and variance for normal priors and scale and shape for inverse-Gamma priors. Values can be specified individually for each parameter or as a single value if the same prior is assigned to all parameters of a given type. For the species-specific overdispersion parameters, the prior is specified as a list with two elements representing the lower and upper bound of the uniform distribution, where each of the elements can be a single value if the same prior is used for all species, or a vector of values for each species if specifying a different prior for each species.

By default, we set the prior hyperparameter values for the community-level means to a mean of 0 and a variance of 100. Below we specify these priors explicitly. For the community-level variance parameters, by default we set the scale and shape parameters to 0.1 following the recommendations of (Lunn et al. 2013), which results in a weakly informative prior on the community-level variances. This may lead to shrinkage of the community-level variance towards zero under certain circumstances so that all species will have fairly similar values for the species-specific covariate effect (Gelman 2006), although we have found multi-species models to be relatively robust to this prior specification. For the negative binomial overdispersion parameters, we by default use a lower bound of 0 and upper bound of 100 for the uniform priors for each species, as we did in the single-species (univariate) models. Below we explicitly set these default prior values for our example. For the Gaussian variance parameters, we by default set the shape and scale parameter equal to 0.01, which results in a vague prior on the residual variances.

ms.priors <- list(beta.comm.normal = list(mean = 0, var = 100),
                  tau.sq.beta.ig = list(a = 0.1, b = 0.1),
                  sigma.sq.mu.ig = list(a = 0.1, b = 0.1),
                  kappa.unif = list(a = 0, b = 100))

All that’s now left to do is specify the number of threads to use (n.omp.threads), the number of MCMC samples (which we do by specifying the number of batches n.batch and batch length batch.length), the initial tuning variances (tuning), the amount of samples to discard as burn-in (n.burn), the thinning rate (n.thin), and arguments to control the display of sampler progress (verbose, n.report). Note for the tuning variances, we do not need to specify initial tuning values for any of the community-level parameters, as those parameters can be sampled with an efficient Gibbs update. We will also run the model with a Poisson distribution for abundance, which later we will shortly compare to a negative binomial.

# Specify initial tuning values
ms.tuning <- list(beta = 0.3, beta.star = 0.5, kappa = 0.5)
# Approx. run time:  1 min
out.ms <- msAbund(formula = ms.formula,
                  data = bbsData,
                  inits = ms.inits,
                  n.batch = 800,
                  tuning = ms.tuning,
                  batch.length = 25,
                  family = 'Poisson',
                  priors = ms.priors,
                  n.omp.threads = 1,
                  verbose = TRUE,
                  n.report = 200,
                  n.burn = 10000,
                  n.thin = 10,
                  n.chains = 3)
----------------------------------------
    Preparing to run the model
----------------------------------------
beta.star is not specified in initial values.
Setting initial values from the prior.
----------------------------------------
    Model description
----------------------------------------
Multi-species Poisson Abundance model fit with 95 sites and 6 species.

Samples per Chain: 20000 
Burn-in: 10000 
Thinning Rate: 10 
Number of Chains: 3 
Total Posterior Samples: 3000 

Source compiled with OpenMP support and model fit using 1 thread(s).

Adaptive Metropolis with target acceptance rate: 43.0
----------------------------------------
    Chain 1
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     28.0        0.11837
    2   beta[1]     44.0        0.23364
    3   beta[1]     48.0        0.27418
    4   beta[1]     56.0        0.15047
    5   beta[1]     44.0        0.15351
    6   beta[1]     40.0        0.29113
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.11602
    2   beta[1]     48.0        0.23364
    3   beta[1]     28.0        0.27972
    4   beta[1]     32.0        0.15047
    5   beta[1]     40.0        0.15047
    6   beta[1]     24.0        0.30914
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     48.0        0.11147
    2   beta[1]     44.0        0.23836
    3   beta[1]     52.0        0.26875
    4   beta[1]     36.0        0.15351
    5   beta[1]     48.0        0.14749
    6   beta[1]     40.0        0.30914
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 2
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     72.0        0.10927
    2   beta[1]     56.0        0.23836
    3   beta[1]     40.0        0.26875
    4   beta[1]     32.0        0.16966
    5   beta[1]     44.0        0.15047
    6   beta[1]     40.0        0.29113
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.11602
    2   beta[1]     32.0        0.22448
    3   beta[1]     36.0        0.27418
    4   beta[1]     36.0        0.15047
    5   beta[1]     32.0        0.14749
    6   beta[1]     40.0        0.30302
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     52.0        0.11147
    2   beta[1]     52.0        0.22003
    3   beta[1]     48.0        0.25821
    4   beta[1]     52.0        0.17308
    5   beta[1]     52.0        0.15661
    6   beta[1]     36.0        0.29701
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 3
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     48.0        0.11602
    2   beta[1]     20.0        0.22901
    3   beta[1]     44.0        0.28537
    4   beta[1]     32.0        0.16630
    5   beta[1]     44.0        0.16301
    6   beta[1]     36.0        0.29113
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     64.0        0.10290
    2   beta[1]     52.0        0.23836
    3   beta[1]     40.0        0.26875
    4   beta[1]     56.0        0.16301
    5   beta[1]     48.0        0.15661
    6   beta[1]     52.0        0.29701
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Species Parameter   Acceptance  Tuning
    1   beta[1]     56.0        0.11837
    2   beta[1]     28.0        0.23364
    3   beta[1]     32.0        0.29113
    4   beta[1]     44.0        0.15978
    5   beta[1]     40.0        0.15351
    6   beta[1]     48.0        0.30302
-------------------------------------------------
Batch: 800 of 800, 100.00%

We can display a nice summary of these results using the summary() function as before. For multivariate objects, when using summary we need to specify the level of parameters we want to summarize. We do this using the argument level, which takes values community, species, or both to print results for community-level parameters, species-level parameters, or all parameters. By default, level is set to both to display both species and community-level parameters.

summary(out.ms)

Call:
msAbund(formula = ms.formula, data = bbsData, inits = ms.inits, 
    priors = ms.priors, tuning = ms.tuning, n.batch = 800, batch.length = 25, 
    family = "Poisson", n.omp.threads = 1, verbose = TRUE, n.report = 200, 
    n.burn = 10000, n.thin = 10, n.chains = 3)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000
Run Time (min): 0.8102

----------------------------------------
    Community Level
----------------------------------------
Abundance Means (log scale): 
                   Mean     SD    2.5%     50%  97.5%   Rhat  ESS
(Intercept)     -1.0393 0.7391 -2.4502 -1.0377 0.4309 1.0010 2274
scale(bio2)      0.0007 0.2008 -0.3670 -0.0049 0.3912 1.0023 2190
scale(bio8)      0.0853 0.1875 -0.2710  0.0823 0.4643 1.0022 2171
scale(bio18)    -0.2274 0.1937 -0.6227 -0.2246 0.1491 1.0063 2039
scale(forest)    1.3699 0.3627  0.6709  1.3424 2.1602 1.0005 1514
scale(devel)    -0.0005 0.1940 -0.3640 -0.0047 0.3954 1.0056 1704
scale(day)      -0.2754 0.1898 -0.6378 -0.2792 0.1111 1.0085 1572
I(scale(day)^2) -0.0651 0.1417 -0.3430 -0.0661 0.2185 1.0100 2496
scale(tod)       0.1603 0.2962 -0.4281  0.1576 0.7486 1.0001 2715

Abundance Variances (log scale): 
                  Mean     SD   2.5%    50%   97.5%   Rhat  ESS
(Intercept)     3.0970 3.4972 0.6871 2.1580 11.1144 1.0030 2183
scale(bio2)     0.2092 0.2747 0.0380 0.1401  0.8459 1.0153 2457
scale(bio8)     0.1671 0.1924 0.0266 0.1092  0.6839 1.0049 2308
scale(bio18)    0.1888 0.2525 0.0353 0.1256  0.6866 1.0386 2817
scale(forest)   0.7757 1.6877 0.1261 0.4934  2.9547 1.1343 3000
scale(devel)    0.1552 0.2066 0.0279 0.1032  0.6283 1.0502 2081
scale(day)      0.1743 0.2098 0.0298 0.1132  0.6660 1.0056 2092
I(scale(day)^2) 0.1059 0.1224 0.0233 0.0738  0.3685 1.0466 2799
scale(tod)      0.4711 0.6369 0.0787 0.3203  1.6806 1.0279 2457

Abundance Random Effect Variances (log scale): 
                  Mean     SD   2.5%    50%  97.5%   Rhat ESS
(Intercept)-obs 2.1382 0.3287 1.5833 2.1095 2.8507 1.0187 377

----------------------------------------
    Species Level
----------------------------------------
Abundance (log scale): 
                        Mean     SD    2.5%     50%   97.5%   Rhat  ESS
(Intercept)-AMRE      1.0009 0.2151  0.5644  1.0091  1.4049 1.0300  185
(Intercept)-BLBW     -1.7039 0.3583 -2.4368 -1.6887 -1.0629 1.0113  294
(Intercept)-BTBW     -2.2007 0.4580 -3.1252 -2.1949 -1.3400 1.0103  242
(Intercept)-BTNW     -0.6548 0.3026 -1.2501 -0.6492 -0.0827 1.0230  193
(Intercept)-HOWA     -0.1369 0.2786 -0.7266 -0.1240  0.3669 1.0062  219
(Intercept)-MAWA     -2.5903 0.4437 -3.5178 -2.5668 -1.7899 1.0020  314
scale(bio2)-AMRE     -0.2452 0.0923 -0.4252 -0.2437 -0.0643 1.0077  955
scale(bio2)-BLBW     -0.2561 0.1779 -0.6138 -0.2531  0.0795 1.0051  825
scale(bio2)-BTBW      0.2241 0.2020 -0.1532  0.2165  0.6373 1.0030 1011
scale(bio2)-BTNW      0.1489 0.1268 -0.1013  0.1472  0.4047 1.0023  823
scale(bio2)-HOWA     -0.2140 0.1072 -0.4252 -0.2141 -0.0021 1.0029 1549
scale(bio2)-MAWA      0.3133 0.2161 -0.0995  0.3081  0.7493 1.0030 1022
scale(bio8)-AMRE      0.0801 0.1146 -0.1448  0.0779  0.3026 1.0467  518
scale(bio8)-BLBW      0.2403 0.2449 -0.2079  0.2335  0.7593 1.0012 1078
scale(bio8)-BTBW      0.1358 0.2319 -0.2925  0.1309  0.6111 1.0037 1520
scale(bio8)-BTNW      0.2617 0.1999 -0.1154  0.2572  0.6780 1.0084  759
scale(bio8)-HOWA     -0.0192 0.1370 -0.2873 -0.0200  0.2480 1.0074  669
scale(bio8)-MAWA     -0.1965 0.2434 -0.6892 -0.1887  0.2591 1.0026 1036
scale(bio18)-AMRE     0.0113 0.1016 -0.1923  0.0134  0.2075 1.0341  580
scale(bio18)-BLBW    -0.5902 0.2228 -1.0635 -0.5826 -0.1887 1.0113  646
scale(bio18)-BTBW    -0.2567 0.1894 -0.6394 -0.2582  0.1132 1.0068 1119
scale(bio18)-BTNW    -0.3335 0.1427 -0.6141 -0.3323 -0.0422 1.0388  901
scale(bio18)-HOWA     0.0354 0.1184 -0.1961  0.0361  0.2741 1.0215  711
scale(bio18)-MAWA    -0.1982 0.1872 -0.5683 -0.1991  0.1640 1.0130  880
scale(forest)-AMRE    0.6889 0.1245  0.4495  0.6878  0.9408 1.0567  662
scale(forest)-BLBW    1.3169 0.2707  0.8099  1.3072  1.8578 1.0042  594
scale(forest)-BTBW    2.2385 0.4098  1.5360  2.2032  3.0924 1.0045  278
scale(forest)-BTNW    1.4532 0.1905  1.0874  1.4569  1.8287 1.0124  436
scale(forest)-HOWA    0.7379 0.1303  0.4892  0.7362  0.9982 1.0121 1036
scale(forest)-MAWA    1.8175 0.3449  1.2101  1.7991  2.5680 1.0137  312
scale(devel)-AMRE    -0.2034 0.1139 -0.4290 -0.2023  0.0159 1.0268  637
scale(devel)-BLBW     0.1020 0.2333 -0.3622  0.0976  0.5651 1.0073 1145
scale(devel)-BTBW     0.0908 0.2492 -0.3902  0.0934  0.5800 1.0088  832
scale(devel)-BTNW     0.1239 0.1836 -0.2314  0.1281  0.4782 1.0096  397
scale(devel)-HOWA    -0.1300 0.1204 -0.3764 -0.1268  0.0966 1.0066  739
scale(devel)-MAWA     0.0050 0.3067 -0.6445  0.0163  0.5742 1.0049 1522
scale(day)-AMRE      -0.3274 0.1078 -0.5323 -0.3255 -0.1156 1.0042  638
scale(day)-BLBW      -0.4723 0.1823 -0.8420 -0.4692 -0.1344 1.0321  658
scale(day)-BTBW      -0.1056 0.2458 -0.5662 -0.1095  0.4029 1.0391  733
scale(day)-BTNW      -0.1829 0.1686 -0.5041 -0.1870  0.1501 1.0570  437
scale(day)-HOWA      -0.5354 0.1340 -0.8135 -0.5311 -0.2817 1.0185  967
scale(day)-MAWA      -0.0554 0.2608 -0.5456 -0.0638  0.4881 1.0394  582
I(scale(day)^2)-AMRE -0.0989 0.0823 -0.2564 -0.1004  0.0681 1.0158  554
I(scale(day)^2)-BLBW  0.0998 0.1185 -0.1328  0.0994  0.3292 1.0188  669
I(scale(day)^2)-BTBW -0.0933 0.1491 -0.4033 -0.0885  0.1888 1.0218  781
I(scale(day)^2)-BTNW  0.0299 0.0976 -0.1626  0.0311  0.2182 1.0403  476
I(scale(day)^2)-HOWA -0.2505 0.1192 -0.4942 -0.2437 -0.0347 1.0030  678
I(scale(day)^2)-MAWA -0.0895 0.1366 -0.3669 -0.0880  0.1788 1.0142  726
scale(tod)-AMRE      -0.1377 0.1046 -0.3417 -0.1379  0.0613 1.0117  379
scale(tod)-BLBW      -0.2145 0.1932 -0.5925 -0.2110  0.1609 1.0293  557
scale(tod)-BTBW       0.2214 0.2755 -0.2889  0.2160  0.7817 1.0004 1732
scale(tod)-BTNW       0.0180 0.1629 -0.2997  0.0187  0.3430 1.0124 1207
scale(tod)-HOWA       1.0564 0.3049  0.5050  1.0420  1.6866 1.0207  552
scale(tod)-MAWA       0.0270 0.2652 -0.4964  0.0259  0.5432 1.0004 1726
# Or more explicitly
# summary(out.ms, level = 'both')

We see adequate convergence of most parameters, although we may run the model a bit longer to ensure convergence of the community-level variance parameters. Looking at the community-level values, we see a very strong effect of forest cover on average across the community. This is not too surprising given that we are working with six warbler species that breed in forest habitat.

Posterior predictive checks

We can use the ppcAbund() function to again perform posterior predictive checks, and then subsequently summarize the check with a Bayesian p-value using the summary() function. Notice for multivariate models we perform a posterior predictive check separately for each species, and the resulting Bayesian p-values can be summarized for both the overall community and individually for each species.

ppc.ms.out <- ppcAbund(out.ms, fit.stat = 'freeman-tukey', group = 0)
Currently on species 1 out of 6
Currently on species 2 out of 6
Currently on species 3 out of 6
Currently on species 4 out of 6
Currently on species 5 out of 6
Currently on species 6 out of 6
summary(ppc.ms.out)

Call:
ppcAbund(object = out.ms, fit.stat = "freeman-tukey", group = 0)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000

----------------------------------------
    Community Level
----------------------------------------
Bayesian p-value:  0.0643 

----------------------------------------
    Species Level
----------------------------------------
 Bayesian p-value: 0.0537
 Bayesian p-value: 0.029
 Bayesian p-value: 0.1487
 Bayesian p-value: 0.084
 Bayesian p-value: 3e-04
 Bayesian p-value: 0.0703
Fit statistic:  freeman-tukey 

Model selection using WAIC

Model selection (sometimes also called model comparison) proceeds exactly as before using WAIC. We compare our previous model to the same model, but now use a negative binomial distribution. Note that for multivariate models the logical argument by.sp allows us to calculate WAIC individually for each species if set to TRUE.

# Approx. run time:  1 min
out.ms.nb <- msAbund(formula = ms.formula,
                     data = bbsData,
                     inits = ms.inits,
                     n.batch = 800,
                     tuning = ms.tuning,
                     batch.length = 25,
                     family = 'NB',
                     priors = ms.priors,
                     n.omp.threads = 1,
                     verbose = FALSE,
                     n.burn = 10000,
                     n.thin = 10,
                     n.chains = 3)
beta.star is not specified in initial values.
Setting initial values from the prior.
# Overall WAIC for Poisson model
waicAbund(out.ms)
     elpd        pD      WAIC 
-607.7303  200.6553 1616.7712 
# Overall WAIC for NB model
waicAbund(out.ms.nb)
     elpd        pD      WAIC 
-666.7796  154.8107 1643.1806 
# WAIC by species for Poisson model
waicAbund(out.ms, by.sp = TRUE)
        elpd       pD     WAIC
1 -176.90388 44.44421 442.6962
2  -73.54415 30.31010 207.7085
3  -58.77351 20.92335 159.3937
4 -105.63397 31.56057 274.3891
5 -136.42839 52.48398 377.8247
6  -56.44645 20.93304 154.7590
# WAIC by species for NB model
waicAbund(out.ms.nb, by.sp = TRUE)
        elpd       pD     WAIC
1 -190.41727 38.67562 458.1858
2  -86.42314 19.97564 212.7976
3  -63.65467 21.23337 169.7761
4 -111.47343 31.21249 285.3718
5 -156.12431 24.96286 362.1743
6  -58.68678 18.75072 154.8750

We see the overall WAIC is best for the Poisson model compared to the NB model.

Prediction

Prediction proceeds exactly as we have seen previously with the univariate GLMM. We will use the Poisson model to predict relative abundance across Pennsylvania for each species.

out.ms.pred <- predict(out.ms, X.0, ignore.RE = TRUE)
# Look at the resulting object
str(out.ms.pred)
List of 3
 $ mu.0.samples: num [1:3000, 1:6, 1:816, 1] 8.15 7.59 7.63 8.43 8.71 ...
 $ y.0.samples : int [1:3000, 1:6, 1:816, 1] 7 19 7 8 10 8 7 8 4 5 ...
 $ call        : language predict.msAbund(object = out.ms, X.0 = X.0, ignore.RE = TRUE)
 - attr(*, "class")= chr "predict.msAbund"

Notice we now have estimates of relative abundance across the state for each species, which we can use to generate a map of relative abundance for each species. Alternatively, we can generate a map of total relative abundance across all species as a simple measure of “how many birds” we could expect to observe at any given location. Of course, this is just one community-level metric we could generate and we could instead generate some form of abundance-weighted richness metric, diversity metric, etc.

mu.sum.samples <- apply(out.ms.pred$mu.0.samples, c(1, 3), sum)
# Average total abundance at each site
mu.sum.means <- apply(mu.sum.samples, 2, mean)
plot.df <- data.frame(Easting = bbsPredData$x,
                      Northing = bbsPredData$y,
                      mu.sum.means = mu.sum.means)
coords.stars <- st_as_stars(plot.df, crs = my.crs)
# Plot of total relative abundance
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.sum.means)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Relative abundance of six warbler species')

If you’re familiar with Pennsylvania geography, this map makes a fair amount of sense as total relative abundance of the six species is highest in the North-Central portion of the state (which is heavily forested and more variable in elevation) and is lowest in the Southeastern portion of the state (which is heavily developed).

Latent factor multivariate GLMMs

Basic model description

The latent factor multivariate GLMM is identical to the previously described multivariate GLMM except we include an additional component in the model to accommodate residual correlations between species. This type of model is often referred to as an abundance-based joint species distribution model (JSDM) in the statistical ecology literature (Warton et al. 2015). The previously described multivariate GLMM can be viewed as a simplified version of the latent factor multivariate GLMM, where we assume there are no residual correlations between species. In fact, the previously described multivariate GLMM could also more simply be described as a simple GLMM with a bunch of random intercepts and slopes across the different species in the data set. In the statistical literature, the latent factor multivariate GLMM now explicitly accounts for correlations between responses, and thus may formally be considered a “joint” model.

The model is identical to the previously described multivariate GLMM, with the only addition being a species-specific random effect at each site added to the equation for relative abundance. More specifically, we model species-specific relative abundance as

\[\begin{equation} g(\mu_{i, j}) = \boldsymbol{x}_j^\top\boldsymbol{\beta}_i + \text{w}^\ast_{i, j}. \end{equation}\]

The species-specific random effect \(\text{w}^\ast_{i, j}\) is used to account for residual correlations between species by assuming that correlation amongst the species can be explained by species-specific effects of a set of \(q\) latent variables. More specifically, we use a factor modeling approach (Lopes and West 2004) to account for species residual correlations in a computationally efficient manner (Hui et al. 2015). This approach is ideal for large groups of species, where estimating a full \(I \times I\) covariance matrix would be computationally intractable (and/or require massive amounts of data). Specifically, we decompose \(\text{w}^\ast_{i, j}\) into a linear combination of \(q\) latent variables (i.e., factors) and their associated species-specific coefficients (i.e., factor loadings) according to

\[\begin{equation}\label{factorModel} \text{w}^\ast_{i, j} = \boldsymbol{\lambda}_i^\top\text{w}_j, \end{equation}\]

where \(\boldsymbol{\lambda}_i^\top\) is the \(i\text{th}\) row of factor loadings from an \(I \times q\) loadings matrix \(\boldsymbol{\Lambda}\), and \(\text{w}_j\) is a \(q \times 1\) vector of independent latent factors at site \(j\). By settng \(q << N\), we achieve dimension reduction to efficiently model communities with a large number of species (Lopes and West 2004; Hui et al. 2015). The approach accounts for residual species correlations via their species-specific responses to the \(q\) factors. We model each latent factor as a standard normal random variable. To ensure identifiability of the latent factors from the latent factor loadings, we fix the upper triangle of the factor loadings matrix to 0 and the diagonal elements to 1. We assign standard normal priors to the lower triangular elements of the factor loadings matrix. All other priors are identical to the multivariate GLMM previously discussed.

The constraints on the factor loadings matrix ensure identifiability of the factor loadings from the latent factors, but this also results in important practical considerations when fitting these models (e.g., ordering of the species, initial values). This vignette on the spOccupancy website discusses these (and other) considerations. All the advice applied to factor models fit with detection-nondetection data in spOccupancy also apply to factor models fit in spAbundance.

Fitting latent factor multivariate GLMMs with lfMsAbund()

The function lfMsAbund() fits latent factor multi-species GLMMs in spAbundance. The arguments are identical to those in msAbund() with one additional argument that specifies the number of factors we wish to use in the model (n.factors):

lfMsAbund(formula, data, inits, priors, tuning, n.factors,
          n.batch, batch.length, accept.rate = 0.43, family = 'Poisson',
          n.omp.threads = 1, verbose = TRUE, n.report = 100, 
          n.burn = round(.10 * n.batch * batch.length), n.thin = 1, n.chains = 1,
          save.fitted = TRUE, ...)

For guidance on choosing the number of latent factors, see this vignette on the spOccupancy website. Here we will fit a model with 3 latent factors.

n.factors <- 3

There are only a few slight differences in how we go about fitting a multivariate GLMM with latent factors compared to one without latent factors. The data format as well as formula remain the same as before.

ms.formula <- ~ scale(bio2) + scale(bio8) + scale(bio18) + scale(forest) +
                scale(devel) + scale(day) + I(scale(day)^2) + scale(tod) +
                (1 | obs)

Initial values are specified as with msAbund(), with the exception that we can now specify initial values for the latent factor loadings matrix lambda and the latent factors w. Initial values for the species-specific factor loadings (lambda) are specified as a numeric matrix with \(I\) rows and \(q\) columns, where \(I\) is the number of species and \(q\) is the number of latent factors used in the model. The diagonal elements of the matrix must be 1, and values in the upper triangle must be set to 0 to ensure identifiability of the latent factors. Note that the default initial values for the lower triangle of the factor loadings matrix is 0. We can also specify the initial values for the latent factors. Below we set these to 0 (which is the default).

# Number of species
n.sp <- dim(bbsData$y)[1]
# Initiate all lambda initial values to 0.
lambda.inits <- matrix(0, n.sp, n.factors)
# Set diagonal elements to 1
diag(lambda.inits) <- 1
# Set lower triangular elements to random values from a standard normal distribution
lambda.inits[lower.tri(lambda.inits)] <- rnorm(sum(lower.tri(lambda.inits)))
# Check it out.
lambda.inits
            [,1]       [,2]      [,3]
[1,]  1.00000000  0.0000000 0.0000000
[2,]  0.26608875  1.0000000 0.0000000
[3,] -0.40888084  0.1784342 1.0000000
[4,] -0.11546692 -0.4924976 0.3923432
[5,]  1.10328447  0.1192894 0.9443346
[6,] -0.09166088  1.0739639 1.1579203
w.inits <- matrix(0, n.factors, ncol(bbsData$y))
ms.inits <- list(beta.comm = 0, beta = 0, tau.sq.beta = 1, 
                 lambda = lambda.inits, kappa = 1, w = w.inits,
                 sigma.sq.mu = 0.5)

Priors are specified exactly the same as we saw with msAbund(). We do not need to explicitly specify the prior for the factor loadings, as we require the prior for the lower-triangular values to be Normal(0, 1).

ms.priors <- list(beta.comm.normal = list(mean = 0, var = 2.72),
                  tau.sq.beta.ig = list(a = 0.1, b = 0.1),
                  sigma.sq.mu.ig = list(a = 0.1, b = 0.1),
                  kappa.unif = list(a = 0, b = 100))

We finish up by specifying the tuning values, where now we specify the initial tuning variance for the factor loadings lambda as well as the latent factors w. We then run the model for 20,000 iterations with a burn-in of 10,000 samples and a thinning rate of 10, for each of 3 chains, yielding a total of 3000 posterior samples. We will fit the model with a Poisson distribution for abundance.

# Specify initial tuning values
ms.tuning <- list(beta = 0.3, beta.star = 0.5, kappa = 0.5, w = 0.5, lambda = 0.5)
# Approx. run time: 1.5 min
out.lf.ms <- lfMsAbund(formula = ms.formula,
                       data = bbsData,
                       inits = ms.inits,
                       n.batch = 800,
                       n.factors = n.factors,
                       tuning = ms.tuning,
                       batch.length = 25,
                       family = 'Poisson',
                       priors = ms.priors,
                       n.omp.threads = 1,
                       verbose = TRUE,
                       n.report = 200,
                       n.burn = 10000,
                       n.thin = 10,
                       n.chains = 3)
----------------------------------------
    Preparing to run the model
----------------------------------------
----------------------------------------
    Model description
----------------------------------------
Latent Factor Multi-species Poisson Abundance
model fit with 95 sites and 6 species.

Samples per Chain: 20000 
Burn-in: 10000 
Thinning Rate: 10 
Number of Chains: 3 
Total Posterior Samples: 3000 

Using 3 latent factors.

Source compiled with OpenMP support and model fit using 1 thread(s).

Adaptive Metropolis with target acceptance rate: 43.0
----------------------------------------
    Chain 1
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     56.0        0.11837
    2   beta[1]     52.0        0.24318
    3   beta[1]     52.0        0.27418
    4   beta[1]     48.0        0.15047
    5   beta[1]     56.0        0.16301
    6   beta[1]     48.0        0.27972
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     48.0        0.12076
    2   beta[1]     44.0        0.23364
    3   beta[1]     52.0        0.28537
    4   beta[1]     36.0        0.16301
    5   beta[1]     52.0        0.16630
    6   beta[1]     32.0        0.29701
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.10498
    2   beta[1]     52.0        0.22901
    3   beta[1]     32.0        0.26875
    4   beta[1]     40.0        0.15351
    5   beta[1]     60.0        0.15661
    6   beta[1]     20.0        0.29113
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 2
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     44.0        0.11837
    2   beta[1]     40.0        0.22003
    3   beta[1]     44.0        0.26875
    4   beta[1]     60.0        0.16301
    5   beta[1]     48.0        0.15661
    6   beta[1]     52.0        0.26343
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     44.0        0.12076
    2   beta[1]     56.0        0.21568
    3   beta[1]     52.0        0.26875
    4   beta[1]     48.0        0.15661
    5   beta[1]     40.0        0.15978
    6   beta[1]     60.0        0.30302
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.11372
    2   beta[1]     52.0        0.24809
    3   beta[1]     36.0        0.24809
    4   beta[1]     56.0        0.15978
    5   beta[1]     52.0        0.15351
    6   beta[1]     52.0        0.29113
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 3
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     44.0        0.12320
    2   beta[1]     48.0        0.22448
    3   beta[1]     44.0        0.29113
    4   beta[1]     36.0        0.15978
    5   beta[1]     28.0        0.15978
    6   beta[1]     48.0        0.27972
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     24.0        0.11602
    2   beta[1]     48.0        0.21568
    3   beta[1]     40.0        0.27972
    4   beta[1]     32.0        0.15978
    5   beta[1]     24.0        0.15047
    6   beta[1]     36.0        0.27418
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     60.0        0.12076
    2   beta[1]     60.0        0.25310
    3   beta[1]     56.0        0.28537
    4   beta[1]     40.0        0.14171
    5   beta[1]     36.0        0.14749
    6   beta[1]     40.0        0.28537
-------------------------------------------------
Batch: 800 of 800, 100.00%
summary(out.lf.ms)

Call:
lfMsAbund(formula = ms.formula, data = bbsData, inits = ms.inits, 
    priors = ms.priors, tuning = ms.tuning, n.factors = n.factors, 
    n.batch = 800, batch.length = 25, family = "Poisson", n.omp.threads = 1, 
    verbose = TRUE, n.report = 200, n.burn = 10000, n.thin = 10, 
    n.chains = 3)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000
Run Time (min): 1.2829

----------------------------------------
    Community Level
----------------------------------------
Abundance Means (log scale): 
                   Mean     SD    2.5%     50%  97.5%   Rhat  ESS
(Intercept)     -0.9799 0.7056 -2.3172 -1.0055 0.5152 1.0093 1095
scale(bio2)      0.0839 0.2435 -0.4066  0.0860 0.5889 1.0417  532
scale(bio8)     -0.0945 0.2165 -0.4981 -0.1009 0.3462 1.0192  604
scale(bio18)    -0.3327 0.2556 -0.8635 -0.3241 0.1544 1.0369  477
scale(forest)    1.5110 0.3795  0.7594  1.5050 2.2540 1.0216  499
scale(devel)     0.0314 0.2374 -0.4451  0.0334 0.4936 1.0202  362
scale(day)      -0.2160 0.2190 -0.6250 -0.2195 0.2270 1.0070  423
I(scale(day)^2) -0.1566 0.1683 -0.4813 -0.1617 0.1756 1.0229  235
scale(tod)       0.1185 0.2716 -0.4154  0.1223 0.6586 1.0088  770

Abundance Variances (log scale): 
                  Mean     SD   2.5%    50%   97.5%   Rhat  ESS
(Intercept)     3.4601 3.7987 0.7774 2.4613 11.8784 1.0096 1805
scale(bio2)     0.2364 0.2980 0.0378 0.1553  0.8862 1.0157 1311
scale(bio8)     0.1509 0.1730 0.0274 0.0997  0.5521 1.0044 2160
scale(bio18)    0.2354 0.2636 0.0395 0.1557  0.9665 1.0073 1560
scale(forest)   0.7023 0.8921 0.0953 0.4564  2.7287 1.0716 1017
scale(devel)    0.1466 0.2046 0.0256 0.0917  0.5772 1.0265 2119
scale(day)      0.1513 0.1753 0.0279 0.1023  0.5761 1.0182 1973
I(scale(day)^2) 0.0965 0.1258 0.0209 0.0635  0.3726 1.0084 3000
scale(tod)      0.3243 0.4451 0.0500 0.2090  1.2518 1.0216 1832

Abundance Random Effect Variances (log scale): 
                  Mean     SD   2.5%    50% 97.5%   Rhat ESS
(Intercept)-obs 0.4942 0.1355 0.2699 0.4791 0.793 1.0374 276

----------------------------------------
    Species Level
----------------------------------------
Abundance (log scale): 
                        Mean     SD    2.5%     50%   97.5%   Rhat  ESS
(Intercept)-AMRE      1.0569 0.1973  0.6680  1.0577  1.4278 1.1873  182
(Intercept)-BLBW     -2.1404 0.4768 -3.1294 -2.1071 -1.3049 1.2388  142
(Intercept)-BTBW     -2.6169 0.5133 -3.7090 -2.5799 -1.6959 1.0849  174
(Intercept)-BTNW     -0.8948 0.3973 -1.8082 -0.8770 -0.1801 1.1179  103
(Intercept)-HOWA     -0.0497 0.2634 -0.6261 -0.0367  0.4295 1.0911  257
(Intercept)-MAWA     -2.4412 0.4759 -3.4712 -2.4286 -1.5654 1.0474  231
scale(bio2)-AMRE     -0.1476 0.1412 -0.4299 -0.1459  0.1219 1.0768  266
scale(bio2)-BLBW     -0.3013 0.2673 -0.8495 -0.2984  0.1927 1.0397  284
scale(bio2)-BTBW      0.3779 0.2666 -0.1215  0.3703  0.9347 1.0295  413
scale(bio2)-BTNW      0.1683 0.2126 -0.2497  0.1641  0.6155 1.0317  252
scale(bio2)-HOWA      0.0185 0.1832 -0.3252  0.0150  0.3867 1.0432  260
scale(bio2)-MAWA      0.3690 0.2675 -0.1329  0.3629  0.9024 1.0272  514
scale(bio8)-AMRE     -0.2003 0.1350 -0.4672 -0.2005  0.0622 1.0228  352
scale(bio8)-BLBW      0.0266 0.3085 -0.5425  0.0029  0.6797 1.0193  520
scale(bio8)-BTBW     -0.0435 0.2698 -0.5516 -0.0601  0.5315 1.0091  557
scale(bio8)-BTNW      0.0829 0.2394 -0.3681  0.0802  0.5765 1.0273  398
scale(bio8)-HOWA     -0.1375 0.1752 -0.4859 -0.1387  0.2024 1.0072  448
scale(bio8)-MAWA     -0.3206 0.2360 -0.7972 -0.3194  0.1362 1.0151 1089
scale(bio18)-AMRE    -0.0541 0.1403 -0.3372 -0.0529  0.2038 1.0482  289
scale(bio18)-BLBW    -0.7081 0.3167 -1.3759 -0.7029 -0.1241 1.0518  217
scale(bio18)-BTBW    -0.4370 0.2800 -1.0183 -0.4294  0.0800 1.0608  290
scale(bio18)-BTNW    -0.5702 0.2552 -1.1204 -0.5564 -0.1048 1.1276  197
scale(bio18)-HOWA    -0.1625 0.1759 -0.5137 -0.1538  0.1720 1.0723  421
scale(bio18)-MAWA    -0.1024 0.2523 -0.6086 -0.1012  0.3797 1.0140  614
scale(forest)-AMRE    0.8875 0.1673  0.5651  0.8838  1.2265 1.0116  294
scale(forest)-BLBW    1.8163 0.3895  1.0828  1.8125  2.6345 1.0985  209
scale(forest)-BTBW    2.3601 0.5163  1.4452  2.3152  3.4925 1.0402  165
scale(forest)-BTNW    1.6651 0.3101  1.0576  1.6640  2.3073 1.0426  124
scale(forest)-HOWA    0.9250 0.2093  0.5374  0.9203  1.3542 1.0084  442
scale(forest)-MAWA    1.8599 0.3863  1.1566  1.8311  2.6591 1.0333  226
scale(devel)-AMRE    -0.0239 0.1619 -0.3601 -0.0192  0.2902 1.0078  294
scale(devel)-BLBW    -0.0555 0.3063 -0.7012 -0.0390  0.4937 1.0123  325
scale(devel)-BTBW     0.0359 0.3142 -0.6111  0.0436  0.6427 1.0268  357
scale(devel)-BTNW     0.1613 0.2503 -0.3675  0.1669  0.6277 1.0552  202
scale(devel)-HOWA     0.1181 0.1786 -0.2559  0.1234  0.4574 1.0181  369
scale(devel)-MAWA    -0.0408 0.3294 -0.7745 -0.0155  0.5484 1.0027  804
scale(day)-AMRE      -0.2190 0.1529 -0.5372 -0.2169  0.0809 1.0039  245
scale(day)-BLBW      -0.1824 0.2641 -0.7011 -0.1829  0.3331 1.0070  269
scale(day)-BTBW      -0.1271 0.2653 -0.6289 -0.1233  0.4062 1.0358  403
scale(day)-BTNW      -0.1911 0.2325 -0.6281 -0.1929  0.2734 1.0069  237
scale(day)-HOWA      -0.5180 0.1964 -0.9124 -0.5120 -0.1309 1.0133  361
scale(day)-MAWA      -0.0814 0.2750 -0.6245 -0.0793  0.4554 1.0018  515
I(scale(day)^2)-AMRE -0.1542 0.1136 -0.3656 -0.1547  0.0771 1.0348  144
I(scale(day)^2)-BLBW -0.1513 0.1921 -0.5372 -0.1529  0.2181 1.0246  141
I(scale(day)^2)-BTBW -0.1210 0.1870 -0.4813 -0.1216  0.2493 1.0112  217
I(scale(day)^2)-BTNW -0.0854 0.1800 -0.4284 -0.0975  0.2893 1.0379  125
I(scale(day)^2)-HOWA -0.2408 0.1475 -0.5346 -0.2412  0.0397 1.0017  330
I(scale(day)^2)-MAWA -0.2173 0.1751 -0.5776 -0.2127  0.1161 1.0101  355
scale(tod)-AMRE      -0.0838 0.1402 -0.3478 -0.0897  0.1968 1.0296  225
scale(tod)-BLBW      -0.2272 0.2644 -0.7334 -0.2265  0.2855 1.0083  232
scale(tod)-BTBW       0.1416 0.2796 -0.3859  0.1347  0.7108 1.0064  762
scale(tod)-BTNW       0.0748 0.2260 -0.3721  0.0667  0.5287 1.0013  278
scale(tod)-HOWA       0.7714 0.2803  0.2709  0.7568  1.3580 1.0218  435
scale(tod)-MAWA       0.0570 0.2690 -0.4644  0.0560  0.5828 1.0061 1135

We see adequate convergence of most model parameters shown in the summary output. Note that the factor loadings themselves are not shown in the summary() output, but are available in the lambda.samples portion of the model list object.

# Rhats for lambda (the factor loadings)
out.lf.ms$rhat$lambda.lower.tri
 [1] 1.124701 1.176972 1.079710 1.024288 1.010535 1.188765 1.281021 1.149803
 [9] 1.077576 1.033295 1.078828 1.101105
# ESS for lambda
out.lf.ms$ESS$lambda
   AMRE-1    BLBW-1    BTBW-1    BTNW-1    HOWA-1    MAWA-1    AMRE-2    BLBW-2 
  0.00000 159.71893 160.25277 164.82549 220.78667 348.50176   0.00000   0.00000 
   BTBW-2    BTNW-2    HOWA-2    MAWA-2    AMRE-3    BLBW-3    BTBW-3    BTNW-3 
 96.37328  98.86019 123.77617 237.88031   0.00000   0.00000   0.00000 226.23541 
   HOWA-3    MAWA-3 
259.17024 201.55708 
# Posterior quantiles for the latent factor loadings
summary(out.lf.ms$lambda.samples)$quantiles
             2.5%         25%         50%        75%     97.5%
AMRE-1  1.0000000  1.00000000  1.00000000  1.0000000 1.0000000
BLBW-1  0.8837218  1.28189214  1.53530817  1.8196473 2.3032788
BTBW-1  0.2352498  0.68664321  0.91816178  1.1688200 1.6970022
BTNW-1  0.5772008  0.94035244  1.12555330  1.3173621 1.6856323
HOWA-1  0.2235111  0.52997083  0.68991460  0.8412379 1.1353611
MAWA-1 -0.7472806 -0.21612725  0.02917146  0.2710399 0.7736972
AMRE-2  0.0000000  0.00000000  0.00000000  0.0000000 0.0000000
BLBW-2  1.0000000  1.00000000  1.00000000  1.0000000 1.0000000
BTBW-2 -0.2707385  0.33839524  0.63676463  0.9373619 1.6378534
BTNW-2  0.2042814  0.57711300  0.78520556  0.9905208 1.4358069
HOWA-2 -0.7298074 -0.40865573 -0.22570190 -0.0251981 0.3970189
MAWA-2  0.3267504  0.79169811  1.01591567  1.2662355 1.8353836
AMRE-3  0.0000000  0.00000000  0.00000000  0.0000000 0.0000000
BLBW-3  0.0000000  0.00000000  0.00000000  0.0000000 0.0000000
BTBW-3  1.0000000  1.00000000  1.00000000  1.0000000 1.0000000
BTNW-3  0.1314306  0.41712356  0.56316714  0.7196401 1.0531968
HOWA-3  0.4312636  0.67833199  0.81053330  0.9400819 1.1920601
MAWA-3 -0.5232725  0.01551997  0.29003001  0.5886751 1.1479021

Notice the Rhat values are only reported for the elements of the factor loadings matrix that are estimated and not fixed at any specific value, while the ESS values are reported for all elements of the factor loadings matrix, and take value 0 for those parameters that are fixed for identifiability purposes. We can inspect the latent factor loadings as well as the latent factors (stored in out.lf.ms$w.samples) to provide information on any groupings that arise from the species in the modeled community. See Hui et al. (2015) for a discussion on using factor models as a model-based ordination technique.

Posterior predictive checks

We again use the ppcAbund() function to perform a posterior predictive check of our model

ppc.out.lf.ms <- ppcAbund(out.lf.ms, fit.stat = 'freeman-tukey', group = 0)
Currently on species 1 out of 6
Currently on species 2 out of 6
Currently on species 3 out of 6
Currently on species 4 out of 6
Currently on species 5 out of 6
Currently on species 6 out of 6
# Summarize with a Bayesian p-value
summary(ppc.out.lf.ms)

Call:
ppcAbund(object = out.lf.ms, fit.stat = "freeman-tukey", group = 0)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000

----------------------------------------
    Community Level
----------------------------------------
Bayesian p-value:  0.4148 

----------------------------------------
    Species Level
----------------------------------------
 Bayesian p-value: 0.5793
 Bayesian p-value: 0.448
 Bayesian p-value: 0.3737
 Bayesian p-value: 0.521
 Bayesian p-value: 0.231
 Bayesian p-value: 0.3357
Fit statistic:  freeman-tukey 

Model selection using WAIC

We can use waicAbund() to calculate the WAIC for comparison to other models. Below, we compare the multivariate GLMM to the latent factor multivariate GLMM.

# With latent factors
waicAbund(out.lf.ms, by.sp = TRUE)
        elpd       pD     WAIC
1 -164.37320 40.49137 409.7291
2  -59.97490 19.67621 159.3022
3  -53.98120 18.71476 145.3919
4  -95.10316 26.40511 243.0165
5 -118.14175 36.68752 309.6585
6  -51.54636 18.49930 140.0913
# Without latent factors
waicAbund(out.ms, by.sp = TRUE)
        elpd       pD     WAIC
1 -176.90388 44.44421 442.6962
2  -73.54415 30.31010 207.7085
3  -58.77351 20.92335 159.3937
4 -105.63397 31.56057 274.3891
5 -136.42839 52.48398 377.8247
6  -56.44645 20.93304 154.7590

We see substantial improvements in WAIC for the multivariate GLMM that does account for correlations between species (using the factor modeling approach) relative to one that ignores the correlations.

Prediction

Prediction proceeds exactly as before with the multivariate GLMM

out.ms.pred <- predict(out.lf.ms, X.0, coords.0, ignore.RE = TRUE)
# Look at the resulting object
str(out.ms.pred)
List of 4
 $ mu.0.samples: num [1:3000, 1:6, 1:816, 1] 1.45 3.96 3.8 2.64 16.84 ...
 $ y.0.samples : int [1:3000, 1:6, 1:816, 1] 0 5 3 2 16 21 39 9 6 6 ...
 $ w.0.samples : num [1:3000, 1:3, 1:816] -1.84 -1 -1.09 -1.55 0.21 ...
 $ call        : language predict.lfMsAbund(object = out.lf.ms, X.0 = X.0, coords.0 = coords.0, ignore.RE = TRUE)
 - attr(*, "class")= chr "predict.lfMsAbund"
mu.sum.samples <- apply(out.ms.pred$mu.0.samples, c(1, 3), sum)
# Average total abundance at each site
mu.sum.means <- apply(mu.sum.samples, 2, mean)
plot.df <- data.frame(Easting = bbsPredData$x,
                      Northing = bbsPredData$y,
                      mu.sum.means = mu.sum.means)
coords.stars <- st_as_stars(plot.df, crs = my.crs)
# Plot of total relative abundance
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.sum.means)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Relative abundance of six warbler species')

Spatial factor multivariate GLMMs

Basic model description

Our final, and most complex, GLMM that we fit in spAbundance is a multivariate spatially-explicit GLMM. This model is nearly identical to the latent factor multivariate GLMM, except we give a spatial structure to the latent factors instead of assuming they are independent from each other. By modeling the latent factors with a spatial structure, we will often see such a model have improved predictive performance relative to a latent factor multivariate GLMM (Thorson et al. 2015; Ovaskainen et al. 2016; Doser, Finley, and Banerjee 2023). This model again is an abundance-based JSDM, but now it simultaneously accounts for residual correlations between species and spatial autocorrelation. The model we present below is a direct extension of the Gaussian spatial factor NNGP model of Taylor-Rodriguez et al. (2019), where we now allow for the response to be Poisson or negative binomial (in addition to Gaussian).

The model is identical to the previously described latent factor multivariate GLMM with the exception that the latent factors are assumed to have a spatial structure to them. More specifically, our model for species-specific relative abundance is again

\[\begin{equation} g(\mu_{i}(\boldsymbol{s}_j)) = \boldsymbol{x}(\boldsymbol{s}_j)^\top\boldsymbol{\beta}_i + \text{w}^\ast_{i}(\boldsymbol{s}_j). \end{equation}\]

Note again that for spatial models we use the notation \(\boldsymbol{s}_j\) to make it clear that the model is spatially-explicit and relies upon the coordinates (\(\boldsymbol{s}_j\)) at each site \(j\) to estimate this spatial pattern. As with the latent factor model, we decompose \(\text{w}^\ast_i(\boldsymbol{s}_j)\) into a linear combination of \(q\) latent variables (i.e., factors) and their associated species-specific coefficients (i.e., factor loadings) according to

\[\begin{equation} \text{w}^\ast_{i}(\boldsymbol{s}_j) = \boldsymbol{\lambda}_i^\top\textbf{w}(\boldsymbol{s}_j). \end{equation}\]

Now, instead of modeling \(\textbf{w}(\boldsymbol{s}_j)\) as independent normal latent variables, we assume \(\textbf{w}(\boldsymbol{s}_j)\) arise from spatial processes, allowing us to account for both residual species correlations and residual spatial autocorrelation in species-specific abundance. More specifically, each spatial factor \(\textbf{w}_r\) for each \(r = 1, \dots, q\) is modeled using a Nearest Neighbor Gaussian Process (Datta et al. 2016), i.e.,

\[\begin{equation} \textbf{w}_r \sim \text{Normal}(\boldsymbol{0}, \tilde{\boldsymbol{C}}_r(\boldsymbol{\theta}_r)), \end{equation}\]

where \(\tilde{\boldsymbol{C}}_r(\boldsymbol{\theta}_r)\) is the NNGP-derived correlation matrix for the \(r^{\text{th}}\) spatial factor. Note that the spatial variance parameter for each spatial factor is assumed to be 1 for identifiability purposes. The vector \(\boldsymbol{\theta}_r\) consists of parameters governing the spatial process for each spatial factor according to some spatial correlation function, as we saw with the single-species GLMM. Thus, for the exponential, spherical, and Gaussian correlation functions, \(\boldsymbol{\theta}_r\) includes a spatial decay parameter \(\phi_r\), while the Matern correlation function includes an additional spatial smoothness parameter, \(\nu_r\).

We assume the same priors and identifiability constraints as the latent factor GLMM. We assign a uniform prior for the spatial decay parameter, \(\phi_r\), and the spatial smoothness parameters, \(\nu_r\), if using a Matern correlation function.

Notice that this spatial factor modeling approach is the only approach we implement in spAbundance for modeling multi-species datasets while accounting for spatial autocorrelation. While we could envision fitting a separate spatial random effect for each species in our multi-species data set (as we implemented in spOccupancy in the spMsPGOcc function), we prefer (and recommend) using the spatial factor modeling approach as (1) it is far more computationally efficient than fitting a separate spatial effect for each species; (2) it explicitly acknowledges dependence between species; (3) estimating a separate spatial effect for each species would be very difficult to do with multiple rare species in the data set; and (4) even if the species are independent (i.e., there are no residual correlations), the spatial factor modeling approach performs extremely similarly to a model with a separate spatial process for each species (Doser, Finley, and Banerjee 2023), while still being substantially faster.

Fitting spatial factor multivariate GLMMs with sfMsAbund()

The function sfMsAbund() fits spatial factor multivariate GLMMs in spAbundance. The arguments are very similar to lfMsAbund() and spAbund().

sfMsAbund(formula, data, inits, priors,  
          tuning, cov.model = 'exponential', NNGP = TRUE, 
          n.neighbors = 15, search.type = 'cb', n.factors,
          n.batch, batch.length, accept.rate = 0.43, family = 'Poisson',
          n.omp.threads = 1, verbose = TRUE, n.report = 100, 
          n.burn = round(.10 * n.batch * batch.length), n.thin = 1, n.chains = 1,
          save.fitted = TRUE, ...)

We will again fit the model with three spatial factors, and will use the same covariates/random effects on abundance and detection as we have done throughout the vignette

n.factors <- 3
ms.formula <- ~ scale(bio2) + scale(bio8) + scale(bio18) + scale(forest) +
                scale(devel) + scale(day) + I(scale(day)^2) + scale(tod) +
                (1 | obs)

Initial values are identical to what we saw with lfMsAbund(), but we will also specify the initial values for the spatial decay parameters for each spatial factor (we use an exponential correlation model so we do not need to specify any initial values for nu, the spatial smoothness parameter used when cov.model = 'matern'.

# Number of species
n.sp <- dim(bbsData$y)[1]
# Initiate all lambda initial values to 0.
lambda.inits <- matrix(0, n.sp, n.factors)
# Set diagonal elements to 1
diag(lambda.inits) <- 1
# Set lower triangular elements to random values from a standard normal distribution
lambda.inits[lower.tri(lambda.inits)] <- rnorm(sum(lower.tri(lambda.inits)))
# Check it out.
lambda.inits
           [,1]        [,2]       [,3]
[1,]  1.0000000  0.00000000  0.0000000
[2,] -0.2052814  1.00000000  0.0000000
[3,]  2.0460323  0.72926132  1.0000000
[4,]  0.4795688 -0.65842958 -0.1561537
[5,] -1.0064583 -0.06655202 -1.0984774
[6,] -1.7288239  0.32197098  0.3567728
w.inits <- matrix(0, n.factors, ncol(bbsData$y))
# Pair-wise distances between all sites
dist.mat <- dist(dataNMixSim$coords)
# Exponential covariance model
cov.model <- 'exponential'
ms.inits <- list(beta.comm = 0, beta = 0, tau.sq.beta = 1,
                 lambda = lambda.inits, kappa = 1, phi = 3 / mean(dist.mat),
                 w = w.inits, sigma.sq.mu = 0.5)

Priors are the same as with the latent factor multivariate GLMM, where we also add in our default prior for the spatial decay parameters, which allows the effective spatial range of each spatial factor to range from the maximum intersite distance to the minimum intersite distance. Notice the prior for phi is now specified as a list as opposed to an atomic vector as we did for the single-species case. If desired, the list format allows you to easily specify a different prior for each of the spatial factors. See ?sfMsAbund for more details.

max.dist <- max(dist.mat)
min.dist <- min(dist.mat)
ms.priors <- list(beta.comm.normal = list(mean = 0, var = 2.72),
                  tau.sq.beta.ig = list(a = 0.1, b = 0.1),
                  sigma.sq.mu.ig = list(a = 0.1, b = 0.1),
                  kappa.unif = list(a = 0, b = 100),
                  phi.unif = list(3 / max.dist, 3 / min.dist))

We lastly specify the tuning values and run the model for 20,000 iterations with a burn-in of 10,000 samples and a thinning rate of 10, for each of 3 chains, yielding a total of 3000 posterior samples. As before, we will use a Poisson distribution and 15 nearest neighbors.

# Specify initial tuning values
ms.tuning <- list(beta = 0.3, beta.star = 0.5, kappa = 0.5,
                  w = 0.5, lambda = 0.5, phi = 0.5)
# Approx. run time: ~4.5 min
out.sf.ms <- sfMsAbund(formula = ms.formula,
                      data = bbsData,
                      inits = ms.inits,
                      n.batch = 800,
                      n.factors = n.factors,
                      family = 'Poisson',
                      tuning = ms.tuning,
                      batch.length = 25,
                      priors = ms.priors,
                      n.neighbors = 15,
                      n.omp.threads = 1,
                      verbose = TRUE,
                      n.report = 200,
                      n.burn = 10000,
                      n.thin = 10,
                      n.chains = 3)
----------------------------------------
    Preparing to run the model
----------------------------------------
----------------------------------------
    Building the neighbor list
----------------------------------------
----------------------------------------
Building the neighbors of neighbors list
----------------------------------------
----------------------------------------
    Model description
----------------------------------------
Spatial Factor NNGP Multi-species Poisson Abundance
model fit with 95 sites and 6 species.

Samples per Chain: 20000 
Burn-in: 10000 
Thinning Rate: 10 
Number of Chains: 3 
Total Posterior Samples: 3000 

Using the exponential spatial correlation model.

Using 3 latent spatial factors.
Using 15 nearest neighbors.

Source compiled with OpenMP support and model fit using 1 thread(s).

Adaptive Metropolis with target acceptance rate: 43.0
----------------------------------------
    Chain 1
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     36.0        0.10927
    2   beta[1]     28.0        0.23364
    3   beta[1]     40.0        0.26343
    4   beta[1]     44.0        0.15978
    5   beta[1]     48.0        0.15351
    6   beta[1]     52.0        0.30302
    1   phi     48.0        3.44476
    2   phi     44.0        3.11694
    3   phi     44.0        3.37654
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     52.0        0.11147
    2   beta[1]     40.0        0.21568
    3   beta[1]     48.0        0.26875
    4   beta[1]     52.0        0.16966
    5   beta[1]     32.0        0.15351
    6   beta[1]     48.0        0.28537
    1   phi     56.0        4.20743
    2   phi     24.0        4.37914
    3   phi     32.0        3.96241
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     52.0        0.11602
    2   beta[1]     56.0        0.23364
    3   beta[1]     28.0        0.26875
    4   beta[1]     36.0        0.16301
    5   beta[1]     24.0        0.14749
    6   beta[1]     32.0        0.29701
    1   phi     40.0        4.12412
    2   phi     52.0        4.37914
    3   phi     20.0        4.29243
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 2
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     28.0        0.11372
    2   beta[1]     56.0        0.23364
    3   beta[1]     40.0        0.27418
    4   beta[1]     36.0        0.15978
    5   beta[1]     44.0        0.15351
    6   beta[1]     40.0        0.30914
    1   phi     32.0        4.04246
    2   phi     20.0        4.20743
    3   phi     36.0        4.64993
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.11837
    2   beta[1]     36.0        0.21141
    3   beta[1]     44.0        0.25821
    4   beta[1]     52.0        0.15351
    5   beta[1]     52.0        0.15661
    6   beta[1]     20.0        0.28537
    1   phi     28.0        4.20743
    2   phi     32.0        4.12412
    3   phi     28.0        4.46761
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     44.0        0.12320
    2   beta[1]     48.0        0.22901
    3   beta[1]     44.0        0.26343
    4   beta[1]     40.0        0.17308
    5   beta[1]     40.0        0.16301
    6   beta[1]     40.0        0.28537
    1   phi     36.0        4.46761
    2   phi     56.0        4.64993
    3   phi     32.0        4.29243
-------------------------------------------------
Batch: 800 of 800, 100.00%
----------------------------------------
    Chain 3
----------------------------------------
Sampling ... 
Batch: 200 of 800, 25.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     24.0        0.11147
    2   beta[1]     48.0        0.24318
    3   beta[1]     44.0        0.25821
    4   beta[1]     44.0        0.15661
    5   beta[1]     44.0        0.14749
    6   beta[1]     36.0        0.30302
    1   phi     28.0        4.83970
    2   phi     36.0        4.20743
    3   phi     28.0        4.04246
-------------------------------------------------
Batch: 400 of 800, 50.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     52.0        0.12076
    2   beta[1]     36.0        0.21568
    3   beta[1]     48.0        0.29701
    4   beta[1]     36.0        0.15351
    5   beta[1]     40.0        0.15047
    6   beta[1]     32.0        0.27418
    1   phi     36.0        4.04246
    2   phi     52.0        4.20743
    3   phi     56.0        4.37914
-------------------------------------------------
Batch: 600 of 800, 75.00%
    Number  Parameter   Acceptance  Tuning
    1   beta[1]     40.0        0.11602
    2   beta[1]     44.0        0.25821
    3   beta[1]     44.0        0.26343
    4   beta[1]     44.0        0.16966
    5   beta[1]     36.0        0.15047
    6   beta[1]     44.0        0.29701
    1   phi     52.0        4.37914
    2   phi     56.0        4.64993
    3   phi     52.0        4.29243
-------------------------------------------------
Batch: 800 of 800, 100.00%
summary(out.sf.ms)

Call:
sfMsAbund(formula = ms.formula, data = bbsData, inits = ms.inits, 
    priors = ms.priors, tuning = ms.tuning, n.neighbors = 15, 
    n.factors = n.factors, n.batch = 800, batch.length = 25, 
    family = "Poisson", n.omp.threads = 1, verbose = TRUE, n.report = 200, 
    n.burn = 10000, n.thin = 10, n.chains = 3)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000
Run Time (min): 4.0584

----------------------------------------
    Community Level
----------------------------------------
Abundance Means (log scale): 
                   Mean     SD    2.5%     50%  97.5%   Rhat  ESS
(Intercept)     -0.9941 0.6625 -2.2616 -1.0119 0.3919 1.0135 1422
scale(bio2)      0.0744 0.2392 -0.3915  0.0725 0.5524 1.0111  643
scale(bio8)     -0.1279 0.2271 -0.5686 -0.1282 0.3237 1.0104  504
scale(bio18)    -0.3034 0.2508 -0.8494 -0.2943 0.1554 1.0268  376
scale(forest)    1.5274 0.4029  0.6999  1.5230 2.3137 1.0166  530
scale(devel)     0.0460 0.2333 -0.4272  0.0470 0.4984 1.0247  330
scale(day)      -0.1892 0.2280 -0.6176 -0.1872 0.2483 1.0275  379
I(scale(day)^2) -0.1490 0.1702 -0.4799 -0.1453 0.1734 1.0476  310
scale(tod)       0.1446 0.2622 -0.3756  0.1481 0.6601 1.0151  671

Abundance Variances (log scale): 
                  Mean     SD   2.5%    50%   97.5%   Rhat  ESS
(Intercept)     3.6908 4.2739 0.8439 2.5864 13.1504 1.0156 3000
scale(bio2)     0.2419 0.3084 0.0396 0.1591  0.9303 1.0234 2121
scale(bio8)     0.1508 0.1878 0.0269 0.0993  0.5820 1.0213 2495
scale(bio18)    0.2313 0.3300 0.0353 0.1474  0.9063 1.0136 1986
scale(forest)   0.7704 0.9680 0.1048 0.5075  3.1445 1.0194  750
scale(devel)    0.1417 0.1656 0.0262 0.0928  0.5672 1.0104 2572
scale(day)      0.1564 0.2093 0.0283 0.1031  0.5728 1.0089 2814
I(scale(day)^2) 0.0938 0.0990 0.0203 0.0650  0.3438 1.0013 1839
scale(tod)      0.3075 0.3880 0.0484 0.2014  1.2236 1.0285 1761

Abundance Random Effect Variances (log scale): 
                  Mean    SD   2.5%    50%  97.5%   Rhat ESS
(Intercept)-obs 0.4984 0.136 0.2705 0.4867 0.7942 1.0099 233

----------------------------------------
    Species Level
----------------------------------------
Abundance (log scale): 
                        Mean     SD    2.5%     50%   97.5%   Rhat  ESS
(Intercept)-AMRE      1.0453 0.2034  0.6581  1.0381  1.4627 1.1831  203
(Intercept)-BLBW     -2.2258 0.4478 -3.1038 -2.2104 -1.3874 1.0586  192
(Intercept)-BTBW     -2.6145 0.4923 -3.6654 -2.5839 -1.7219 1.0118  199
(Intercept)-BTNW     -0.8807 0.3389 -1.5834 -0.8696 -0.2747 1.0819  149
(Intercept)-HOWA     -0.0607 0.2613 -0.6113 -0.0493  0.4283 1.0837  247
(Intercept)-MAWA     -2.4806 0.4867 -3.4444 -2.4566 -1.6433 1.0196  220
scale(bio2)-AMRE     -0.1529 0.1386 -0.4336 -0.1523  0.1127 1.0683  331
scale(bio2)-BLBW     -0.3212 0.2714 -0.8722 -0.3164  0.1908 1.0255  333
scale(bio2)-BTBW      0.3762 0.2575 -0.1038  0.3738  0.8898 1.0079  421
scale(bio2)-BTNW      0.1646 0.2052 -0.2245  0.1563  0.5864 1.0103  350
scale(bio2)-HOWA      0.0113 0.1767 -0.3276  0.0083  0.3622 1.0252  416
scale(bio2)-MAWA      0.3817 0.2542 -0.0905  0.3735  0.8982 1.0171  521
scale(bio8)-AMRE     -0.2247 0.1472 -0.5064 -0.2243  0.0771 1.0276  349
scale(bio8)-BLBW     -0.0127 0.3008 -0.5748 -0.0214  0.6044 1.0366  406
scale(bio8)-BTBW     -0.0733 0.2847 -0.6100 -0.0866  0.5316 1.0043  471
scale(bio8)-BTNW      0.0529 0.2473 -0.4067  0.0455  0.5585 1.0054  371
scale(bio8)-HOWA     -0.1678 0.1745 -0.5099 -0.1671  0.1700 1.0291  460
scale(bio8)-MAWA     -0.3408 0.2360 -0.8111 -0.3351  0.1064 1.0025  803
scale(bio18)-AMRE    -0.0313 0.1434 -0.3274 -0.0256  0.2384 1.1363  260
scale(bio18)-BLBW    -0.6725 0.3191 -1.3477 -0.6602 -0.1033 1.0610  199
scale(bio18)-BTBW    -0.4044 0.2818 -0.9793 -0.3936  0.1151 1.0476  261
scale(bio18)-BTNW    -0.5305 0.2512 -1.0743 -0.5205 -0.0628 1.0656  193
scale(bio18)-HOWA    -0.1305 0.1697 -0.4663 -0.1281  0.1944 1.0301  404
scale(bio18)-MAWA    -0.0950 0.2480 -0.5741 -0.0998  0.4085 1.0118  627
scale(forest)-AMRE    0.8863 0.1690  0.5635  0.8854  1.2223 1.0430  180
scale(forest)-BLBW    1.8806 0.4348  1.0972  1.8476  2.8041 1.0632  160
scale(forest)-BTBW    2.4183 0.5048  1.4952  2.3831  3.4350 1.0275  178
scale(forest)-BTNW    1.7005 0.3049  1.1331  1.6924  2.3311 1.0702  162
scale(forest)-HOWA    0.9241 0.2099  0.5381  0.9168  1.3647 1.0078  427
scale(forest)-MAWA    1.9217 0.4107  1.1915  1.9064  2.7646 1.0723  216
scale(devel)-AMRE    -0.0146 0.1560 -0.3372 -0.0127  0.2841 1.0888  311
scale(devel)-BLBW    -0.0365 0.3077 -0.6669 -0.0231  0.5415 1.0213  310
scale(devel)-BTBW     0.0492 0.2931 -0.5267  0.0485  0.6051 1.0319  444
scale(devel)-BTNW     0.1787 0.2437 -0.2882  0.1865  0.6396 1.0208  231
scale(devel)-HOWA     0.1172 0.1742 -0.2275  0.1147  0.4815 1.0276  341
scale(devel)-MAWA    -0.0246 0.3337 -0.7731 -0.0046  0.5872 1.0101  989
scale(day)-AMRE      -0.1974 0.1569 -0.5083 -0.1943  0.1076 1.0696  263
scale(day)-BLBW      -0.1577 0.2708 -0.6928 -0.1534  0.3611 1.0583  269
scale(day)-BTBW      -0.0822 0.2640 -0.5805 -0.0912  0.4494 1.0140  458
scale(day)-BTNW      -0.1494 0.2181 -0.5617 -0.1473  0.2940 1.0325  254
scale(day)-HOWA      -0.4928 0.1854 -0.8696 -0.4883 -0.1372 1.0227  451
scale(day)-MAWA      -0.0511 0.2734 -0.5776 -0.0537  0.4987 1.0105  443
I(scale(day)^2)-AMRE -0.1431 0.1130 -0.3692 -0.1413  0.0726 1.0941  207
I(scale(day)^2)-BLBW -0.1290 0.1954 -0.5267 -0.1266  0.2317 1.1372  155
I(scale(day)^2)-BTBW -0.1126 0.1854 -0.4706 -0.1105  0.2614 1.0294  288
I(scale(day)^2)-BTNW -0.0687 0.1564 -0.3867 -0.0718  0.2316 1.0632  190
I(scale(day)^2)-HOWA -0.2316 0.1491 -0.5294 -0.2301  0.0622 1.0345  389
I(scale(day)^2)-MAWA -0.2114 0.1833 -0.5773 -0.2139  0.1476 1.0129  417
scale(tod)-AMRE      -0.0574 0.1334 -0.3267 -0.0570  0.2003 1.0369  231
scale(tod)-BLBW      -0.1945 0.2616 -0.7051 -0.1869  0.3121 1.0206  213
scale(tod)-BTBW       0.1654 0.2829 -0.3820  0.1583  0.7407 1.0062  635
scale(tod)-BTNW       0.1124 0.2209 -0.3197  0.1056  0.5561 1.0106  372
scale(tod)-HOWA       0.7839 0.2671  0.3043  0.7686  1.3366 1.0065  626
scale(tod)-MAWA       0.0705 0.2700 -0.4384  0.0681  0.6356 1.0011 1111

----------------------------------------
    Spatial Covariance
----------------------------------------
         Mean      SD   2.5%     50%   97.5%   Rhat  ESS
phi-1 21.8270 11.6493 3.0767 21.5886 41.0746 1.0009 3000
phi-2 22.1304 11.4875 3.0306 21.8373 40.9826 1.0039 3000
phi-3 22.2394 11.4432 3.2527 22.1574 41.1147 1.0019 3000

Posterior predictive checks

As before, we can use the ppcAbund() function to perform a posterior predictive check of our model

ppc.out.sf.ms <- ppcAbund(out.sf.ms, fit.stat = 'freeman-tukey', group = 0)
Currently on species 1 out of 6
Currently on species 2 out of 6
Currently on species 3 out of 6
Currently on species 4 out of 6
Currently on species 5 out of 6
Currently on species 6 out of 6
# Summarize with a Bayesian p-value
summary(ppc.out.sf.ms)

Call:
ppcAbund(object = out.sf.ms, fit.stat = "freeman-tukey", group = 0)

Samples per Chain: 20000
Burn-in: 10000
Thinning Rate: 10
Number of Chains: 3
Total Posterior Samples: 3000

----------------------------------------
    Community Level
----------------------------------------
Bayesian p-value:  0.4164 

----------------------------------------
    Species Level
----------------------------------------
 Bayesian p-value: 0.5773
 Bayesian p-value: 0.4643
 Bayesian p-value: 0.3903
 Bayesian p-value: 0.5067
 Bayesian p-value: 0.2347
 Bayesian p-value: 0.3253
Fit statistic:  freeman-tukey 

Model selection using WAIC

We use waicAbund() to calculate the WAIC for all species in the data set, and compare the WAIC to that obtained using the non-spatial latent factor GLMM.

# Non-spatial latent factor model
waicAbund(out.lf.ms, by.sp = FALSE)
     elpd        pD      WAIC 
-543.1206  160.4743 1407.1897 
# Spatial factor model
waicAbund(out.sf.ms, by.sp = FALSE)
     elpd        pD      WAIC 
-543.4398  159.1499 1405.1793 

Prediction

Prediction proceeds exactly as we have seen with all previous models.

out.ms.pred <- predict(out.sf.ms, X.0, coords.0, ignore.RE = TRUE,
                       n.report = 100, verbose = TRUE)
----------------------------------------
    Prediction description
----------------------------------------
Spatial Factor NNGP GLMM with 95 observations.

Number of covariates 9 (including intercept if specified).

Using the exponential spatial correlation model.

Using 15 nearest neighbors.
Using 3 latent spatial factors.

Number of MCMC samples 3000.

Predicting at 816 non-sampled locations.


Source compiled with OpenMP support and model fit using 1 threads.
-------------------------------------------------
        Predicting
-------------------------------------------------
Location: 100 of 816, 12.25%
Location: 200 of 816, 24.51%
Location: 300 of 816, 36.76%
Location: 400 of 816, 49.02%
Location: 500 of 816, 61.27%
Location: 600 of 816, 73.53%
Location: 700 of 816, 85.78%
Location: 800 of 816, 98.04%
Location: 816 of 816, 100.00%
Generating abundance predictions
# Look at the resulting object
str(out.ms.pred)
List of 6
 $ y.0.samples : num [1:3000, 1:6, 1:816, 1] 25 3 32 39 14 13 5 12 23 3 ...
 $ w.0.samples : num [1:3000, 1:3, 1:816] 0.723 -0.745 1.303 1.945 0.632 ...
 $ mu.0.samples: num [1:3000, 1:6, 1:816, 1] 22.46 4.42 28.69 42.11 16.28 ...
 $ run.time    : 'proc_time' Named num [1:5] 70.9 124.9 50.7 0 0
  ..- attr(*, "names")= chr [1:5] "user.self" "sys.self" "elapsed" "user.child" ...
 $ call        : language predict.sfMsAbund(object = out.sf.ms, X.0 = X.0, coords.0 = coords.0, verbose = TRUE,      n.report = 100, ignore.RE = TRUE)
 $ object.class: chr "sfMsAbund"
 - attr(*, "class")= chr "predict.sfMsAbund"
mu.sum.samples <- apply(out.ms.pred$mu.0.samples, c(1, 3), sum)
# Average total abundance at each site
mu.sum.means <- apply(mu.sum.samples, 2, mean)
plot.df <- data.frame(Easting = bbsPredData$x,
                      Northing = bbsPredData$y,
                      mu.sum.means = mu.sum.means)
coords.stars <- st_as_stars(plot.df, crs = my.crs)
# Plot of total relative abundance
ggplot() +
  geom_stars(data = coords.stars, aes(x = Easting, y = Northing, fill = mu.sum.means)) +
  geom_sf(data = coords.sf, col = 'grey') +
  scale_fill_viridis_c(na.value = NA) +
  theme_bw(base_size = 12) +
  labs(fill = '', x = 'Longitude', y = 'Latitude', 
       title = 'Relative abundance of six warbler species')

References

Banerjee, Sudipto, Bradley P Carlin, and Alan E Gelfand. 2003. Hierarchical Modeling and Analysis for Spatial Data. Chapman; Hall/CRC.
Bates, Douglas, Martin Mächler, Ben Bolker, and Steve Walker. 2015. “Fitting Linear Mixed-Effects Models Using lme4.” Journal of Statistical Software 67 (1): 1–48. https://doi.org/10.18637/jss.v067.i01.
Brooks, Stephen P., and Andrew Gelman. 1998. “General Methods for Monitoring Convergence of Iterative Simulations.” Journal of Computational and Graphical Statistics 7 (4): 434–55.
Datta, Abhirup, Sudipto Banerjee, Andrew O Finley, and Alan E Gelfand. 2016. “Hierarchical Nearest-Neighbor Gaussian Process Models for Large Geostatistical Datasets.” Journal of the American Statistical Association 111 (514): 800–812.
Diggle, Peter J. 1998. “Model-Based Geostatistics.” Journal of the Royal Statistical Society Series C: Applied Statistics 47 (3): 299–350.
Doser, Jeffrey W, Andrew O Finley, and Sudipto Banerjee. 2023. “Joint Species Distribution Models with Imperfect Detection for High-Dimensional Spatial Data.” Ecology 104 (9): e4137.
Doser, Jeffrey W, Andrew O Finley, Marc Kéry, and Elise F Zipkin. 2022. spOccupancy: An R package for single-species, multi-species, and integrated spatial occupancy models.” Methods in Ecology and Evolution 13 (8): 1670–78.
Finley, Andrew O, Abhirup Datta, and Sudipto Banerjee. 2020. “spNNGP r Package for Nearest Neighbor Gaussian Process Models.” arXiv Preprint arXiv:2001.09111.
Finley, Andrew O, Abhirup Datta, Bruce D Cook, Douglas C Morton, Hans E Andersen, and Sudipto Banerjee. 2019. “Efficient Algorithms for Bayesian Nearest Neighbor Gaussian Processes.” Journal of Computational and Graphical Statistics 28 (2): 401–14.
Gelman, Andrew. 2006. “Prior Distributions for Variance Parameters in Hierarchical Models (Comment on Article by Browne and Draper).” Bayesian Analysis 1 (3): 515–34.
Guélat, Jérôme, and Marc Kéry. 2018. “Effects of Spatial Autocorrelation and Imperfect Detection on Species Distribution Models.” Methods in Ecology and Evolution 9 (6): 1614–25.
Hobbs, N Thompson, and Mevin B Hooten. 2015. Bayesian Models. Princeton University Press.
Hui, Francis KC, Sara Taskinen, Shirley Pledger, Scott D Foster, and David I Warton. 2015. “Model-Based Approaches to Unconstrained Ordination.” Methods in Ecology and Evolution 6 (4): 399–411.
Link, William A, and John R Sauer. 2002. “A Hierarchical Analysis of Population Change with Application to Cerulean Warblers.” Ecology 83 (10): 2832–40.
Lopes, Hedibert Freitas, and Mike West. 2004. “Bayesian Model Assessment in Factor Analysis.” Statistica Sinica, 41–67.
Lunn, David, Christopher Jackson, Nicky Best, Andrew Thomas, and David Spiegelhalter. 2013. “The BUGS Book: A Practical Introduction to Bayesian Analysis.”
Ovaskainen, Otso, David B Roy, Richard Fox, and Barbara J Anderson. 2016. “Uncovering Hidden Spatial Structure in Species Communities with Spatially Explicit Joint Species Distribution Models.” Methods in Ecology and Evolution 7 (4): 428–36.
Pardieck, K. L., D. J. Ziolkowski Jr, M. Lutmerding, V. I. Aponte, and M-A.R. Hudson. 2020. North American Breeding Bird Survey Dataset 1966–2019.” U.S. Geological Survey Data Release, Https://Doi.org/10.5066/P9J6QUF6.
Roberts, Gareth O, and Jeffrey S Rosenthal. 2009. “Examples of Adaptive MCMC.” Journal of Computational and Graphical Statistics 18 (2): 349–67.
Taylor-Rodriguez, Daniel, Andrew O Finley, Abhirup Datta, Chad Babcock, Hans-Erik Andersen, Bruce D Cook, Douglas C Morton, and Sudipto Banerjee. 2019. Spatial factor models for high-dimensional and large spatial data: An application in forest variable mapping.” Statistica Sinica 29: 1155.
Thorson, James T, Mark D Scheuerell, Andrew O Shelton, Kevin E See, Hans J Skaug, and Kasper Kristensen. 2015. “Spatial Factor Analysis: A New Tool for Estimating Joint Species Distributions and Correlations in Species Range.” Methods in Ecology and Evolution 6 (6): 627–37.
Warton, David I, F Guillaume Blanchet, Robert B O’Hara, Otso Ovaskainen, Sara Taskinen, Steven C Walker, and Francis KC Hui. 2015. “So Many Variables: Joint Modeling in Community Ecology.” Trends in Ecology & Evolution 30 (12): 766–79.