Chapter 3 Use case 2: jointModel() with site-level covariates

This second use case uses the same goby data as in use case 1, except this time we will include site-level covariates that affect the sensitivity of eDNA relative to traditional surveys.



library(eDNAjoint)
data(gobyData)

In addition to count and qPCR data, the goby data includes site-level covariates, which is optional when implementing jointModel(). Here, the data represent salinity, mean time to filter eDNA samples, density of other fish, habitat size, and vegetation presence at each site. Two important notes:

  1. Notice that the continuous covariate data is standardized. This is useful since this data will be used in a linear regression. Similarly, one should use dummy variables for categorical variables (like the ‘Veg’ variable).

  2. The columns in the matrix should be named, since these identifiers will be used when fitting the model.

head(gobyData$site.cov)
##        Salinity Filter_time Other_fishes   Hab_size Veg
## [1,] -0.7114925       -1.17          0.0 -0.2715560   0
## [2,] -0.2109183       -1.24          0.0 -0.2663009   0
## [3,] -1.1602831       -1.29          0.0 -0.2717707   0
## [4,] -0.5561419        0.11        160.9 -0.2164312   1
## [5,] -0.9876713       -0.70        113.0  4.9981956   1
## [6,]  1.2562818       -0.55         19.3 -0.2934710   0

For more data formatting guidance, see section 2.1.1.

3.1 Fit the model

Now that we understand our data, let’s fit the joint model. The key arguments of this function include:

  1. data: list of qPCR.K, qPCR.N, count, and site.cov matrices
  2. cov: character vector of site-level covariates (this model will only include mean eDNA sample filter time and salinity)
  3. family: probability distribution used to model the seine count data. A poisson distribution is chosen here.
  4. p10priors: Beta distribution parameters for the prior on the probability of false positive eDNA detection, \(p_{10}\). c(1,20) is the default specification. More on this later.
  5. q: logical value indicating the presence of multiple traditional gear types. Here, we’re only using data from one traditional method.

More parameters exist to further customize the MCMC sampling, but we’ll stick with the defaults.

# run the joint model with two covariates
goby.fit.cov1 <- jointModel(data = gobyData, cov=c('Filter_time','Salinity'), 
                            family = 'poisson', p10priors = c(1,20), q=FALSE)

goby.fit.cov1 is a list containing:

  1. model fit (goby.fit.cov1$model) of the class ‘stanfit’ and can be accessed and interpreted using all functions in the rstan package.
  2. initial values used for each chain in MCMC (goby.fit.cov1$inits)

3.2 Model selection

We previously made a choice to include two site-level covariates. Perhaps we want to test how that model specification compares to a model specification with different site-level covariates.

# fit a new model with one site-level covariate
goby.fit.cov2 <- jointModel(data = gobyData, cov='Other_fishes',
                            family = 'poisson', p10priors = c(1,20), q=FALSE)

We can now compare the fit of these model to our data using the jointSelect() function, which performs leave-one-out cross validation with functions from the loo package.

# perform model selection
jointSelect(modelfits = list(goby.fit.cov1$model, goby.fit.cov2$model))
##        elpd_diff se_diff
## model1     0.0       0.0
## model2 -1060.0      98.4

These results tell us that model1 has a higher Bayesian LOO estimate of the expected log pointwise predictive density (elpd_loo). This means that goby.fit.cov1 is likely a better fit to the data.

You could keep going with this further and include/exclude different covariates, or compare to a null model without covariates.

3.3 Interpret the output

3.3.1 Summarize posterior distributions

Let’s interpret goby.fit.cov1. Use jointSummarize() to see the posterior summaries of the model parameters.

jointSummarize(goby.fit.cov1$model, par = c('p10','alpha'))
##            mean se_mean    sd   2.5%  97.5%     n_eff Rhat
## p10       0.003   0.000 0.001  0.001  0.007 16372.495    1
## alpha[1]  0.542   0.001 0.100  0.342  0.734 10063.793    1
## alpha[2]  1.021   0.001 0.118  0.778  1.247  9333.476    1
## alpha[3] -0.350   0.001 0.106 -0.556 -0.143  9601.087    1

