我有一个网格化数据集,可在以下位置获得数据:
lon <- seq(-179.75,179.75, by = 0.5)
lat <- seq(-89.75,89.75, by = 0.5)
我想找到距该地点500公里范围内的所有数据点:
mylat <- 47.9625
mylon <- -87.0431
我的目标是在R中使用geosphere包,但我目前编写的方法效率似乎不高:
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
}
}
在这里,我遍历数据中的每个网格,并查找距离是否小于500 km。然后我存储一个TRUE或FALSE变量,然后我可以使用它来平均数据(其他变量)。从这个方法,我想要一个TRUE或FALSE的矩阵,用于距离lat和lon 500公里范围内的位置。有没有更有效的方法来做到这一点?
答案 0 :(得分:7)
<强>时序:强>
比较@ nicola和我的版本给出:
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
我原来的解决方案:(恕我直言,尼古拉的第二个版本更清洁,更快。)
您可以执行以下操作(以下说明)
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
}
}
}
说明:
outer_loop_state
初始化为0.如果找到圆圈内至少有一个光栅点的行outer_loop_state
设置为1.一旦给定圆圈内没有更多的点第i
行。
@nicola版本中的distm
调用基本上没有这个技巧。所以它计算所有行。
时间代码:
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}
)
答案 1 :(得分:5)
dist*
包的geosphere
函数是矢量化的,因此您只需要更好地准备输入。试试这个:
#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
正如@ Floo0回答所示,有很多不必要的计算。我们可以遵循另一种策略:我们首先确定可以接近阈值的lon和lat范围,然后我们仅使用它们来计算距离:
#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
通过这种方式,您只计算lg+ln+lg*ln
(lg
和ln
的长度为latgood
和longood
),即531距离,与用我之前的方法完成259200。
答案 2 :(得分:1)
我在下面添加了使用spatialrisk软件包的解决方案。此软件包中的关键功能是用C ++(Rcpp)编写的,因此非常快。
首先,加载数据:
mylat <- 47.9625
mylon <- -87.0431
lon <- seq(-179.75,179.75, by = 0.5)
lat <- seq(-89.75,89.75, by = 0.5)
df <- expand.grid(lon = lon, lat = lat)
spatialrisk :: points_in_circle()函数计算从中心点开始的半径范围内的观测值。请注意,距离是使用Haversine公式计算的。
与@Hugh版本相比,空间风险方法的计时:
spatialrisk::points_in_circle(df, mylon, mylat, radius = 5e5)
Unit: milliseconds
expr min lq mean median uq max neval cld
spatialrisk 3.071897 3.366256 5.224479 4.068124 4.809626 17.24378 100 a
hutils 17.507311 20.788525 29.470707 25.061943 31.066139 268.29375 100 b
结果可以轻松转换为矩阵。
看看@philcolbourn提供的关于如何测试点是否在圆内的出色答案。参见:https://stackoverflow.com/a/7227057/5440749
答案 3 :(得分:0)
只需直接使用hutils::haversine_distance(lat, lon, mylat, mylon) < 500
。
它的速度和鲁棒性改进了现有答案。特别是,它不依赖于数据的网格化性质,并且可以与长坐标向量一起工作。以下是100,000点的计时
# 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