7 Space as a network of neighbours

Link each language to the languages around it and let every language lean towards the average of its neighbours.

Fitted with spdep and brmsModel fitting about 6 minutesPackages brms spdep sf rnaturalearth ggplot2Download the R script

7.1 Set up

library(brms)
library(spdep)
library(sf)
library(rnaturalearth)
library(ggplot2)

base <- "https://rehan-muh.github.io/tutorials/data/"
d    <- read.csv(paste0(base, "languages.csv"))
d$elev_km <- d$elevation / 1000

options(mc.cores = 4, brms.backend = "cmdstanr")
dir.create("fits", showWarnings = FALSE)

7.2 The idea

The Gaussian process in Chapter 6 measured distances. The models in this chapter only ask who is next to whom. You draw a graph with a link between every pair of neighbouring languages, and the model gives each language a spatial effect that is expected to equal the average of its neighbours’ effects:

\[\phi_i \mid \phi_{\text{others}} \sim \mathcal{N}\!\left(\frac{1}{n_i}\sum_{j \sim i} \phi_j,\ \frac{\sigma^2}{n_i}\right)\]

Here \(j \sim i\) runs over the \(n_i\) neighbours of language \(i\). This is the intrinsic conditional autoregressive model, ICAR (Besag 1974). It was designed for areas such as counties, which have borders and therefore natural neighbours. Languages recorded as points have no borders, so the graph is yours to define, and that definition is the main modelling decision in this chapter.

7.3 Building the graph

A simple, defensible rule is to link every language to its five nearest languages by great-circle distance and make every link mutual.

coords <- as.matrix(d[, c("lon", "lat")])
nb <- knn2nb(knearneigh(coords, k = 5, longlat = TRUE))
nb <- make.sym.nb(nb)

summary(card(nb))        # number of neighbours per language
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    5.00    5.00    6.00    6.43    7.00   10.00
n.comp.nb(nb)$nc         # number of separate pieces
## [1] 1

After the links are made mutual, languages have between 5 and 10 neighbours. The second number matters more. It says the graph is in one piece, which the models below need. If n.comp.nb() reports more than one piece, raise k until it reports one.

Always look at the graph.

world <- ne_countries(scale = "small", returnclass = "sf")
pts   <- st_as_sf(d, coords = c("lon", "lat"), crs = 4326)
links <- nb2lines(nb, coords = st_geometry(pts))
# split the links that cross the 180th meridian, so they are drawn the short way
links <- st_wrap_dateline(links, options = c("WRAPDATELINE=YES",
                                             "DATELINEOFFSET=60"))

ggplot() +
  geom_sf(data = world, fill = "grey94", colour = NA) +
  geom_sf(data = links, colour = "#2A78D6", linewidth = 0.25) +
  geom_sf(data = pts, size = 0.5) +
  coord_sf(crs = "+proj=eqearth")
The five-nearest-neighbour graph. Most links are short. The graph holds together through a few longer ones: across the Bering Strait, through Central America, and from Australia to New Guinea.

Figure 7.1: The five-nearest-neighbour graph. Most links are short. The graph holds together through a few longer ones: across the Bering Strait, through Central America, and from Australia to New Guinea.

brms takes the graph as a matrix W with a 1 wherever two languages are linked. Its row names must match a column of the data.

W <- nb2mat(nb, style = "B")
dimnames(W) <- list(d$glottocode, d$glottocode)

7.4 ICAR

The spatial term is car(). It names the matrix, the column that identifies the rows, and the type of model.

priors <- prior(normal(0, 1), class = b) +
          prior(exponential(1), class = sdcar)

m_icar <- brm(ejectives ~ elev_km + car(W, gr = glottocode, type = "icar"),
              data = d, data2 = list(W = W),
              family = bernoulli(), prior = priors,
              seed = 1, file = "fits/m_icar")

