Подтвердить что ты не робот

Географическое/геопространственное расстояние между 2 списками точек lat/lon (координаты)

У меня есть 2 списка (list1, list2) с широтой/долготами разных мест. Один список (list2) имеет имена местностей, которые list1 не имеет.

Я хочу приблизительную локальность для каждой точки в списке1. Поэтому я хочу взять точку в list1, попытаться найти ближайшую точку в list2 и принять эту локальность. Повторяю для каждой точки в list1. Он также хочет расстояние (в метрах) и индекс точки (в list1), поэтому я могу создать вокруг него некоторые бизнес-правила - по существу это 2 новых столбца, которые следует добавить в list1 (near_dist, indx).

Я использую функцию gdist, но я не могу заставить это работать с вводом данных.

Примеры входных списков:

list1 <- data.frame(longitude = c(80.15998, 72.89125, 77.65032, 77.60599, 
                                  72.88120, 76.65460, 72.88232, 77.49186, 
                                  72.82228, 72.88871), 
                    latitude = c(12.90524, 19.08120, 12.97238, 12.90927, 
                                 19.08225, 12.81447, 19.08241, 13.00984,
                                 18.99347, 19.07990))
list2 <- data.frame(longitude = c(72.89537, 77.65094, 73.95325, 72.96746, 
                                  77.65058, 77.66715, 77.64214, 77.58415,
                                  77.76180, 76.65460), 
                    latitude = c(19.07726, 13.03902, 18.50330, 19.16764, 
                                 12.90871, 13.01693, 13.00954, 12.92079,
                                 13.02212, 12.81447), 
                    locality = c("A", "A", "B", "B", "C", "C", "C", "D", "D", "E"))
4b9b3361

Ответ 1

Чтобы вычислить географическое расстояние между двумя точками с координатами широты/долготы, вы можете использовать несколько формул. Для расчета расстояния пакет geosphere имеет значения distCosine, distHaversine, distVincentySphere и distVincentyEllipsoid. Из них distVincentyEllipsoid считается наиболее точным, но вычислительно более интенсивным, чем другие.

С помощью одной из этих функций вы можете сделать матрицу расстояний. На основе этой матрицы вы можете назначить имена locality на основе кратчайшего расстояния с помощью which.min и соответствующего расстояния с помощью min (см. Последнюю часть ответа) следующим образом:

library(geosphere)

# create distance matrix
mat <- distm(list1[,c('longitude','latitude')], list2[,c('longitude','latitude')], fun=distVincentyEllipsoid)

# assign the name to the point in list1 based on shortest distance in the matrix
list1$locality <- list2$locality[max.col(-mat)]

это дает:

> list1
   longitude latitude locality
1   80.15998 12.90524        D
2   72.89125 19.08120        A
3   77.65032 12.97238        C
4   77.60599 12.90927        D
5   72.88120 19.08225        A
6   76.65460 12.81447        E
7   72.88232 19.08241        A
8   77.49186 13.00984        D
9   72.82228 18.99347        A
10  72.88871 19.07990        A

Другая возможность - назначить locality на основе средних значений долготы и широты locality в list2:

library(dplyr)
list2a <- list2 %>% group_by(locality) %>% summarise_each(funs(mean)) %>% ungroup()
mat2 <- distm(list1[,c('longitude','latitude')], list2a[,c('longitude','latitude')], fun=distVincentyEllipsoid)
list1 <- list1 %>% mutate(locality2 = list2a$locality[max.col(-mat2)])

или с помощью data.table:

library(data.table)
list2a <- setDT(list2)[,lapply(.SD, mean), by=locality]
mat2 <- distm(setDT(list1)[,.(longitude,latitude)], list2a[,.(longitude,latitude)], fun=distVincentyEllipsoid)
list1[, locality2 := list2a$locality[max.col(-mat2)] ]

это дает:

> list1
   longitude latitude locality locality2
1   80.15998 12.90524        D         D
2   72.89125 19.08120        A         B
3   77.65032 12.97238        C         C
4   77.60599 12.90927        D         C
5   72.88120 19.08225        A         B
6   76.65460 12.81447        E         E
7   72.88232 19.08241        A         B
8   77.49186 13.00984        D         C
9   72.82228 18.99347        A         B
10  72.88871 19.07990        A         B

Как вы можете видеть, это приводит в большинстве случаев (7 из 10) к другому назначенному locality.


Вы можете добавить расстояние с помощью:

list1$near_dist <- apply(mat2, 1, min)

или другой подход с max.col (что очень вероятно быстрее):

list1$near_dist <- mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)]

# or using dplyr
list1 <- list1 %>% mutate(near_dist = mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)])
# or using data.table (if not already a data.table, convert it with 'setDT(list1)' )
list1[, near_dist := mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)] ]

результат:

> list1
    longitude latitude locality locality2   near_dist
 1:  80.15998 12.90524        D         D 269966.8970
 2:  72.89125 19.08120        A         B  65820.2047
 3:  77.65032 12.97238        C         C    739.1885
 4:  77.60599 12.90927        D         C   9209.8165
 5:  72.88120 19.08225        A         B  66832.7223
 6:  76.65460 12.81447        E         E      0.0000
 7:  72.88232 19.08241        A         B  66732.3127
 8:  77.49186 13.00984        D         C  17855.3083
 9:  72.82228 18.99347        A         B  69456.3382
10:  72.88871 19.07990        A         B  66004.9900

Ответ 2

Кредиты Мартину Харринге для этого решения по облегчению этого способа, когда вам нужна эта функция, выполняются путем перемещения по кадру данных на блоге Mark Needham

library(dplyr)

df %>%
rowwise() %>%
mutate(newcolumn_distance = distHaversine(c(df$long1, df$lat1), 
                                          c(df$long2, df$lat2)))

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