Skip to contents

Builds a neighbour list from an sf polygon layer for the BYM2 model. By default, a district with no neighbours is linked to the nearest district.

Usage

spi_adjacency(
  boundaries,
  id_col,
  contiguity = "queen",
  handle_islands = TRUE,
  snap_tolerance = 1e-05
)

Arguments

boundaries

sf object with polygon geometries.

id_col

character. column name containing the unique district identifier.

contiguity

character. "queen" (default) or "rook" contiguity rule.

handle_islands

Logical. If TRUE (default), districts with no neighbours are linked to their nearest district. Separate groups of connected districts are not joined.

snap_tolerance

numeric. tolerance for boundary matching in CRS units. default 1e-5.

Value

Object of class nb (spdep). A list of length nrow(boundaries) where element i is an integer vector of neighbour indices for district i. Key attributes:

  • region.id – character vector of district ids in graph order; downstream functions use this to align cases$district_id with the spatial index.

  • type – "queen" or "rook".

  • sym – logical, TRUE for symmetric graphs (always TRUE for contiguity graphs; may become FALSE after island attachment via knn).

  • ncomp – list describing the connected components.

Details

Neighbour list. An nb object (from spdep) has one element per district. Element i lists the row numbers of district i's neighbours. This records which districts share a boundary under the chosen rule.

Use in the model. The BYM2 spatial random effect fitted inside spi_expected() separates district variation into a structured component (correlated across neighbours) and an unstructured component. The structured part is a Gaussian Markov random field whose precision matrix is built directly from this neighbour graph. Districts with no neighbours contribute no spatial smoothing. By default, the function links these isolated districts to their nearest district.

Contiguity rule. Queen contiguity treats districts as neighbours if they share any boundary point (edge or corner); rook requires a shared edge. Queen is the usual choice for administrative polygons.

Inspecting the output

Use the print and summary methods from spdep to inspect the graph:


summary(adj)                   # link counts, component count, extremes
length(adj)                    # number of districts
spdep::card(adj) |> table()    # distribution of neighbour counts
which(spdep::card(adj) == 0)   # remaining islands (should be empty)
attr(adj, "region.id")         # district ids in graph order
adj[[1]]                       # integer indices of district 1's neighbours

Plot the graph over the polygons:


ctr <- sf::st_centroid(sf::st_geometry(boundaries))
plot(sf::st_geometry(boundaries), border = "grey80")
plot(adj, sf::st_coordinates(ctr), add = TRUE, col = "steelblue")

What to look for.

  • Average neighbours of roughly 4-7 is typical for admin-2 layers. An average below 2 or above 10 is a reason to check the coordinate reference system and look for gaps, overlaps, or duplicated polygons.

  • Disjoint connected subgraphs > 1 means the country splits into separate components. BYM2 still fits, but each component is smoothed independently. Check whether the separate components reflect actual geography, such as islands, or errors in the boundaries.

  • Remaining islands (card == 0) should be empty when handle_islands = TRUE.

See also

spi_expected(), spdep::poly2nb(), spdep::summary.nb()