generated from r4ds/bookclub-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
07_spatial-neighborhood-matrices.Rmd
375 lines (253 loc) · 8.12 KB
/
07_spatial-neighborhood-matrices.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# Spatial neighbourhood matrices
**Learning objectives:**
- understand what spatial neighbours are
- know how spatial neighbours can be defined
- create and plot a neighbours list
- use a neighbours list to create a spatial neighbourhood matrix
## Areal data {-}
- This is the first chapter of the part 'areal data'.
- > In areal or lattice data, the domain D is a fixed countable collection of (regular or irregular) areal units at which variables are observed.
- Areal data usually arise when a number of events corresponding to some variable of interest are aggregated in areas.
## Spatial neighbourhood {-}
- It represents which areas are close to one another (polygons, points)
- In this chapter, we won't use attribute variables, only the geometries
## Spatial neighbourhood {-}
- It will help to assess spatial autocorrelation with areal data
- to do that, areas must be spatially connected by weights: in a **spatial neighbourhood matrix**
- to obtain a neighbourhood matrix, one needs to define the neighbours of each area: the **neighbours list**
## Spatial neighbourhood in R {-}
- Package **spdep**: <https://r-spatial.github.io/spdep>
```{r message=FALSE}
library(sf)
library(spdep)
library(ggplot2)
```
## Read example data {-}
```{r}
map <- read_sf(system.file("shapes/columbus.shp",
package = "spData"), quiet = TRUE)
map
```
## Example data {-}
From `?spData::columbus`:
> The columbus data frame has 49 rows and 22 columns
> Unit of analysis: 49 neighbourhoods in Columbus, OH, 1980 data
## Example data {-}
```{r}
# we won't need attributes:
map_geom <- st_geometry(map)
ggplot(map_geom) + geom_sf() + theme_bw()
```
## Spatial neighbourhood {-}
Remind:
- **spatial neighbourhood matrix**: connects areas by weights
- to obtain it, one needs the **neighbours list**: defines the neighbours of each area
## Spatial neighbours {-}
The concept of a neighbour is **binary** (0 / 1)!
Area 2 **is** a spatial neighbour of area 1, or it is **not**.
## Spatial neighbours list {-}
- A **neighbours list** (**`nb`** class) is a kind of sparse matrix: a list that gives the indices of neighbours for each area in turn.
- e.g. the first 6 elements give the neighbour indices of the first 6 geometries of the input layer:
```r
[[1]]
[1] 2 3
[[2]]
[1] 1 3 4
[[3]]
[1] 1 2 4 5
[[4]]
[1] 2 3 5 8
[[5]]
[1] 3 4 6 8 9 11 15 16
[[6]]
[1] 5 9
```
## Defining who is a neighbour and who isn't {-}
- **contiguity criteria** -- this needs _polygons_:
- the areas that share at least a common vertex (type **Queen**)
- the areas that share a common border (type **Rook**)
- **distance criteria** -- this needs _points_ (e.g. polygon centroids):
- the areas that are **within some distance** apart (lower and upper bounds)
- the areas that are **among the $k$ nearest** to an area (asymmetric relationship)
## Creating a neighbours list ('nb') from geometries {-}
- contiguity based:
- `poly2nb(<polygons>, queen = TRUE)` (default)
- `poly2nb(<polygons>, queen = FALSE)`
- distance based:
- `dnearneigh(<points>, d1, d2)`
- `knn2nb(<matrix of nearest neighbours>)`
## Neighbours list: type Queen contiguity {-}
```{r}
nb1 <- poly2nb(map_geom, queen = TRUE)
nb1
head(nb1)
```
## Neighbours list: type Rook contiguity {-}
```{r}
nb2 <- poly2nb(map_geom, queen = FALSE)
nb2
head(nb2)
```
## Plotting {-}
With `nb.plot(<neighbours list>, <sfc object>)`
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nb1, map_geom, add = TRUE)
```
## Plotting {-}
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nb2, map_geom, add = TRUE)
```
## Neighbours list based on distance bounds {-}
Creating centroids from sf polygons:
```{r collapse=TRUE}
(centroids <- st_centroid(map_geom))
```
## Neighbours list based on distance bounds {-}
```{r out.width='100%'}
ggplot() +
geom_sf(data = map_geom) +
geom_sf(data = centroids) +
theme_bw()
```
## Neighbours list based on distance bounds {-}
```{r}
nb3 <- dnearneigh(x = centroids, d1 = 0, d2 = 0.4)
head(nb3)
```
## Neighbours list based on distance bounds {-}
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nb3, map_geom, add = TRUE)
```
## Neighbours list based on $k$ nearest neighbours {-}
In two steps:
1. `knearneigh()`: create a `knn` object ('k-nearest neighbour classification')
- it contains `nn`: a _matrix_ that defines the k nearest neighbors
1. `knn2nb()`: convert the `knn` object to a neighbours list
```{r}
knn_centroids <- knearneigh(centroids, k = 3)
class(knn_centroids)
class(knn_centroids$nn)
head(knn_centroids$nn)
```
## Neighbours list based on $k$ nearest neighbours {-}
Step 2:
```{r}
nb4 <- knn2nb(knn_centroids)
head(nb4)
```
## Neighbours list based on $k$ nearest neighbours {-}
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nb4, map_geom, add = TRUE)
```
## Creating higher order neighbours lists {-}
Starting from an existing neighbours list, one can redefine neighbours using a lag:
- lag = 2: neighbours are 2 links apart in the original neighbours list
- lag = 3: neighbours are 3 links apart in the original neighbours list
- ...
## Creating higher order neighbours lists {-}
`nblag(<neighbours list>, maxlag =)`: to produce `maxlag` higher order neighbours lists
- returns a list of lagged neighbours lists: element 1 for lag = 1, etc)
## Creating higher order neighbours lists {-}
```{r}
nblags <- nblag(neighbours = nb1, maxlag = 3)
class(nblags)
length(nblags)
all.equal(nb1, nblags[[1]], check.attributes = FALSE)
```
## Creating higher order neighbours lists {-}
```{r}
lapply(nblags, head, 2)
```
## Creating higher order neighbours lists {-}
Plotting the second order neighbours list:
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nblags[[2]], map_geom, add = TRUE)
```
## Creating higher order neighbours lists {-}
Plotting the third order neighbours list:
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nblags[[3]], map_geom, add = TRUE)
```
## Cumulating neighbours lists {-}
You can cumulate multiple neighbour lists to a single neighbour list:\
`nblag_cumul(<list of neighbours lists>)`
Cumulating the 1st and 2nd order neighbours lists from before:
```{r}
nblagsc <- nblag_cumul(nblags[1:2])
class(nblagsc)
head(nblagsc)
```
## Cumulating neighbours lists {-}
```{r out.width='100%'}
plot(map_geom, border = "lightgray")
plot.nb(nblagsc, map_geom, add = TRUE)
```
## Further things to do with a neighbours list {-}
- Count neighbours: `lengths(<nb>)` (or `spdep::card()`)
- Compute distances between neighbours: `nbdists(<nb>, <points>)`
- Create a spatial neighbourhoods matrix: `nb2mat(<nb>, ...)`
## Count neighbours {-}
```{r}
lengths(nb1)
```
## Compute distances between neighbours {-}
```{r}
nbdists(nb1) |> try()
```
## Compute distances between neighbours {-}
```{r}
nbdists(nb1, centroids) |> head()
```
## Neighbourhood matrix {-}
Straightforward function is the `nb2mat()` function (not in the book).
It converts the 'sparse' neighbours list to a square neighbourhood matrix of **weights**.
## Neighbourhood matrix {-}
Basic conversion from the neighbours list to a neighbourhood matrix:
```{r}
nb2mat(nb1, style = "B") |> dim()
```
## Neighbourhood matrix {-}
The basic (B) format uses its input as-is: binary!
```{r}
nb2mat(nb1, style = "B")[1:4, 1:7]
```
## Neighbourhood matrix {-}
But one can standardise, e.g. by row (W):
```{r}
nb2mat(nb1, style = "W")[1:4, 1:7] |> round(2)
```
## Neighbourhood matrix {-}
You can use `glist` argument of `nb2mat()` to replace the 0 / 1 value from the neighbours list by preset weights.
For example, calculate inverse distance weights and feed them to `nb2mat()`.
```{r}
dists <- nbdists(nb1, centroids)
head(dists)
```
## Neighbourhood matrix {-}
```{r}
ids <- lapply(dists, function(x) {1 / x})
head(ids)
```
## Neighbourhood matrix {-}
```{r}
nb2mat(nb1, glist = ids, style = "B")[1:4, 1:7]
```
## Neighbourhood matrix {-}
```{r}
nb2mat(nb1, glist = ids, style = "W")[1:4, 1:7]
```
## Meeting Videos {-}
### Cohort 1 {-}
`r knitr::include_url("https://www.youtube.com/embed/URL")`
<details>
<summary> Meeting chat log </summary>
```
LOG
```
</details>