NIMBLE provides the ability to specify and fit conditional autoregressive (CAR) models, both intrinsic and proper models.
Here we’ll use an intrinsic CAR (ICAR) model for disease mapping.
We’ll illustrate with data on hospital admissions due to respiratory
disease in 2010 from the 134 Intermediate Geographies (IG) north of the
river Clyde in the Greater Glasgow and Clyde health board. The data are
available in the CARBayesdata
package and can be
transformed into the neighborhood information needed by NIMBLE using
functions from the spdep
package. In particular, we need to
provide vectors indicating which regions are neighbors of which other
regions (adj
), the weights for each pair of neighbors
(weights
), and the number of neighbors for each region
(num
).
As usual, the mean of the Poisson counts includes an offset for the expected count. We’ll also include a covariate, the percentage of people defined to be income deprived in each spatial region.
library(nimble, warn.conflicts = FALSE)
## nimble version 1.3.0 is loaded.
## For more information on NIMBLE and a User Manual,
## please visit https://R-nimble.org.
##
## Note for advanced users who have written their own MCMC samplers:
## As of version 0.13.0, NIMBLE's protocol for handling posterior
## predictive nodes has changed in a way that could affect user-defined
## samplers in some situations. Please see Section 15.5.1 of the User Manual.
library(CARBayesdata, quietly = TRUE)
library(sp, quietly = TRUE)
library(spdep, quietly = TRUE)
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
## Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 8.2.1; sf_use_s2() is TRUE
data(GGHB.IZ)
data(respiratorydata)
We handle the spatial analysis here with nb2WB
from the
package spdep
.
respiratorydata_spatial <- merge(x = GGHB.IZ, y = respiratorydata, by = "IZ", all.x = FALSE)
W.nb <- poly2nb(respiratorydata_spatial, row.names = rownames(respiratorydata_spatial))
## Determine neighborhood/adjacency information needed for neighborhood-based CAR model
nbInfo <- nb2WB(W.nb)
# A vector of indices indicating which regions are neighbors of which.
nbInfo$adj
## [1] 2 3 5 87 91 93 1 3 91 113 1 2 5 11 113 6 7 10
## [19] 98 101 105 106 1 3 9 11 19 87 90 104 109 4 10 12 105 108
## [37] 4 10 20 106 109 11 15 16 113 114 5 19 20 109 4 6 7 12
## [55] 13 14 20 3 5 8 16 19 22 113 6 10 13 17 26 108 111 112
## [73] 122 124 126 10 12 14 17 10 13 17 20 8 16 18 24 113 114 116
## [91] 8 11 15 18 22 12 13 14 20 25 26 15 16 21 22 24 5 9
## [109] 11 20 22 28 7 9 10 14 17 19 23 25 28 109 18 22 24 11
## [127] 16 18 19 21 24 27 28 20 25 15 18 21 22 17 20 23 26 12
## [145] 17 25 126 22 28 19 20 22 27 30 33 37 38 29 31 34 38 30
## [163] 34 35 41 44 39 40 29 36 37 39 30 31 35 38 41 42 31 34
## [181] 41 33 37 39 43 29 33 36 38 43 29 30 34 37 42 43 32 33
## [199] 36 40 43 46 47 32 39 46 55 31 34 35 42 44 45 34 38 41
## [217] 43 45 52 36 37 38 39 42 47 52 54 31 41 45 50 51 58 41
## [235] 42 44 50 52 59 39 40 47 48 55 39 43 46 48 54 56 46 47
## [253] 55 56 53 57 61 44 45 58 59 70 44 58 70 42 43 45 54 59
## [271] 63 49 55 61 65 43 47 52 56 63 40 46 48 53 56 62 65 66
## [289] 72 47 48 54 55 62 63 74 49 60 61 44 50 51 70 45 50 52
## [307] 63 70 91 113 57 61 64 67 68 73 49 53 57 60 65 68 71 55
## [325] 56 63 66 74 76 83 52 54 56 59 62 74 91 113 60 67 69 75
## [343] 78 53 55 61 71 72 81 55 62 72 76 60 64 73 75 60 61 71
## [361] 73 77 79 64 78 80 82 50 51 58 59 113 61 65 68 79 81 55
## [379] 65 66 76 81 88 90 60 67 68 75 77 85 56 62 63 83 91 93
## [397] 64 67 73 78 85 62 66 72 83 87 90 68 73 79 85 86 64 69
## [415] 75 80 82 85 68 71 77 81 84 86 69 78 82 92 95 65 71 72
## [433] 79 84 88 69 78 80 85 95 62 74 76 87 93 79 81 86 88 89
## [451] 73 75 77 78 82 86 94 95 98 101 77 79 84 85 89 96 98 1
## [469] 5 76 83 90 93 72 81 84 89 90 96 97 100 104 84 86 88 96
## [487] 5 72 76 87 88 104 1 2 59 63 74 93 113 80 95 99 103 117
## [505] 1 74 83 87 91 85 95 101 102 105 80 82 85 92 94 99 102 86
## [523] 88 89 98 100 106 88 100 104 109 4 85 86 96 101 106 92 95 102
## [541] 103 107 88 96 97 106 109 4 85 94 98 102 105 94 95 99 101 105
## [559] 107 92 99 107 117 119 5 88 90 97 109 4 6 94 101 102 107 108
## [577] 4 7 96 98 100 109 99 102 103 105 108 110 119 6 12 105 107 110
## [595] 111 112 5 7 9 20 97 100 104 106 107 108 112 119 122 12 108 112
## [613] 12 108 110 111 122 124 2 3 8 11 15 59 63 70 91 114 8 15
## [631] 113 115 116 114 116 15 114 115 92 103 118 119 117 119 120 121 123 103
## [649] 107 110 117 118 120 122 118 119 121 122 118 120 122 123 126 12 110 112
## [667] 119 120 121 124 126 118 121 126 127 12 112 122 126 127 129 130 12 26
## [685] 121 122 123 124 127 132 123 125 126 129 132 131 125 127 130 132 125 129
## [703] 131 132 128 130 132 133 126 127 129 130 131 133 134 131 132 134 132 133
# A vector of weights. In this case, all weights are 1.
head(nbInfo$weights)
## [1] 1 1 1 1 1 1
# A vector of length N. num[n] indicates how many neighbors region n contains.
# This helps map the adj vector to the starting region.
nbInfo$num
## [1] 6 4 5 7 9 5 5 5 4 7 7 11 4 4 7 5 6 5 6 10 3 8 2 4 4
## [26] 4 2 4 4 4 5 2 4 6 3 4 5 6 7 4 6 6 8 6 6 5 6 4 3 5
## [51] 3 6 4 5 9 7 3 4 7 6 7 7 8 5 6 4 4 6 4 5 5 7 6 6 5
## [76] 6 5 6 6 5 6 5 5 5 10 7 6 9 4 6 7 5 5 5 7 6 4 6 5 5
## [101] 6 6 5 5 7 6 7 7 8 5 3 6 10 5 2 3 4 5 7 4 5 8 4 4 3
## [126] 8 5 1 4 4 4 7 3 2
Now we have the three pieces of information we need. We’re ready to use the dcar_normal distribution in a nimbleModel.
nregions <- nrow(respiratorydata_spatial)
code <- nimbleCode({
# priors
beta ~ dnorm(0, sd = 100)
sigma ~ dunif(0, 100) # prior for variance components based on Gelman (2006)
tau <- 1 / sigma^2
# latent process
s[1:N] ~ dcar_normal(adj[1:L], weights[1:L], num[1:N], tau, zero_mean = 0)
# likelihood
for(i in 1:N) {
log(lambda[i]) <- log(expected[i]) + beta*x[i] + s[i]
y[i] ~ dpois(lambda[i])
}
})
x <- respiratorydata_spatial$incomedep
x <- x - mean(x) # center for improved MCMC performance
set.seed(1)
constants <- list(N = nregions, L = length(nbInfo$adj),
adj = nbInfo$adj, weights = nbInfo$weights, num = nbInfo$num,
x = x, expected = respiratorydata_spatial$expected)
data <- list(y = respiratorydata_spatial$observed)
inits <- list(beta = 0, sigma = 1, s = rnorm(nregions))
model <- nimbleModel(code, constants = constants, data = data, inits = inits)
## Defining model
## Building model
## Setting data and initial values
## Running calculate on model
## [Note] Any error reports that follow may simply reflect missing values in model variables.
## Checking model sizes and dimensions
cModel <- compileNimble(model)
## Compiling
## [Note] This may take a minute.
## [Note] Use 'showCompilerOutput = TRUE' to see C++ compilation details.
conf <- configureMCMC(model, monitors = c('beta', 'sigma', 's'))
## ===== Monitors =====
## thin = 1: beta, s, sigma
## ===== Samplers =====
## RW sampler (2)
## - beta
## - sigma
## CAR_normal sampler (1)
## - s[1:134]
conf$printSamplers()
## [1] RW sampler: beta
## [2] RW sampler: sigma
## [3] CAR_normal sampler: s[1:134]
MCMC <- buildMCMC(conf)
cMCMC <- compileNimble(MCMC, project = cModel)
## Compiling
## [Note] This may take a minute.
## [Note] Use 'showCompilerOutput = TRUE' to see C++ compilation details.
samples <- runMCMC(cMCMC, niter = 10000, nburnin = 1000)
## running chain 1...
## |-------------|-------------|-------------|-------------|
## |-------------------------------------------------------|
Here are density plots for some posterior samples of interest.
plot(density(samples[,"sigma"]))
plot(density(samples[,"s[1]"]))
plot(density(samples[,"beta"]))
In some cases, MCMC mixing in CAR models (particularly when
considering the CAR hyperparameter(s)) can be slow, and considering
NIMBLE’s HMC sampler (in the nimbleHMC
package) may be
worthwhile.