summary(m_icar)
##  Family: bernoulli 
##   Links: mu = logit 
## Formula: ejectives ~ elev_km + car(W, gr = glottocode, type = "icar") 
##    Data: d (Number of observations: 500) 
##   Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup draws = 4000
## 
## Correlation Structures:
##       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sdcar     4.04      1.00     2.45     6.31 1.00      860     1867
## 
## Regression Coefficients:
##           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## Intercept    -7.83      1.68   -11.67    -5.20 1.01      494     1071
## elev_km       1.18      0.36     0.53     1.95 1.00     1709     2821
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

sdcar is the standard deviation of the spatial effects. It is not on the same footing as sd in Chapter 3 or sdgp in Chapter 6, because its meaning depends on how many links the graph has. That is one reason to prefer the next model.

7.5 BYM2

ICAR forces all the extra variation to be spatially smooth. BYM2, named after Besag, York and Mollié (1991) and used here in the scaled form of Riebler et al. (2016), splits each language’s effect into a smooth part and an independent part, and estimates the mix:

\[u_i = \sigma\left(\sqrt{\rho}\,\phi_i^{*} + \sqrt{1-\rho}\,\theta_i\right)\]

\(\phi^{*}\) is the ICAR effect rescaled to have variance near 1, \(\theta\) is plain independent noise, and \(\rho\) is the share of the variance that is spatial. Because of the rescaling, \(\sigma\) is an ordinary standard deviation and the exponential(1) prior means what it meant in Chapter 3.

m_bym <- brm(ejectives ~ elev_km + car(W, gr = glottocode, type = "bym2"),
             data = d, data2 = list(W = W),
             family = bernoulli(), prior = priors,
             seed = 1, file = "fits/m_bym")

summary(m_bym)
##  Family: bernoulli 
##   Links: mu = logit 
## Formula: ejectives ~ elev_km + car(W, gr = glottocode, type = "bym2") 
##    Data: d (Number of observations: 500) 
##   Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup draws = 4000
## 
## Correlation Structures:
##        Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## rhocar     0.96      0.04     0.87     1.00 1.00      831     1176
## sdcar      5.21      1.27     3.19     8.18 1.01      559     1623
## 
## Regression Coefficients:
##           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## Intercept    -7.68      1.59   -11.32    -5.14 1.02      264     1388
## elev_km       1.17      0.37     0.52     1.97 1.00     1055     1902
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

The Stan implementation behind car() follows M. Morris et al. (2019). rhocar near 1 says that almost all the extra variation is shared between neighbours; near 0 it would say the languages vary independently and the graph is not helping.

The effective sample size of the intercept is in the hundreds, well below the slope’s. Neighbour models mix slowly in the intercept because every spatial effect can shift against it. The estimate is still usable, but if a Bulk_ESS drops below about 400, add iter = 4000 to the call and let it run longer.

sea_level <- transform(d, elev_km = 0)
pts$baseline <- fitted(m_bym, newdata = sea_level)[, "Estimate"]

ggplot() +
  geom_sf(data = world, fill = "grey94", colour = NA) +
  geom_sf(data = pts[order(pts$baseline), ], aes(colour = baseline),
          size = 1.6) +
  scale_colour_viridis_c(name = "baseline probability") +
  coord_sf(crs = "+proj=eqearth")
The BYM2 baseline at each language: the probability of ejectives the model expects there at sea level. Compare the surface in an earlier chapter.

Figure 7.2: The BYM2 baseline at each language: the probability of ejectives the model expects there at sea level. Compare the surface in Chapter 6.

7.6 The regression line

Draw the model’s regression line beside the naive one. As in Chapter 3, the fair comparison is the line averaged over the sample: keep each language’s own spatial effect, move all 500 languages to the same elevation, and take the share expected to have ejectives.

m0 <- glm(ejectives ~ elev_km, family = binomial, data = d)
x  <- seq(0, 4.5, by = 0.1)
pr <- predict(m0, newdata = data.frame(elev_km = x), se.fit = TRUE)
naive <- data.frame(elev_km = x, model = "naive glm", p = plogis(pr$fit),
                    lo = plogis(pr$fit - 1.96 * pr$se.fit),
                    hi = plogis(pr$fit + 1.96 * pr$se.fit))

