Сопоставить два набора данных по минимальному геопространственному расстоянию (R)
У меня есть два следующих набора данных:
houses <- data.table(house_number = c(1:3),
lat_decimal = seq(1.1, 1.3, by = 0.1),
lon_decimal = seq(1.4, 1.6, by = 0.1))
stations <- data.table(station_numer = c(1:11),
lat_decimal = seq(1, 2, by = 0.1),
lon_decimal = seq(2, 3, by = 0.1))
Я хочу слить houses
а также stations
вместе, так что в результате station_number
это станция, которая ближе всего к соответствующему house_number
,
Этот вопрос очень похож, но я не уверен, что они работают с широтой и долготой, а также я не знаю, как рассчитать расстояния при работе с долготой и широтой (вот почему я предпочитаю просто использовать distm
от geosphere
пакет).
Я никогда не работал с внешней функцией. Если ответ на вышеупомянутый вопрос будет работать, как я могу адаптировать ответ, чтобы использовать distm
функция вместо sqrt
функционировать?
2 ответа
Использование match_nrst_haversine
от hutilscpp
:
library(hutilscpp)
houses[, c("station_number", "dist") := match_nrst_haversine(lat_decimal,
lon_decimal,
addresses_lat = stations$lat_decimal,
addresses_lon = stations$lon_decimal,
Index = stations$station_numer,
close_enough = 0,
cartesian_R = 5)]
houses
#> house_number lat_decimal lon_decimal station_number dist
#> 1: 1 1.1 1.4 1 67.62617
#> 2: 2 1.2 1.5 1 59.87076
#> 3: 3 1.3 1.6 1 55.59026
Вы можете настроить close_enough
а также cartesian_R
если ваши данные многочисленны (то есть более миллиона точек для соответствия) для производительности.
`cartesian_R`
Максимальный радиус любого адреса из точек, подлежащих геокодированию. Используется для ускорения обнаружения минимальных расстояний. Обратите внимание, что, как следует из названия аргумента, расстояние в декартовых координатах, поэтому, вероятно, небольшое число.
`close_enough`
Расстояние в метрах, ниже которого будет считаться совпадение. (Расстояние, которое считается "достаточно близким", чтобы соответствовать.)
Например, close_enough = 10 означает, что первое местоположение в пределах десяти метров будет сопоставлено, даже если более близкое совпадение произойдет позже.
Может быть представлен в виде строки для выделения единиц, например close_enough = "0.25km". Только км и м разрешены.
Ваш вопрос немного сложнее, чем простое слияние, и outer
несколько не подходит для этой цели. Чтобы быть как можно более тщательным, мы хотим рассчитать расстояние между всеми комбинациями домов и станций, а затем оставить только ближайшую станцию на дом. Нам понадобятся два пакета:
library(tidyverse)
library(geosphere)
Сначала немного подготовки. distm
ожидаем, что координаты будут упорядочены как первая долгота, вторая широта (у вас наоборот), так что давайте исправим это, назовем столбцы лучше и исправим опечатку, пока мы на ней:
houses <- data.frame(house_number = c(1:3),
lon_house = seq(1.4, 1.6, by = 0.1),
lat_house = seq(1.1, 1.3, by = 0.1)
)
stations <- data.frame(station_number = c(1:11),
lon_station = seq(2, 3, by = 0.1),
lat_station = seq(1, 2, by = 0.1)
)
Мы создадим "вложенные" фреймы данных, чтобы было проще хранить координаты вместе:
house_nest <- nest(houses, -house_number, .key = 'house_coords')
station_nest <- nest(stations, -station_number, .key = 'station_coords')
house_number house_coords
<int> <list>
1 1 <data.frame [1 × 2]>
2 2 <data.frame [1 × 2]>
3 3 <data.frame [1 × 2]>
station_number station_coords
<int> <list>
1 1 <data.frame [1 × 2]>
2 2 <data.frame [1 × 2]>
3 3 <data.frame [1 × 2]>
4 4 <data.frame [1 × 2]>
5 5 <data.frame [1 × 2]>
6 6 <data.frame [1 × 2]>
7 7 <data.frame [1 × 2]>
8 8 <data.frame [1 × 2]>
9 9 <data.frame [1 × 2]>
10 10 <data.frame [1 × 2]>
11 11 <data.frame [1 × 2]>
использование dplyr::crossing
объединить каждую строку из обоих фреймов данных:
data.master <- crossing(house_nest, station_nest)
house_number house_coords station_number station_coords
<int> <list> <int> <list>
1 1 <data.frame [1 × 2]> 1 <data.frame [1 × 2]>
2 1 <data.frame [1 × 2]> 2 <data.frame [1 × 2]>
3 1 <data.frame [1 × 2]> 3 <data.frame [1 × 2]>
4 1 <data.frame [1 × 2]> 4 <data.frame [1 × 2]>
5 1 <data.frame [1 × 2]> 5 <data.frame [1 × 2]>
6 1 <data.frame [1 × 2]> 6 <data.frame [1 × 2]>
7 1 <data.frame [1 × 2]> 7 <data.frame [1 × 2]>
8 1 <data.frame [1 × 2]> 8 <data.frame [1 × 2]>
9 1 <data.frame [1 × 2]> 9 <data.frame [1 × 2]>
10 1 <data.frame [1 × 2]> 10 <data.frame [1 × 2]>
# ... with 23 more rows
Имея все это сейчас, мы можем использовать distm
в каждом ряду рассчитать расстояние и сохранить кратчайшее расстояние на дом:
data.dist <- data.master %>%
mutate(dist = map2_dbl(house_coords, station_coords, distm)) %>%
group_by(house_number) %>%
filter(dist == min(dist))
house_number house_coords station_number station_coords dist
<int> <list> <int> <list> <dbl>
1 1 <data.frame [1 × 2]> 1 <data.frame [1 × 2]> 67690.
2 2 <data.frame [1 × 2]> 1 <data.frame [1 × 2]> 59883.
3 3 <data.frame [1 × 2]> 1 <data.frame [1 × 2]> 55519.