簡體   English   中英

如何計算R中低於某個閾值的2個坐標之間的距離?

[英]How to calculate distance between 2 coordinates below a certain threshold in R?

我有 44,000 個美國郵政編碼,它是 R 中相應的質心緯度/經度。這是來自 R 中的“郵政編碼”包。我需要計算每個郵政編碼之間的距離並保持這些距離小於 5 英里。 問題是計算郵政編碼之間的所有距離,我必須創建一個大小為 44,000x44,0000 的向量,由於空間問題,我無法創建該向量。

我檢查了 R 中的帖子,最接近我的要求的是吐出 2 個數據集之間的最小距離的經緯度/經度

DB1 <- data.frame(location_id=1:7000,LATITUDE=runif(7000,min = -90,max = 90),LONGITUDE=runif(7000,min = -180,max = 180))
DB2 <- data.frame(location_id=7001:12000,LATITUDE=runif(5000,min = -90,max = 90),LONGITUDE=runif(5000,min = -180,max = 180))

DistFun <- function(ID){
  TMP <- DB1[DB1$location_id==ID,]
  TMP1 <- distGeo(TMP[,3:2],DB2[,3:2])
  TMP2 <- data.frame(DB1ID=ID,DB2ID=DB2[which.min(TMP1),1],DistanceBetween=min(TMP1)      ) 
  print(ID)
  return(TMP2)
}

DistanceMatrix <- rbind_all(lapply(DB1$location_id, DistFun))

即使我們可以修改上面的代碼以包含所有距離 <= 5 英里(例如),它的執行速度也非常慢。

是否有一種有效的方法可以到達距離彼此質心 <= 5 英里的所有郵政編碼組合?

一次生成整個距離矩陣將非常消耗 RAM,循環遍歷每個唯一郵政編碼的組合 - 非常耗時。 讓我們找到一些妥協。

我建議將zipcode data.frame分成(例如)100 行(借助包bitchunk功能),然后計算 44336 和 100 點之間的距離,根據目標距離閾值進行過濾,然后繼續下一個數據塊。 在我的示例中,我將zipcode數據轉換為data.table以提高速度並節省 RAM。

library(zipcode)
library(data.table)
library(magrittr)
library(geosphere)

data(zipcode)

setDT(zipcode)
zipcode[, dum := NA] # we'll need it for full outer join

僅供參考 - 這是 RAM 中每條數據的大致大小。

merge(zipcode, zipcode[1:100], by = "dum", allow.cartesian = T) %>% 
  object.size() %>% print(unit = "Mb")
# 358.2 Mb

代碼本身。

lapply(bit::chunk(1, nrow(zipcode), 1e2), function(ridx) {
  merge(zipcode, zipcode[ridx[1]:ridx[2]], by = "dum", allow.cartesian = T)[
    , dist := distGeo(matrix(c(longitude.x, latitude.x), ncol = 2), 
                      matrix(c(longitude.y, latitude.y), ncol = 2))/1609.34 # meters to miles
    ][dist <= 5 # necessary distance treshold
      ][, dum := NULL]
  }) %>% rbindlist -> zip_nearby_dt

zip_nearby_dt # not the whole! for first 10 chunks only

       zip.x          city.x state.x latitude.x longitude.x zip.y     city.y state.y latitude.y longitude.y     dist
    1: 00210      Portsmouth      NH   43.00590   -71.01320 00210 Portsmouth      NH   43.00590   -71.01320 0.000000
    2: 00210      Portsmouth      NH   43.00590   -71.01320 00211 Portsmouth      NH   43.00590   -71.01320 0.000000
    3: 00210      Portsmouth      NH   43.00590   -71.01320 00212 Portsmouth      NH   43.00590   -71.01320 0.000000
    4: 00210      Portsmouth      NH   43.00590   -71.01320 00213 Portsmouth      NH   43.00590   -71.01320 0.000000
    5: 00210      Portsmouth      NH   43.00590   -71.01320 00214 Portsmouth      NH   43.00590   -71.01320 0.000000