b      <- as_draws_df(m_bym)$b_elev_km
eta    <- posterior_linpred(m_bym)        # draws by languages, logit scale
at_sea <- eta - outer(b, d$elev_km)      # every language moved to 0 km
p      <- sapply(x, function(xi) rowMeans(plogis(at_sea + b * xi)))
model  <- data.frame(elev_km = x, model = "neighbour graph (BYM2)",
                     p  = colMeans(p),
                     lo = apply(p, 2, quantile, 0.025),
                     hi = apply(p, 2, quantile, 0.975))

ggplot(rbind(naive, model), aes(elev_km, p, colour = model, fill = model)) +
  geom_ribbon(aes(ymin = lo, ymax = hi), alpha = 0.15, colour = NA) +
  geom_line(linewidth = 0.9) +
  scale_colour_manual(values = c("grey40", "#2A78D6"),
                      aesthetics = c("colour", "fill"), name = NULL) +
  labs(x = "elevation (km)", y = "probability of ejectives")
The naive regression line (grey) and the BYM2 line averaged over the sample (blue), with 95% bands. It is close to the Gaussian-process line in an earlier chapter, although the two models describe space differently.

Figure 7.3: The naive regression line (grey) and the BYM2 line averaged over the sample (blue), with 95% bands. It is close to the Gaussian-process line in Chapter 6, although the two models describe space differently.

7.7 How much does the graph matter?

The graph was a choice, so vary it. Build a denser one with ten nearest neighbours and refit.

nb10 <- make.sym.nb(knn2nb(knearneigh(coords, k = 10, longlat = TRUE)))
W10  <- nb2mat(nb10, style = "B")
dimnames(W10) <- list(d$glottocode, d$glottocode)

m_bym10 <- brm(ejectives ~ elev_km + car(W, gr = glottocode, type = "bym2"),
               data = d, data2 = list(W = W10),
               family = bernoulli(), prior = priors,
               seed = 1, file = "fits/m_bym10")

slopes <- rbind(fixef(m_icar)["elev_km", ], fixef(m_bym)["elev_km", ],
                fixef(m_bym10)["elev_km", ])
rownames(slopes) <- c("ICAR, k = 5", "BYM2, k = 5", "BYM2, k = 10")
round(slopes, 2)
##              Estimate Est.Error Q2.5 Q97.5
## ICAR, k = 5      1.18      0.36 0.53  1.95
## BYM2, k = 5      1.17      0.37 0.52  1.97
## BYM2, k = 10     1.31      0.39 0.62  2.13

Doubling the number of neighbours moves the slope a little and leaves the conclusion where it was. Report a check like this whenever you use a neighbour model on point data. If the conclusion changes with a reasonable change of graph, report that too, because it shows how far the data support the conclusion.

Watch out. Nearest-neighbour links ignore everything a linguist knows about contact. A link across the Sahara or the Bering Strait counts the same as one between adjacent villages. Other rules are a line of code away: dnearneigh() links all pairs within a fixed distance, and tri2nb() or gabrielneigh() link natural neighbours without choosing k. You can also edit links by hand with edit.nb() or build W yourself from a table of known contact.

With your own data. If your units really are areas (language polygons, countries, grid cells), read them with sf and call poly2nb() to link the ones that share a border. Everything after nb is the same.

7.8 Other models in the same family

car() also accepts type = "escar", the proper CAR model, which estimates how strongly neighbours pull on each other in place of assuming the maximum. Simultaneous autoregressive (SAR) models are a close relative with their own term, sar(), but brms fits them only for continuous outcomes; they appear in Chapter 8.

7.9 What to carry forward

  • Neighbour models need a graph in one piece and a matrix W whose row names match the data.
  • car(W, gr = , type = "bym2") is the version to start with: its parameters have plain meanings, a standard deviation and a spatial share.
  • The graph is an assumption. Map it, and refit with a different graph to check that the conclusion holds.

The chapters so far each handled one kind of dependence. Chapter 9 puts ancestry and geography in the same model.

7.10 Further reading

