Ближайшая точка между несколькими точками

#r

#r

Вопрос:

У меня есть два списка, один содержит id и для каждого id набор координат:

  ------- ------ ------ 
| store | lat  | lon  |
 ------- ------ ------ 
|   123 | 37.2 | 13.5 |
|   456 | 39.1 |  9.1 |
|  789  | 45.4 | 11.0 |
 ------- ------ ------ 
  

В то время как вторая представляет собой список метеостанций с координатами и некоторыми другими данными:

  ---- -------- -------- --------------- ---------------- 
| id |  lat   |  lon   |     name      |    address     |
 ---- -------- -------- --------------- ---------------- 
|  1 | 44.907 |  8.612 | airport_one   | bond street    |
|  2 | 39.930 | 9.720  | airport_two   | oxford street  |
| 3  | 40.020 | 15.280 | airport_three | ellesmere road |
 ---- -------- -------- --------------- ---------------- 
  

Я хотел бы добавить два столбца в первый список (список магазинов) с расстоянием и названием ближайшего аэропорта, поэтому мне нужно сравнить каждый магазин с каждым аэропортом и вернуть кратчайшее расстояние.

Я пытался достичь этого в цикле for, используя distm функцию, но я определенно что-то теряю:

 for (val in 1:length(airport_master[,1])){

  n <- distm(store_master[1,3:2], airport_master[val,6:5])
  distances <- append(distances, n)
  store_master$closest_airport <- airport_master$name[val])

}
  

Есть ли какая-либо библиотека или лучший способ достичь этого результата?

Ответ №1:

Вы можете сделать это следующим образом, используя tidyverse пакет:

 library(tidyverse)

# data

store_master <-
  tibble(
    'store' = c(123, 456, 789),
    'lat'   = c(37.2, 39.1, 45.4),
    'lon'   = c(13.5, 9.1, 11.0)
  )

airport_master <-
  tibble(
    'id' = 1:3,
    'lat' = c(44.907, 39.93, 40),
    'lon' = c(8.612, 9.72, 15.28),
    'name' = c('airport_one', 'airport_two', 'airport_three')
  )

# solution

crossing(
  store = store_master$store,
  id = airport_master$id
) %>%
  left_join(store_master, "store") %>%
  left_join(airport_master, "id", suffix = c("_store", "_airpot")) %>%
  mutate(distance = sqrt((lat_store - lat_airpot)^2   (lon_store - lon_airpot)^2)) %>%
  group_by(store) %>%
  filter(distance == min(distance))
  

Результат:

   store    id lat_store lon_store lat_airpot lon_airpot name          distance
  <dbl> <int>     <dbl>     <dbl>      <dbl>      <dbl> <chr>            <dbl>
1   123     3      37.2      13.5       40        15.3  airport_three     3.32
2   456     2      39.1       9.1       39.9       9.72 airport_two       1.04
3   789     1      45.4      11         44.9       8.61 airport_one       2.44
  

Ответ №2:

В моем решении использовалась функция pdist из библиотеки pdist

 ### Store 
library(pdist)
dat1 <- cbind('store' = c(123, 456, 789),
              'lat'   = c(37.2, 39.1, 45.4),
              'lon'   = c(13.5, 9.1, 11.0))

dat2 <- cbind('id' = 1:3,
              'lat' = c(44.907, 39.93, 40),
              'lon' = c(8.612, 9.72, 15.28))


dist.mat <- as.matrix(pdist(dat1[, 2:3], dat2[,2:3]))
dat2[apply(dist.mat, 1, which.min), 1] ## Or name 

### Combining the result with the first data set 
data.frame(dat1,
           'ClosestID' = dat2[apply(dist.mat, 1, which.min), 1])
  

Комментарии:

1. Большое спасибо, но как я могу создать столбец в da2 с именем?

2. Просто измените столбец, взятый в качестве имени. Для связывания столбцов вместе использовался либо cbind, либо data.frame.

3. Я совсем новичок в R, не могли бы вы, пожалуйста, показать мне пример?