[英]Finding shortest distance between two sets of points ( latitude and longitude) points in R
[英]How to speed up code that finds shortest driving time between two sets of points using osrm
假設我有兩組坐標,A 和 B。我的目標是,對於 A 的每個元素,找到 B 中行駛時間最短的元素(保留 B 的索引、行駛時間和距離)。 根據@Ben 在這個問題( 計算 R 中多個坐標的距離)中的回答,我想出了下面的代碼。 我的問題是如何讓它更快。
library(osrm)
library(sf)
apotheke.df <- st_read(system.file("gpkg/apotheke.gpkg", package = "osrm"),
quiet = TRUE)
points <- data.frame(
id = 1:3,
longitude = c(13.4, 13.5, 13.3),
latitude = c(52.4, 52.5, 52.3)
)
route <- list()
#overall goal: for each point, find distance to nearest apotheke
#find distance from each of the 3 points to the first apotheke
for (x in 1:nrow(points)) {
route[[x]] <- osrmRoute(src = c(points$longitude[x], points$latitude[x]),
dst = apotheke.df[1,],
overview = FALSE, osrm.profile = "car")
#add index
route[[x]][3] <- 1
}
#replace if duration is less than the lowest one
for (x in 1:nrow(points)) {
for(y in 2:nrow(apotheke.df)) {
temp <- osrmRoute(src = c(points$longitude[x], points$latitude[x]),
dst = apotheke.df[y,],
overview = FALSE, osrm.profile = "car")
temp[3] <- y
print(y)
print(temp)
if(temp[2] < route[[x]][2]) route[[x]] <- temp
}
}
do.call(rbind, route)
結果:
duration distance
[1,] 3.52 1.84 18
[2,] 2.05 1.00 14
[3,] 17.10 17.76 76
在我的實際應用中,一組點有150個左右,另一組有幾千幾千。 這需要很長時間才能完成。 這引出了我的問題——我怎樣才能加快上面的代碼?
我最好的猜測是使用並行處理(盡管代碼的其他方面可能很慢),但我不擅長弄清楚如何做到這一點。 它可能與這些問題有關:1) 在 R (OSRM) 中並行化 API和 2) 如何將 osrm function 應用於 dataframe 的每一行。
這是一個可重現的例子,詳細說明了我的評論。
source
和dest
數據:這只是為了回答的目的。
library(osrm)
source <- data.frame(
id = 1:3,
longitude = c(13.4, 13.5, 13.3),
latitude = c(52.4, 52.5, 52.3)
)
dest <- data.frame(
id = 4:5,
longitude = c(13.9, 13.6),
latitude = c(52.2, 52.4)
)
使用osrmTable()
而不是循環遍歷數據並每次調用osrmRoute()
將節省大量時間。 如果您使用的是遠程服務器而不是托管您自己的服務器,則尤其如此,因為每個請求都通過 inte.net 傳輸。 使用osrmTable()
向服務器發出對整個表的一次請求,而不是對每一對坐標的請求。
distances <- osrmTable(
src = source[c("longitude", "latitude")],
dst = dest[c("longitude", "latitude")],
osrm.profile = "car"
)
distances[["durations"]]
# 1 2
# 1 60.6 25.7
# 2 71.4 25.4
# 3 55.7 25.3
即使只有 100 個源坐標和 100 個目的地,R 也需要解釋一個命令,發出一個 .network 請求並執行一個高級分配 ( <-
) 操作,而不是 10,000 個,如果您單獨迭代每個操作。
同樣,如果您跳過循環,它會更快。 我將在示例中使用data.table
,因為它很快。 首先,將距離matrix
變為data.table
並使用melt()
將其變為長格式:
library(data.table)
distances_dt <- data.table(
source = source$id,
distances[["durations"]],
key = "source"
) |>
setnames(
seq_len(nrow(dest)) + 1,
as.character(dest$id)
) |>
melt(
id.vars = "source",
variable.name = "dest",
value.name = "distance"
)
# source dest distance
# <int> <fctr> <num>
# 1: 1 4 60.6
# 2: 2 4 71.4
# 3: 3 4 55.7
# 4: 1 5 25.7
# 5: 2 5 25.4
# 6: 3 5 25.3
然后我們可以簡單地找到每個源的最小距離:
distances_dt[,
.(min_distance = min(distance)),
by = source
]
# source min_distance
# <int> <num>
# 1: 1 25.7
# 2: 2 25.4
# 3: 3 25.3
osrm-backend
實例我可以預見的唯一問題是,如果您使用默認的 R osrm
package 服務器,而不是本地服務器,您將收到速率限制錯誤。 根據 R osrm
package文檔:
OSRM 演示服務器不允許大型查詢(超過 10000 個距離或持續時間)。
如果您不確定您是否在托管自己的服務器,那么您可能不是。 您可以通過運行getOption("osrm.server")
檢查 R session 中的默認服務器。 默認值為"https://routing.openstreetmap.de/"
。
如果您在正在使用的服務器上發出大於 max-table-size 參數的請求,您將收到以下響應:
{"code":"TooBig","message":"Too many table coordinates"}
如果是這種情況,那么如果可行的話,您可以將數據分解成更小的塊。
或者,您可以運行自己的 osrm-backend 實例。 如果您打算定期計算距離,或者一次計算很多距離,我建議您這樣做。 有兩種方法:
max-table-size
參數 (Linux/Mac)這是我幾年前遵循的教程,我認為這是一個很好的方法。 這些說明適用於 Ubuntu,但只需進行少量更改即可在其他流行的 Linux 版本或 MacOS 上安裝。
您需要更改教程中的一行。 而不是運行:
osrm-routed map.xml.osrm
你可以運行:
osrm-routed map.xml.osrm --max-table-size 100000
(或者任何足夠大的數字。)
或者,如果您正在運行 Windows,或者您對 Docker 感到滿意,那么使用Docker 圖像可能更容易。 即使您以前沒有使用過 Docker, 這也是一套很好的說明。
如果您在 Docker 容器中運行 osrm-backend 實例,您可以通過將其傳遞給docker run
來更改max-table-size
參數,按照此處所述運行,其語法類似於:
docker run -t -i -p 5000:5000 -v "${PWD}:/data" osrm/osrm-backend osrm-routed --algorithm mld --max-table-size 10000 /data/berlin-latest.osrm
在任何一種情況下,一旦您設置了自己的 osrm 路由實例,就需要告訴 R osrm
package 使用它。 如果它在你的localhost
的端口 5000 上運行,你可以這樣做:
options(osrm.server = "http://127.0.0.1:5000/")
您可以在此處查看將本地 osrm 路由實例與 R osrm
package 集成的示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.