How earlier studies implemented it. Neighbour models come from disease mapping, where the units are districts with borders (Besag et al. 1991). They have been rare in linguistics, where the earlier way of saying who is near whom was membership of an area: the continental areas of Dryer (1989) or a random intercept for area (Jaeger et al. 2011). A neighbour graph is the finer-grained version of the same idea, and the comparison with the surface models of Chapter 6 is what the spatial-statistics literature recommends before choosing either (Dormann et al. 2007).

Neighbour models have a long literature in disease mapping, and most of what is known about them comes from there.

  • What the ICAR model is. Besag and Kooperberg (1995) is the standard account of intrinsic autoregressions and why they are improper priors. Ver Hoef et al. (2018) explain CAR and SAR models side by side for non-specialists.
  • Scaling and priors. Riebler et al. (2016) introduce the BYM2 parameterisation and explain why the unscaled version has uninterpretable hyperparameters; Simpson et al. (2017) give the penalised-complexity priors that go with it. M. Morris et al. (2019) show the Stan implementation.
  • Graphs in several pieces. This chapter insisted on a connected graph. Freni-Sterrantino et al. (2018) show how to scale and constrain an intrinsic model when the graph has islands, which is how INLA handles them.
  • Alternatives to ICAR. The directed acyclic graph autoregressive model of Datta et al. (2019) has a proper, interpretable correlation parameter and often fits better than the proper CAR.

References

Besag, Julian. 1974. “Spatial Interaction and the Statistical Analysis of Lattice Systems.” Journal of the Royal Statistical Society: Series B 36 (2): 192–236.
Besag, Julian, and Charles Kooperberg. 1995. “On Conditional and Intrinsic Autoregressions.” Biometrika 82 (4): 733–46.
Besag, Julian, Jeremy York, and Annie Mollié. 1991. “Bayesian Image Restoration, with Two Applications in Spatial Statistics.” Annals of the Institute of Statistical Mathematics 43 (1): 1–20.
Datta, Abhirup, Sudipto Banerjee, James S. Hodges, and Leiwen Gao. 2019. “Spatial Disease Mapping Using Directed Acyclic Graph Auto-Regressive (DAGAR) Models.” Bayesian Analysis 14 (4): 1221–44.
Dormann, Carsten F., Jana M. McPherson, Miguel B. Araújo, et al. 2007. “Methods to Account for Spatial Autocorrelation in the Analysis of Species Distributional Data: A Review.” Ecography 30 (5): 609–28.
Dryer, Matthew S. 1989. “Large Linguistic Areas and Language Sampling.” Studies in Language 13 (2): 257–92.
Freni-Sterrantino, Anna, Massimo Ventrucci, and Håvard Rue. 2018. “A Note on Intrinsic Conditional Autoregressive Models for Disconnected Graphs.” Spatial and Spatio-Temporal Epidemiology 26: 25–34.
Jaeger, T. Florian, Peter Graff, William Croft, and Daniel Pontillo. 2011. “Mixed Effect Models for Genetic and Areal Dependencies in Linguistic Typology.” Linguistic Typology 15 (2): 281–319.
Morris, Mitzi, Katherine Wheeler-Martin, Dan Simpson, Stephen J. Mooney, Andrew Gelman, and Charles DiMaggio. 2019. “Bayesian Hierarchical Spatial Models: Implementing the Besag York Mollié Model in Stan.” Spatial and Spatio-Temporal Epidemiology 31: 100301.
Riebler, Andrea, Sigrunn H. Sørbye, Daniel Simpson, and Håvard Rue. 2016. “An Intuitive Bayesian Spatial Model for Disease Mapping That Accounts for Scaling.” Statistical Methods in Medical Research 25 (4): 1145–65.
Simpson, Daniel, Håvard Rue, Andrea Riebler, Thiago G. Martins, and Sigrunn H. Sørbye. 2017. “Penalising Model Component Complexity: A Principled, Practical Approach to Constructing Priors.” Statistical Science 32 (1): 1–28.
Ver Hoef, Jay M., Erin E. Peterson, Mevin B. Hooten, Ephraim M. Hanks, and Marie-Josée Fortin. 2018. “Spatial Autoregressive Models for Statistical Inference from Ecological Data.” Ecological Monographs 88 (1): 36–59.