---                                                                                                              
15252: 02906      Providence      RI   41.83635   -71.39427 02771    Seekonk      MA   41.84345   -71.32343 3.688747
15253: 02912      Providence      RI   41.82674   -71.39770 02771    Seekonk      MA   41.84345   -71.32343 4.003095
15254: 02914 East Providence      RI   41.81240   -71.36834 02771    Seekonk      MA   41.84345   -71.32343 3.156966
15255: 02916         Rumford      RI   41.84325   -71.35391 02769   Rehoboth      MA   41.83507   -71.26115 4.820599
15256: 02916         Rumford      RI   41.84325   -71.35391 02771    Seekonk      MA   41.84345   -71.32343 1.573050

在我的機器上,處理 10 個塊需要 1.7 分鍾,所以整個處理可能需要 70-80 分鍾,不快,但可能令人滿意。 我們可以根據可用的 RAM 容量將塊大小增加到 200 或 300 行,這將分別縮短 2 或 3 倍的處理時間。

此解決方案的缺點是生成的data.table包含“重復”行 - 我的意思是從 A 點到 B 點以及從 B 到 A 都有距離。這可能需要一些額外的過濾。

我想最有效的算法會首先將空間位置轉換為樹狀數據結構。 不過,您不需要明確地執行此操作,如果您有一個算法可以 1) 將緯度/經度划分為空間索引,2) 告訴您該索引的鄰居,那么您可以使用它來過濾您的方形數據。 (這會比構建一棵樹效率低,但可能更容易實現。)

geohash就是這樣一種算法。 它將連續的緯度/經度轉換為二維 bin。 有一個(相當新的)包在 R 中提供 geohash 以下是如何使用它解決此問題的一個想法:

首先,使用 geohash 做一些初步校准

  1. 將 lat/long 轉換為 bin 精度為p的散列(比如說)

  2. 評估哈希是否以與您感興趣的距離相似的精度進行校准(例如,相鄰質心之間的距離為 3-7 英里),如果不是,則返回1並調整精度p

這產生了郵政編碼-哈希值關系。

然后,計算每個(唯一的)哈希值的距離

  1. 確定它的 (8, bc 散列形成一個二維網格) 最近鄰,因此選擇 9 個散列值

  2. 計算 9 個哈希中所有distGeo之間的成對距離(使用,例如,在問題中使用distGeo

  3. 返回哈希值的所有 zip-zip 成對距離(例如,在矩陣中)

這會產生一個哈希值- zip-zip 距離對象關系

(在第2步中,最好只計算一次最近鄰對。但這可能不是必需的。)

最后,對於每個 zip

  1. 使用以上兩步(通過hash值作為key)得到zip-zip
    拉鏈的距離對象
  2. 將對象過濾到與焦點 zip 的距離(回想一下,它是與焦點 zip 相鄰的一組散列中的所有成對距離)
  3. 僅保持距離< 5 miles

這會產生一個拉鏈-在 5 英里的物體內拉鏈 (焦點 zip 5 英里內的 zip 可以存儲為一列列表(每個元素都是一個列表),位於一列焦點 zip 旁邊的數據框中,或者存儲為一個單獨的列表,以焦點 zip 作為名稱)。

以下是使用spatialrisk的解決方案。 這些函數是用 C++ 編寫的,因此速度非常快。 在我的機器上大約需要 25 秒。

library(zipcodeR)
library(spatialrisk)
library(dplyr)

# Zip code data
zipcode <- zipcodeR::zip_code_db

# Radius in meters
radius_meters <- 5000

# Find zipcodes within 5000 meters
sel <- tibble(zipcode) %>%
  select(zipcode, lat, lon = lng) %>%
  filter(!is.na(lat), !is.na(lon)) %>%
  mutate(zipcode_within_radius = purrr::map2(lon, lat, ~points_in_circle(zipcode_sel, .x, .y, radius = radius_meters)[-1,])) %>%
  unnest(cols = c(zipcode_within_radius), names_repair = "unique")

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM