sidebarpanel div app r geosphere

div - sidebarpanel shiny



Encuentre ubicaciones dentro de cierta distancia lat/lon en r (3)

Las funciones dist* del paquete de geosphere están vectorizadas, por lo que solo necesita preparar mejor sus entradas. Prueba esto:

#prepare a matrix with coordinates of every position allCoords<-cbind(lon,rep(lat,each=length(lon))) #call the dist function and put the result in a matrix res<-matrix(distm(cbind(mylon,mylat),allCoords,fun=distHaversine)<=500000,nrow=length(lon)) #check the result identical(res,dd2) #[1] TRUE

Como mostró la respuesta @ Floo0, hay muchos cálculos innecesarios. Podemos seguir otra estrategia: primero determinamos el rango de lon y lat que puede estar más cerca que el umbral y luego los usamos solo para calcular la distancia:

#initialize the return res<-matrix(FALSE,nrow=length(lon),ncol=length(lat)) #we find the possible values of longitude that can be closer than 500000 #How? We calculate the distances between us and points with our same lon longood<-which(distm(c(mylon,mylat),cbind(lon,mylat))<=500000) #Same for latitude latgood<-which(distm(c(mylon,mylat),cbind(mylon,lat))<=500000) #we build the matrix with only those values to exploit the vectorized #nature of distm allCoords<-cbind(lon[longood],rep(lat[latgood],each=length(longood))) res[longood,latgood]<-distm(c(mylon,mylat),allCoords)<=500000

De esta manera, calculas solo lg+ln+lg*ln ( lg y ln son la longitud de latgood y longood ), es decir, 531 distancias, opuestas a las 259200 con mi método anterior.

Tengo un conjunto de datos de cuadrícula, con datos disponibles en las siguientes ubicaciones:

lon <- seq(-179.75,179.75, by = 0.5) lat <- seq(-89.75,89.75, by = 0.5)

Me gustaría encontrar todos los puntos de datos que se encuentran a 500 km de la ubicación:

mylat <- 47.9625 mylon <- -87.0431

Mi objetivo es usar el paquete geosphere en R, pero el método que he escrito actualmente no parece muy eficiente:

require(geosphere) dd2 <- array(dim = c(length(lon),length(lat))) for(i in 1:length(lon)){ for(ii in 1:length(lat)){ clon <- lon[i] clat <- lat[ii] dd <- as.numeric(distm(c(mylon, mylat), c(clon, clat), fun = distHaversine)) dd2[i,ii] <- dd <= 500000 } }

Aquí, recorro cada cuadrícula en los datos y encuentro si la distancia es inferior a 500 km. Luego almaceno una variable con VERDADERO o FALSO, que luego puedo usar para promediar los datos (otra variable). A partir de este método, quiero una matriz con VERDADERO o FALSO para las ubicaciones dentro de los 500 km del lat y lon que se muestran. ¿Hay un método más eficiente para hacer esto?


Solo use hutils::haversine_distance(lat, lon, mylat, mylon) < 500 directamente.

Mejora las respuestas existentes por su rapidez y robustez. En particular, no se basa en la naturaleza reticulada de los datos y funcionará con vectores largos de coordenadas. A continuación se muestran los tiempos de 100.000 puntos

# A tibble: 2 x 14 expression min mean median max `itr/sec` mem_alloc n_gc n_itr total_time <chr> <bch:tm> <bch:tm> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <bch:tm> 1 nicola2 39891.120ms 39891.120ms 39891.120ms 39891.120ms 0.0251 8808.632MB 0 1 39891.120ms 2 hutils 15.492ms 15.591ms 15.578ms 15.728ms 64.1 5.722MB 0 33 514.497ms


Tiempos:

Comparando @ nicola''s y mi versión da:

Unit: milliseconds min lq mean median uq max neval nicola1 184.217002 219.924647 297.60867 299.181854 322.635960 898.52393 100 floo01 61.341560 72.063197 97.20617 80.247810 93.292233 286.99343 100 nicola2 3.992343 4.485847 5.44909 4.870101 5.371644 27.25858 100

Mi solución original: (la segunda versión de Nicola Nicola es mucho más limpia y rápida).

Puedes hacer lo siguiente (explicación abajo)

require(geosphere) my_coord <- c(mylon, mylat) dd2 <- matrix(FALSE, nrow=length(lon), ncol=length(lat)) outer_loop_state <- 0 for(i in 1:length(lon)){ coods <- cbind(lon[i], lat) dd <- as.numeric(distHaversine(my_coord, coods)) dd2[i, ] <- dd <= 500000 if(any(dd2[i, ])){ outer_loop_state <- 1 } else { if(outer_loop_state == 1){ break } } }

Explicación:

Para el bucle aplico la siguiente lógica:

outer_loop_state se inicializa con 0. Si se encuentra una fila con al menos un punto de trama dentro del círculo, la outer_loop_state está establecida en 1. Una vez que no haya más puntos dentro del círculo para una fila dada, se romperá.

La llamada distm en la versión @nicola básicamente hace lo mismo sin este truco. Entonces calcula todas las filas.

Código de tiempos:

microbenchmark::microbenchmark( {allCoords<-cbind(lon,rep(lat,each=length(lon))) res<-matrix(distm(cbind(mylon,mylat),allCoords,fun=distHaversine)<=500000,nrow=length(lon))}, {my_coord <- c(mylon, mylat) dd2 <- matrix(FALSE, nrow=length(lon), ncol=length(lat)) outer_loop_state <- 0 for(i in 1:length(lon)){ coods <- cbind(lon[i], lat) dd <- as.numeric(distHaversine(my_coord, coods)) dd2[i, ] <- dd <= 500000 if(any(dd2[i, ])){ outer_loop_state <- 1 } else { if(outer_loop_state == 1){ break } } }}, {#intitialize the return res<-matrix(FALSE,nrow=length(lon),ncol=length(lat)) #we find the possible value of longitude that can be closer than 500000 #How? We calculate the distance between us and points with our same lat longood<-which(distm(c(mylon,mylat),cbind(lon,mylat))<500000) #Same for latitude latgood<-which(distm(c(mylon,mylat),cbind(mylon,lat))<500000) #we build the matrix with only those values to exploit the vectorized #nature of distm allCoords<-cbind(lon[longood],rep(lat[latgood],each=length(longood))) res[longood,latgood]<-distm(c(mylon,mylat),allCoords)<=500000} )