如何使用 doParallel 计算 R 中邮政编码之间的距离?
Posted
技术标签:
【中文标题】如何使用 doParallel 计算 R 中邮政编码之间的距离?【英文标题】:How to use doParallel for calculating distance between zipcodes in R? 【发布时间】:2019-12-26 05:35:24 【问题描述】:我有一个大型数据集(260 万行),其中包含两个邮政编码和相应的纬度和经度,我正在尝试计算它们之间的距离。我主要使用包geosphere
来计算邮政编码之间的文森蒂椭球距离,但我的数据集需要大量时间。有什么方法可以快速实现?
我尝试了什么
library(tidyverse)
library(geosphere)
zipdata <- select(fulldata,originlat,originlong,destlat,destlong)
## Very basic approach
for(i in seq_len(nrow(zipdata)))
zipdata$dist1[i] <- distm(c(zipdata$originlat[i],zipdata$originlong[i]),
c(zipdata$destlat[i],zipdata$destlong[i]),
fun=distVincentyEllipsoid)
## Tidyverse approach
zipdata <- zipdata%>%
mutate(dist2 = distm(cbind(originlat,originlong), cbind(destlat,destlong),
fun = distHaversine))
这两种方法都非常慢。我知道 210 万行永远不会是一个“快速”的计算,但我认为它可以做得更快。我在较小的测试数据上尝试了以下方法,但没有任何运气,
library(doParallel)
cores <- 15
cl <- makeCluster(cores)
registerDoParallel(cl)
test <- select(head(fulldata,n=1000),originlat,originlong,destlat,destlong)
foreach(i = seq_len(nrow(test))) %dopar%
library(geosphere)
zipdata$dist1[i] <- distm(c(zipdata$originlat[i],zipdata$originlong[i]),
c(zipdata$destlat[i],zipdata$destlong[i]),
fun=distVincentyEllipsoid)
stopCluster(cl)
谁能帮助我正确地使用doParallel
和geosphere
或更好的方法来处理这个问题?
编辑:(一些)回复的基准
## benchmark
library(microbenchmark)
zipsamp <- sample_n(zip,size=1000000)
microbenchmark(
dave =
# Dave2e
zipsamp$dist1 <- distHaversine(cbind(zipsamp$patlong,zipsamp$patlat),
cbind(zipsamp$faclong,zipsamp$faclat))
,
geohav =
zipsamp$dist2 <- geodist(cbind(long=zipsamp$patlong,lat=zipsamp$patlat),
cbind(long=zipsamp$faclong,lat=zipsamp$faclat),
paired = T,measure = "haversine")
,
geovin =
zipsamp$dist3 <- geodist(cbind(long=zipsamp$patlong,lat=zipsamp$patlat),
cbind(long=zipsamp$faclong,lat=zipsamp$faclat),
paired = T,measure = "vincenty")
,
geocheap =
zipsamp$dist4 <- geodist(cbind(long=zipsamp$patlong,lat=zipsamp$patlat),
cbind(long=zipsamp$faclong,lat=zipsamp$faclat),
paired = T,measure = "cheap")
,unit = "s",times = 100)
# Unit: seconds
# expr min lq mean median uq max neval cld
# dave 0.28289613 0.32010753 0.36724810 0.32407858 0.32991396 2.52930556 100 d
# geohav 0.15820531 0.17053853 0.18271300 0.17307864 0.17531687 1.14478521 100 b
# geovin 0.23401878 0.24261274 0.26612401 0.24572869 0.24800670 1.26936889 100 c
# geocheap 0.01910599 0.03094614 0.03142404 0.03126502 0.03203542 0.03607961 100 a
一个简单的all.equal
测试表明,对于我的数据集,haversine 方法等于 vincenty 方法,但与 geodist
包中的“便宜”方法具有“平均相对差异:0.01002573”。
【问题讨论】:
我已经做了一些基准测试here - 成对计算。总结是,geosphere
很慢,最好使用Rcpp
实现或geodist
包。还有启发它的twitter thread。
也许您可以将this answer 调整为类似的问题。
@Alexis 这太好了,我会尝试所有三个回复并在编辑中发布时间。
您甚至可以浏览其他距离的代码on GitHub。
@SymbolixAU 既然我决定接受你关于使用geodist
函数的建议,你能发表你的评论作为答案吗?
【参考方案1】:
如果您要使用 geosphere,我会使用像 distHaversine 这样的快速近似方法,或者仍然快速且非常精确的 distGeo 方法。 (distVincenty* 这些主要是为了好奇而实现的)。
【讨论】:
【参考方案2】:我使用@SymbolixAU 的建议使用geodist
包对我的数据集执行2.1M 距离计算。对于每个测试,我发现它比 geosphere
包要快得多(我在我的主要问题中添加了其中一个)。 geodist
中的 measure=cheap
选项使用便宜的标尺方法,该方法在 100 公里以下的距离内具有低错误率。有关更多信息,请参阅地质学家vignette。鉴于我的一些距离高于 100 公里,我决定使用文森蒂椭球测量。
【讨论】:
【参考方案3】:R 是一种向量化语言,因此该函数将对向量中的所有元素进行操作。由于您正在计算每一行的原始和目标之间的距离,因此循环是不必要的。矢量化方法大约是循环性能的 1000 倍。
同样直接使用distVincentyEllipsoid
(或distHaveersine等)并绕过distm
函数也应该可以提高性能。
没有任何样本数据,此 sn-p 未经测试。
library(geosphere)
zipdata <- select(fulldata,originlat,originlong,destlat,destlong)
## Very basic approach
zipdata$dist1 <- distVincentyEllipsoid(c(zipdata$originlong, zipdata$originlat),
c(zipdata$destlong, zipdata$destlat))
注意:为了使大多数地圈功能正常工作,正确的顺序是:先经度再纬度。
上面列出的 tidyverse 方法缓慢的原因是 distm
函数正在计算每个起点和终点之间的距离,这将导致 200 万乘 200 万元素矩阵。
【讨论】:
我将测试这个和@Symbolix 方法并发布更新。对 tidyverse 缓慢运行的见解非常有帮助。以上是关于如何使用 doParallel 计算 R 中邮政编码之间的距离?的主要内容,如果未能解决你的问题,请参考以下文章
使用 foreach 函数和 doParallel 库在 R 中嵌套 for 循环