How do I use a Nearest Neighbor function as edges for a network analysis using the sf_network package in R?

226 views Asked by At

New to R and to coding in general.

I'm attempting to explore the spatial properties of cities using network analysis. I am unable to share the data at this time.

I have a shapefile with various points. Using the sf and sf_network packages, how would I use the output of a st_nearest_points(x,y) against the same dataset as the connections in a network analysis?

Any advice/help/wisdom would be appreciated. This is about as far as I've gotten.


    library(sf)
    library(sfnetworks)

    p1 = st_point(c(7, 51))
    p2 = st_point(c(7, 52))
    p3 = st_point(c(8, 52))
    p4 = st_point(c(9, 40))

    nodes = st_as_sf(st_sfc(p1, p2, p3, p4, crs = 4326))
    nodes_2 = nodes

    edges = st_nearest_points(nodes, nodes_2)
    edges$to= c(st_cast(edges,"POINT"))
    edges$from= c(st_cast(edges,"POINT"))

    sfnetwork(nodes, edges)


1

There are 1 answers

0
mrhellmann On

Using nngeo::st_nn() works well to find the nearest neighbors to build an sfnetwork object. https://michaeldorman.github.io/nngeo/index.html

The below code takes the four points as nodes, connects each to its two closest neighbors, and creates a network from the resulting connections. Depending on your data, the number of connections may need to be adjusted. A problem could arise with clusters of points distant enough from others to create an island separated from the rest of the points.

library(sf)
library(sfnetworks)
library(nngeo)

# Your data
p1 = st_point(c(7, 51))
p2 = st_point(c(7, 52))
p3 = st_point(c(8, 52))
p4 = st_point(c(9, 40))

nodes = st_as_sf(st_sfc(p1, p2, p3, p4, crs = 4326))

# find the nearest neighbors for each point.
#  adjust 'k' to one more than the number of connections you want each point to have
nn_list <- st_nn(nodes, nodes, k = 3, progress = F)

# Printing nn_list shows that there's a connection from each point
#  to itself. The next step fixes that.
nn_list
#> [[1]]
#> [1] 1 2 3
#> 
#> [[2]]
#> [1] 2 3 1
#> 
#> [[3]]
#> [1] 3 2 1
#> 
#> [[4]]
#> [1] 4 1 3

# the same as above, but since each point is closest to itself I removed
#  the self-referential point
nn_list <- st_nn(nodes, nodes, k = 3) %>%
  purrr::map(tail, -1)


nn_list
#> [[1]]
#> [1] 2 3
#> 
#> [[2]]
#> [1] 3 1
#> 
#> [[3]]
#> [1] 2 1
#> 
#> [[4]]
#> [1] 1 3

# Use the nodes & thier closest neighbors to create a network.
#  each node will be connected to the 2 closest points (excluding itself).
network <- as_sfnetwork(st_connect(nodes, nodes, ids = nn_list))


network
#> # A sfnetwork with 4 nodes and 8 edges
#> #
#> # CRS:  EPSG:4326 
#> #
#> # A directed simple graph with 1 component with spatially explicit edges
#> #
#> # Node Data:     4 × 1 (active)
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: 7 ymin: 40 xmax: 9 ymax: 52
#>             x
#>   <POINT [°]>
#> 1      (7 51)
#> 2      (7 52)
#> 3      (8 52)
#> 4      (9 40)
#> #
#> # Edge Data:     8 × 3
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 7 ymin: 40 xmax: 9 ymax: 52
#>    from    to                x
#>   <int> <int> <LINESTRING [°]>
#> 1     1     2     (7 51, 7 52)
#> 2     1     3     (7 51, 8 52)
#> 3     2     3     (7 52, 8 52)
#> # … with 5 more rows

ggplot2::autoplot(network)

Created on 2022-11-07 with reprex v2.0.2