Find closest neighboring point and list of neighbors within a given radius, coordinates lat-long

Viewed 57

I am attempting to match/link 2 datasets, each of which have a lat lon. I'd like to produce the closest n matches by distance for each given coordinate in the first dataset. I am able to get the closest match but not sure how to expand this to get n number of matches.

The datasets look like this- table_1

id_1 lat_1 lon_
a1 50.8613159 1.2483
d7 50.8526967 1.2566349
s2 50.8666 1.2433232

table_2

id_2 lat_2 lon_2
x2 50.8713562548622 1.24447448004003
r1 50.8548464 1.2402125971721
o9 50.87906755026 1.2453153747299

The code i've been using to determine the closest match has been finding the euclidean dist from the RANN package.

table_1[, c(3, 4)] <- as.data.frame(RANN::nn2(table_1[, c("lat_1", "lon_1")],
                                              table_2[, c("lat_2", "lon_2")], k=1))

which returns an index number and distance. Ideally, i'd like my output to look like-

For 3 closest matches :

id_1 lat_1 lon_1 index_no
a1 50.8613159 1.2483 3
a1 50.8613159 1.2483 2
a1 50.8613159 1.2483 1

Where the last column index_no provides the index number of the closest coordinates from table_2, and populate id_1 3 times to provide the 3 closest matches in any order by dist in km or m.

1 Answers

Use k=3 and cbind every row to "dat1" using apply(). Note that the data in which the neighbors are to be found should be put first (for illustration I extended your second data set by one line).

r <- RANN::nn2(dat1[, c("lat_1", "lon_1")], 
               dat2[, c("lat_2", "lon_2")], k=4)$nn.idx |>
  apply(1, cbind, dat1) |>
  do.call(what=rbind) |>
  {\(.) {names(.)[1] <- 'index_no';.[order(.$id), c(2:3, 1)]}}()  ## optional step, 
                                                                  ## sort and 
                                                                  ## rename cols

r
#   id_1    lat_1 index_no
# 1   a1 50.86132        2
# 4   a1 50.86132        4
# 7   a1 50.86132        1
# 2   d7 50.85270        1
# 5   d7 50.85270        2
# 8   d7 50.85270        2
# 3   s2 50.86660        4
# 6   s2 50.86660        1
# 9   s2 50.86660        3

Note: For the |> pipes you need an R version at least 4.1. If you are condemned to use an old R version, try:

do.call(apply(RANN::nn2(dat1[, c("lat_1", "lon_1")], 
                        dat2[, c("lat_2", "lon_2")], k = 4)$nn.idx, 
              1, cbind, dat1), what = rbind)

Data:

dat1 <- structure(list(id_1 = c("a1", "d7", "s2"), lat_1 = c(50.8613159, 
50.8526967, 50.8666), lon_1 = c(1.2483, 1.2566349, 1.2433232)), class = "data.frame", row.names = c(NA, 
-3L))

dat2 <- structure(list(id_2 = c("x2", "r1", "o9", "xy"), lat_2 = c(50.8713562548622, 
50.8548464, 50.87906755026, 50.85), lon_2 = c(1.24447448004003, 
1.2402125971721, 1.2453153747299, 1.25)), class = "data.frame", row.names = c(NA, 
-4L))
Related