This summarizes the mean, sd, and quantiles of the posterior estimates of \(p_{10}\) and \(\alpha\), as well as the effective sample size (n_eff) and Rhat for the parameters.

The mean estimated probability of a false positive eDNA detection is 0.001. In use case 1, the scalar parameter \(\beta\) was used to scale the relationship between eDNA and traditional sampling, but now the vector \(\alpha\) represents the regression covariates that scales this relationship (see model description for more). alpha[1] corresponds to the intercept of the regression with site-level covariates. alpha[2] corresponds to the regression coefficient associated with Filter_time, and alpha[3] corresponds to the regression coefficient associated with Salinity. Positive regression coefficients indicate an inverse relationship between the covariate and eDNA sensitivity.

We can also use functions from the bayesplot package to examine the posterior distributions and chain convergence.

First let’s look at the posterior distribution for \(p_{10}\).

library(bayesplot)
# plot posterior distribution, highlighting median and 80% credibility interval
mcmc_areas(as.matrix(goby.fit.cov1$model), pars = 'p10', prob = 0.8)

Next let’s look at chain convergence for \(p_{10}\) and \(\mu_{i=1}\).

# this will plot the MCMC chains for p10 and mu at site 1
mcmc_trace(rstan::extract(goby.fit.cov1$model, permuted = FALSE), 
           pars = c('p10', 'mu[1]'))

3.3.2 Effort necessary to detect presence

To further highlight the relative sensitivity of eDNA and traditional sampling, we can use detectionCalculate() to find the units of survey effort necessary to detect presence of the species. Here, detecting presence refers to producing at least one true positive eDNA detection or catching at least one individual in a traditional survey.

This function is finding the median number of survey units necessary to detect species presence if the expected catch rate, \(\mu\) is 0.1, 0.5, or 1. The cov.val argument indicates the value of the covariates used for the prediction. Since the covariate data was standardized, c(0,0) indicates that the prediction is made at the mean Filter_time and Salinity values.

detectionCalculate(goby.fit.cov1$model, mu=c(0.1,0.5,1), 
                   cov.val = c(0,0), probability = 0.9)
##       mu n_traditional n_eDNA
## [1,] 0.1            24     14
## [2,] 0.5             5      4
## [3,] 1.0             3      2

We can see that at the mean covariate values, it takes 14 eDNA samples or 24 seine samples to detect goby presence with 0.9 probability if the expected catch rate is 0.1.

Now let’s perform the same calculation under a condition where the Filter_time covariate value is 0.5 z-scores above the mean.

detectionCalculate(goby.fit.cov1$model, mu=c(0.1,0.5,1), 
                   cov.val = c(0.5,0), probability = 0.9)
##       mu n_traditional n_eDNA
## [1,] 0.1            24     23
## [2,] 0.5             5      5
## [3,] 1.0             3      3

At sites with a longer eDNA sample filter time, it would now take 22 eDNA samples or 24 seine samples to detect goby presence if the expected catch rate is 0.1.

Let’s do the same for salinity.

detectionCalculate(goby.fit.cov1$model, mu=c(0.1,0.5,1), 
                   cov.val = c(0,0.5), probability = 0.9)
##       mu n_traditional n_eDNA
## [1,] 0.1            24     12
## [2,] 0.5             5      3
## [3,] 1.0             3      2

At sites with higher salinity, it would now take 12 eDNA samples or 24 seine samples to detect goby presence if the expected catch rate is 0.1.

We can also plot these comparisons. mu.min and mu.max define the x-axis in the plot.

detectionPlot(goby.fit.cov1$model, mu.min=0.1, mu.max =1, 
              cov.val = c(0,0), probability = 0.9)

3.3.3 Calculate \(\mu_{critical}\)

The probability of a true positive eDNA detection, \(p_{11}\), is a function of the expected catch rate, \(\mu\). Low values of \(\mu\) correspond to low probability of eDNA detection. Since the probability of a false-positive eDNA detection is non-zero, the probability of a false positive detection may be higher than the probability of a true positive detection at very low values of \(\mu\).

