Distancia geográfica/geoespacial entre 2 listas de puntos de latitud/longitud (coordenadas)

Resuelto grammar asked hace 54 años • 3 respuestas

Tengo 2 listas ( list1, list2) con latitud/longitud de varias ubicaciones. Una lista ( list2) tiene nombres de localidades que list1no tiene.

También quiero una localidad aproximada para cada punto de la lista1. Entonces quiero tomar un punto en list1, intentar buscar el punto más cercano list2y tomar esa localidad. Repito para cada punto en list1. También quiere la distancia (en metros) y el índice del punto (en list1), para poder crear algunas reglas comerciales a su alrededor; esencialmente, estas son 2 nuevas columnas que deben agregarse a list1( near_dist, indx).

Estoy usando la gdistfunción, pero no puedo hacer que funcione con entradas de marco de datos.

Listas de entrada de ejemplo:

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"))
grammar avatar Jan 01 '70 08:01 grammar
Aceptado

Para calcular la distancia geográfica entre dos puntos con coordenadas de latitud/longitud, puede utilizar varias fórmulas. El paquete geospheretiene distCosine, distHaversiney distVincentySpherepara distVincentyEllipsoidcalcular la distancia. De estos, el distVincentyEllipsoidse considera el más preciso, pero es computacionalmente más intensivo que los demás.

Con una de estas funciones, puedes hacer una matriz de distancias. Con base en esa matriz, puede asignar localitynombres según la distancia más corta which.miny la distancia correspondiente min(consulte la última parte de la respuesta) de esta manera:

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)]

esto da:

> 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

Otra posibilidad es asignar el localitybasado en los valores promedio de longitud y latitud de la localitys en 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)])

o con 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)] ]

esto da:

> 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

Como puedes ver, esto lleva en la mayoría de ocasiones (7 de cada 10) a otro asignado locality.


Puedes sumar la distancia con:

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

u otro enfoque con max.col(que es muy probable más rápido):

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)] ]

el resultado:

> 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
Jaap avatar Jul 28 '2015 05:07 Jaap

Créditos a Martin Haringa por esta solución para hacer que este camino sea más fácil cuando necesita que esta función se realice atravesando un marco de datos en el blog de Mark Needham.

library(dplyr)
library(geosphere)

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

Probé usando las dos funciones distm y distHaversine por separado en muestras grandes de conjuntos de datos del mundo real, y distHaversine parece funcionar mucho más rápido que la función distm. Me sorprende porque pensé que los dos eran simplemente la misma función en dos formatos.

Alexander Kielland avatar Nov 30 '2018 14:11 Alexander Kielland