Оценивать ближайшее расстояние от одной точки между несколькими вариантами? - PullRequest
1 голос
/ 07 января 2020

У меня есть набор точек долготы / широты во фрейме данных с именем person_location

+----+-----------+-----------+
| id | longitude | latitude  |
+----+-----------+-----------+
|  1 | -76.67707 | 39.399754 |
|  2 | -76.44519 | 39.285084 |
|  3 | -76.69402 |  39.36958 |
|  4 | -76.68936 | 39.369907 |
|  5 | -76.58341 | 39.357994 |
+----+-----------+-----------+

Затем у меня есть другой набор точек долготы и широты в фрейме данных, который называется building_location:

+----+------------+-----------+
| id | longitude  | latitude  |
+----+------------+-----------+
|  1 | -76.624393 | 39.246464 |
|  2 | -76.457246 | 39.336996 |
|  3 | -76.711729 | 39.242936 |
|  4 | -76.631249 | 39.289103 |
|  5 | -76.566742 | 39.286271 |
|  6 | -76.683106 |  39.35447 |
|  7 | -76.530232 | 39.332398 |
|  8 | -76.598582 | 39.344642 |
|  9 | -76.691287 | 39.292849 |
+----+------------+-----------+

Я пытаюсь вычислить для каждого идентификатора в person_location, какой ближайший идентификатор находится в building_location. Я знаю, как рассчитать разницу между двумя отдельными точками, используя функцию distHaversine из library(geosphere), но как я могу получить оценку ближайшего расстояния от одной точки до набора из нескольких точек ?

Ответы [ 3 ]

3 голосов
/ 07 января 2020

Если вам нужно только ближайшее здание для каждого человека, и они относительно близки:

library(sf)

## load data here from @dcarlson's dput

person_location <- person_location %>%
  st_as_sf(coords = c('longitude', 'latitude')) %>%
  st_set_crs(4326)

building_location <- building_location %>%
  st_as_sf(coords = c('longitude', 'latitude')) %>%
  st_set_crs(4326)

st_nearest_feature(person_location, building_location)

#although coordinates are longitude/latitude, st_nearest_feature assumes that they #are planar
#[1] 6 2 6 6 8

Таким образом, люди 1,3 и 4 находятся ближе всего к зданию № 6. Человек 2 -> здание # 2 ...

Все расстояния можно рассчитать с помощью st_distance(person_location, building_location).

. Вы можете использовать библиотеку nngeo, чтобы легко найти кратчайшее расстояние для каждого человека.

library(nngeo)

st_connect(person_location, building_location) %>% st_length()
Calculating nearest IDs
  |===============================================================================================================| 100%
Calculating lines
  |===============================================================================================================| 100%
Done.
Units: [m]
[1] 5054.381 5856.388 1923.254 1796.608 1976.786

Вещи легче понять с помощью графика:

st_connect(person_location, building_location) %>% 
  ggplot() + 
    geom_sf() + 
    geom_sf(data = person_location, color = 'green') + 
    geom_sf(data = building_location, color = 'red')

ggplot people & bldgs

И еще проще на карте:

st_connect(person_location, building_location) %>% 
  mapview::mapview() +
  mapview::mapview(person_location, color = 'green', col.regions = 'green') + 
  mapview::mapview(building_location, color = 'black', col.regions = 'black')

mapview

геосфера, вероятно, более точна, но если вы имеете дело с относительно небольшими участками, эти инструменты, вероятно, достаточно хороши. Мне легче работать, и мне не всегда нужна предельная точность.

1 голос
/ 07 января 2020

Другим решением было бы объединить два data.frames и вычислить расстояние для каждой строки. Это может работать быстрее, чем для большего количества людей.

library(geosphere)
library(dplyr)


person_location <-
  structure(list(id = c(1, 2, 3, 4, 5), 
                 longitude = c(-76.67707, -76.44519, -76.69402, -76.68936, -76.58341), 
                 latitude = c(39.399754, 39.285084, 39.36958, 39.369907, 39.357994)), 
            class = "data.frame", row.names = c(NA, -5L))
building_location <-
  structure(list(id_building = c(1, 2, 3, 4, 5, 6, 7, 8, 9), 
                 longitude_building = c(-76.624393, -76.457246, -76.711729, -76.631249, -76.566742, -76.683106, -76.530232,  -76.598582, -76.691287), 
                 latitude_building = c(39.246464, 39.336996, 39.242936,39.289103, 39.286271, 39.35447, 39.332398, 39.344642, 39.292849)), 
            class = "data.frame", row.names = c(NA, -9L))

all_locations <- merge(person_location, building_location, by=NULL)

all_locations$distance <- distHaversine( 
  all_locations[, c("longitude", "latitude")],
  all_locations[, c("longitude_building", "latitude_building")]
  )

closest <- all_locations %>% 
  group_by(id) %>% 
  filter( distance == min(distance)  ) %>% 
  ungroup()

Created on 2020-01-07 by the reprex package (v0.3.0)
1 голос
/ 07 января 2020

Используйте dput() и вставьте результат в свой вопрос вместо таблиц:

person_location <-
structure(list(id = c(1, 2, 3, 4, 5), longitude = c(-76.67707, 
-76.44519, -76.69402, -76.68936, -76.58341), latitude = c(39.399754, 
39.285084, 39.36958, 39.369907, 39.357994)), class = "data.frame", row.names = c(NA, 
-5L))
building_location <-
structure(list(id = c(1, 2, 3, 4, 5, 6, 7, 8, 9), longitude = c(-76.624393, 
-76.457246, -76.711729, -76.631249, -76.566742, -76.683106, -76.530232, 
-76.598582, -76.691287), latitude = c(39.246464, 39.336996, 39.242936, 
39.289103, 39.286271, 39.35447, 39.332398, 39.344642, 39.292849
)), class = "data.frame", row.names = c(NA, -9L))

Для каждого человека вам нужно получить расстояние до каждого здания, а затем выбрать идентификатор минимального расстояния. Вот простая функция, которая делает это:

closest <- function(i) {
    idx <- which.min(distHaversine(person_location[i, 2:3], building_location[, 2:3]))  
    building_location[idx, "id"]
}

Теперь вам просто нужно запустить ее через всех людей:

sapply(seq_len(nrow(person_location)), closest)
# [1] 6 2 6 6 8
...