\(\mu_{critical}\) describes the value of \(\mu\) where the probability of a false positive eDNA detection equals the probability of a true positive eDNA detection. This value can be calculated using muCritical(). Here, we will calculate this value at the mean covariate values.

muCritical(goby.fit.cov1$model, cov.val = c(0,0), ci = 0.9)
## $median
## [1] 0.005308535
## 
## $lower_ci
## Highest Density Interval: 1.93e-03
## 
## $upper_ci
## Highest Density Interval: 9.94e-03

This function calculates \(\mu_{critical}\) using the entire posterior distributions of parameters from the model, and ‘HDI’ corresponds to the 90% credibility interval calculated using the highest density interval.

3.4 Initial values

By default, eDNAjoint will provide initial values for parameters estimated by the model, but you can provide your own initial values if you prefer. Here is an example of providing initial values for parameters, mu,p10, and alpha, as an input in jointModel().

# set number of chains
n.chain <- 4

# initial values should be a list of named lists
inits <- list()
for(i in 1:n.chain){
  inits[[i]] <- list(
    # length should equal the number of sites (dim(gobyData$count)[1]) for each chain
    mu = stats::runif(dim(gobyData$count)[1], 0.01, 5), 
    # length should equal 1 for each chain 
    p10 = stats::runif(1,0.0001,0.08),
    # length should equal the number of covariates plus 1 (to account for intercept in regression)
    alpha = rep(0.1,length(c('Filter_time','Salinity'))+1)
    )
}

# now fit the model
fit.w.inits <- jointModel(data = gobyData, cov=c('Filter_time','Salinity'),
                          initial_values = inits)
