【问题标题】:R Find the Distance between Two US Zipcode columnsR查找两个美国邮政编码列之间的距离
【发布时间】:2019-08-19 20:51:30
【问题描述】:

我想知道计算两个美国邮政编码列之间距离(​​以英里为单位)的最有效方法是使用 R。

我听说过 geosphere 包,用于计算邮政编码之间的差异,但不完全理解,想知道是否还有其他方法。

例如,假设我有一个看起来像这样的数据框。

 ZIP_START     ZIP_END
 95051         98053
 94534         94128
 60193         60666
 94591         73344
 94128         94128
 94015         73344
 94553         94128
 10994         7105
 95008         94128

我想创建一个如下所示的新数据框。

 ZIP_START     ZIP_END     MILES_DIFFERENCE
 95051         98053       x
 94534         94128       x
 60193         60666       x
 94591         73344       x
 94128         94128       x
 94015         73344       x
 94553         94128       x
 10994         7105        x
 95008         94128       x

其中 x 是两个邮政编码之间的英里数差。

计算这个距离的最佳方法是什么?

这是创建示例数据框的 R 代码。

df <- data.frame("ZIP_START" = c(95051, 94534, 60193, 94591, 94128, 94015, 94553, 10994, 95008), "ZIP_END" = c(98053, 94128, 60666, 73344, 94128, 73344, 94128, 7105, 94128))

如果您有任何问题,请告诉我。

感谢任何建议。

感谢您的帮助。

【问题讨论】:

  • 给定"I have heard of the geosphere package for computing the difference between zipcodes",你见过哪些例子可以做到这一点,你尝试了什么,什么不起作用?关于 SO 的问题似乎只是要求某人做你的工作并没有引起很多关注(并且被否决)。 SO 用于在您编写的程序上寻求编程帮助。
  • 有几种网络服务可以做到这一点,但它们的 API 通常仅限于免费使用和/或需要注册。但是鉴于有一个zipcode 包(每个邮政编码都有纬度和经度),您应该尝试理解geosphere 中的distHaversine 方法。这不是很复杂 - here's a code example.

标签: r tidyverse distance zipcode geosphere


【解决方案1】:

有一个方便的 R 包,名为“zipcode”,它提供了一个邮政编码、城市、州和纬度和经度的表格。所以一旦你有了这些信息,“geosphere”包就可以计算点之间的距离。

library(zipcode)
library(geosphere)

#dataframe need to be character arrays or the else the leading zeros will be dropped causing errors
df <- data.frame("ZIP_START" = c(95051, 94534, 60193, 94591, 94128, 94015, 94553, 10994, 95008), 
       "ZIP_END" = c(98053, 94128, 60666, 73344, 94128, 73344, 94128, "07105", 94128), 
       stringsAsFactors = FALSE)

data("zipcode")

df$distance_meters<-apply(df, 1, function(x){
  startindex<-which(x[["ZIP_START"]]==zipcode$zip)
  endindex<-which(x[["ZIP_END"]]==zipcode$zip)
  distGeo(p1=c(zipcode[startindex, "longitude"], zipcode[startindex, "latitude"]), p2=c(zipcode[endindex, "longitude"], zipcode[endindex, "latitude"]))
})

关于输入数据框的列类的警告。邮政编码应该是字符而不是数字,否则会丢弃前导零导致错误。

从 distGeo 的返回距离以米为单位,我将允许读者确定正确的单位转换为英里。

更新
邮政编码包似乎已归档。有一个替换包:“zipcodeR”,它提供经度和纬度数据以及附加信息。

【讨论】:

  • 谢谢,这正是我想要的!哇,这个功能超级好用,我没想到会用邮政编码数据这样做。
【解决方案2】:

正如 Dave2e 提到的,原来的 zipcode 包已经从 CRAN 中删除,所以我们需要使用 zipcodeR。

if (!require("zipcodeR"))install.packages("zipcodeR")
if (!require("geosphere"))install.packages("geosphere")