## Refer to the eDNAjoint guide for visualization tips:  https://bookdown.org/abigailkeller/eDNAjoint_vignette/tips.html#visualization-tips
# check to see the initial values that were used
fit.w.inits$inits
## $chain1
## $chain1$mu_trad
##  [1] 4.87311389 0.90301135 3.35385926 4.00083071 0.96268310 2.27706004 4.65597632 4.29290668
##  [9] 0.71117890 1.08053922 2.97707712 4.92677729 1.24093501 4.39456004 3.33025322 3.47147832
## [17] 4.43904266 0.67545559 1.38384487 3.68920570 3.70656997 1.52695959 2.56920218 2.37666793
## [25] 1.76868082 3.27385499 3.48625076 0.43165344 1.87793647 0.57437784 2.68198020 3.50293964
## [33] 0.02096915 0.92914496 1.37672297 3.82268541 4.59524243 3.25722698 0.81579629
## 
## $chain1$mu
##  [1] 4.87311389 0.90301135 3.35385926 4.00083071 0.96268310 2.27706004 4.65597632 4.29290668
##  [9] 0.71117890 1.08053922 2.97707712 4.92677729 1.24093501 4.39456004 3.33025322 3.47147832
## [17] 4.43904266 0.67545559 1.38384487 3.68920570 3.70656997 1.52695959 2.56920218 2.37666793
## [25] 1.76868082 3.27385499 3.48625076 0.43165344 1.87793647 0.57437784 2.68198020 3.50293964
## [33] 0.02096915 0.92914496 1.37672297 3.82268541 4.59524243 3.25722698 0.81579629
## 
## $chain1$log_p10
## [1] -4.382506
## 
## $chain1$alpha
## [1] 0.1 0.1 0.1
## 
## 
## $chain2
## $chain2$mu_trad
##  [1] 0.8853014 2.2843590 4.6627828 4.7288319 1.5327739 2.8390243 1.2621569 3.1512105 4.8606645
## [10] 3.9340431 3.0064086 2.6969930 0.8015414 4.8427281 4.5843526 4.4481637 1.5290349 0.6648986
## [19] 4.1765323 4.2306110 2.6089595 0.9345764 1.0894476 1.2179956 1.3018619 1.2480840 4.3215420
## [28] 2.8330587 4.5201090 3.4565455 4.2864487 0.1568849 3.1391937 3.3241466 2.1559391 0.9767542
## [37] 0.8491288 2.3317877 1.5200078
## 
## $chain2$mu
##  [1] 0.8853014 2.2843590 4.6627828 4.7288319 1.5327739 2.8390243 1.2621569 3.1512105 4.8606645
## [10] 3.9340431 3.0064086 2.6969930 0.8015414 4.8427281 4.5843526 4.4481637 1.5290349 0.6648986
## [19] 4.1765323 4.2306110 2.6089595 0.9345764 1.0894476 1.2179956 1.3018619 1.2480840 4.3215420
## [28] 2.8330587 4.5201090 3.4565455 4.2864487 0.1568849 3.1391937 3.3241466 2.1559391 0.9767542
## [37] 0.8491288 2.3317877 1.5200078
## 
## $chain2$log_p10
## [1] -2.912903
## 
## $chain2$alpha
## [1] 0.1 0.1 0.1
## 
## 
## $chain3
## $chain3$mu_trad
##  [1] 4.2136063 4.5286765 2.8726319 3.1759354 4.3319311 2.2869386 3.7080048 2.4968119 2.7158734
## [10] 0.4736079 4.0013499 1.6294822 4.4293389 1.3051248 2.9491655 1.4825110 3.8999873 4.8093090
## [19] 2.8138496 1.0572490 4.8043141 3.6944030 2.6279183 1.7074272 4.1263386 0.7975414 3.3439192
## [28] 2.1665461 4.4645104 1.3379818 1.1232014 1.0237079 1.4393161 2.7829407 0.8280965 4.8089501
## [37] 2.8548738 3.6750154 3.9149659
## 
## $chain3$mu
##  [1] 4.2136063 4.5286765 2.8726319 3.1759354 4.3319311 2.2869386 3.7080048 2.4968119 2.7158734
## [10] 0.4736079 4.0013499 1.6294822 4.4293389 1.3051248 2.9491655 1.4825110 3.8999873 4.8093090
## [19] 2.8138496 1.0572490 4.8043141 3.6944030 2.6279183 1.7074272 4.1263386 0.7975414 3.3439192
## [28] 2.1665461 4.4645104 1.3379818 1.1232014 1.0237079 1.4393161 2.7829407 0.8280965 4.8089501
## [37] 2.8548738 3.6750154 3.9149659
## 
## $chain3$log_p10
## [1] -2.972813
## 
## $chain3$alpha
## [1] 0.1 0.1 0.1
## 
## 
## $chain4
## $chain4$mu_trad
##  [1] 1.73716379 3.44681043 1.75680934 0.59842895 3.21371349 2.50483360 0.10117224 3.56457582
##  [9] 4.28537574 1.79902336 1.01899421 4.77415165 0.67111364 4.86132973 2.64281979 0.19206326
## [17] 4.22371686 0.11819006 1.65882575 4.61783428 1.37783745 4.52700422 2.47502606 2.22880962
## [25] 4.26974470 1.27354565 4.86200215 1.70556052 0.12055805 0.55230596 2.40243769 2.31963024
## [33] 0.94686625 0.31297985 0.54406587 0.02373679 2.15351450 0.82313365 4.57692797
## 
## $chain4$mu
##  [1] 1.73716379 3.44681043 1.75680934 0.59842895 3.21371349 2.50483360 0.10117224 3.56457582
##  [9] 4.28537574 1.79902336 1.01899421 4.77415165 0.67111364 4.86132973 2.64281979 0.19206326
## [17] 4.22371686 0.11819006 1.65882575 4.61783428 1.37783745 4.52700422 2.47502606 2.22880962
## [25] 4.26974470 1.27354565 4.86200215 1.70556052 0.12055805 0.55230596 2.40243769 2.31963024
## [33] 0.94686625 0.31297985 0.54406587 0.02373679 2.15351450 0.82313365 4.57692797
## 
## $chain4$log_p10
## [1] -3.173633
## 
## $chain4$alpha
## [1] 0.1 0.1 0.1