df <- data.frame(
  "ZIP_START" = c(95051, 94534, 60193, 94591, 94128, 94015, 94553, 10994, 95008),
  "ZIP_END" = c(98053, 94128, 60666, 73344, 94128, 73344, 94128, "07105", 94128),
  stringsAsFactors = FALSE
)

data("zip_code_db")

df$distance_meters<-apply(df, 1, function(x){
  startindex<-which(x[["ZIP_START"]]==zip_code_db$zipcode)
  endindex<-which(x[["ZIP_END"]]==zip_code_db$zipcode)
  distGeo(p1=c(zip_code_db[startindex, "lng"], 
               zip_code_db[startindex, "lat"]), 
          p2=c(zip_code_db[endindex, "lng"], 
               zip_code_db[endindex, "lat"]))
})

这是基于新 zipcodeR 包的修复。功劳归于 Dave2e。

【讨论】:

    【解决方案3】:

    OP 要求“最有效”,所以给定

    • geospherequite slow 当你想在大量数据上使用它时
    • apply 本质上是一个循环函数,通常可以使用矢量化代码击败

    我提出使用data.tablelibrary(geodist) 的完全矢量化解决方案

    
    #dataframe need to be character arrays or the else the leading zeros will be dropped causing errors
    df <- data.frame("ZIP_START" = c(95051, 94534, 60193, 94591, 94128, 94015, 94553, 10994, 95008), 
                     "ZIP_END" = c(98053, 94128, 60666, 73344, 94128, 73344, 94128, "07105", 94128), 
                     stringsAsFactors = FALSE)
    
    
    library(zipcodeR)
    library(data.table)
    library(geodist)
    
    ## Convert the zip codes to data.table so we can join on them
    ## I'm using the centroid of the zipcodes (lng and lat).
    ## If you want the distance to the endge of the zipcode boundary you'll
    ## need to convert this into a spatial data set
    dt_zips <- as.data.table( zip_code_db[, c("zipcode", "lng", "lat")])
    
    ## convert the input data.frame into a data.talbe
    setDT( df )
    
    ## the postcodes need to be characters
    df[
      , `:=`(
        ZIP_START = as.character( ZIP_START )
        , ZIP_END = as.character( ZIP_END )
      )
    ]
    
    ## Attach origin lon & lat using a join
    df[
      dt_zips
      , on = .(ZIP_START = zipcode)
      , `:=`(
        lng_start = lng
        , lat_start = lat
      )
    ]
    
    ## Attach destination lon & lat using a join
    df[
      dt_zips
      , on = .(ZIP_END = zipcode)
      , `:=`(
        lng_end = lng
        , lat_end = lat
      )
    ]
    
    ## calculate the distance
    df[
      , distance_metres := geodist::geodist_vec(
        x1 = lng_start
        , y1 = lat_start
        , x2 = lng_end
        , y2 = lat_end
        , paired = TRUE
        , measure = "haversine"
      )
    ]
    
    ## et voila - note the missing zipcode 6066 and 73344
    df
    
    #    ZIP_START ZIP_END lng_start lat_start lng_end lat_end distance_metres
    # 1:     95051   98053   -121.98     37.35 -122.02   47.66      1147708.60
    # 2:     94534   94128   -122.10     38.20 -122.38   37.62        69090.01
    # 3:     60193   60666    -88.09     42.01      NA      NA              NA
    # 4:     94591   73344   -122.20     38.12      NA      NA              NA
    # 5:     94128   94128   -122.38     37.62 -122.38   37.62            0.00
    # 6:     94015   73344   -122.48     37.68      NA      NA              NA
    # 7:     94553   94128   -122.10     38.00 -122.38   37.62        48947.02
    # 8:     10994   07105    -73.97     41.10  -74.15   40.72        44930.17
    # 9:     95008   94128   -121.94     37.28 -122.38   37.62        54263.61
    

    还要注意返回的距离以米为单位。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-09
      相关资源
      最近更新 更多