【问题标题】:Extract unique US zipcodes (5 digits optionally followed by hyphen and 4 digits) from addresses从地址中提取唯一的美国邮政编码(5 位可选,后跟连字符和 4 位)
【发布时间】:2016-12-30 09:54:04
【问题描述】:

我在地址和邮政编码的 df 中查找美国邮政编码。这些记录是非规范化的并且包含数据输入错误。普通邮政编码是 5 位数字。我还在邮政编码字段中寻找 4 位数字,因为前导 0 可能已因无意转换为数值而被丢弃。 5 位邮政编码后面可以跟一个连字符和 4 位数字,我也想捕获它们。我希望邮政编码字段中只有一个邮政编码,但地址字段中可能有多个邮政编码,因为有些人以一个仅用于一个的形式键入两个地址。我只想在邮政编码中找不到邮政编码时才在地址中查找邮政编码,因为地址中的 5 位街道号码会产生太多误报。我意识到如果当地邮政编码与美国格式匹配,美国以外的地址会产生误报。

我想要的结果是一个新的 zip_code 列,其中包含由“/”(如果有多个)分隔的唯一找到的邮政编码字符串,前导 0 添加到 4 位邮政编码,如果没有找到,则为空字符串。

这是我的尝试,完全行不通:

pc <- offices$postal_code[offices$postal_code != ""]
offices$zip_code[offices$postal_code != ""] <- unlist(lapply(pc, function(x) {
  pczc <- sub("(?<!\\d)(\\d{5}\\-\\d{4}|\\d{4,5})(?!\\d)", "\\2", x, perl = T)
  pczc <- ifelse(nchar(pczc) == 4, paste0("0", pczc), pczc)
  return(pczc)
  }))

ad <- offices$address[offices$zip_code == ""]
adzc <- regmatches(ad, gregexpr("(?<!\\d)(\\d{5}\\-\\d{4}|\\d{5})(?!\\d)", ad, perl = T))
offices$zip_code[offices$zip_code != ""] <- paste(unique(adzc), collapse = "/")

我的正则表达式不起作用。此外,可能有一种更快的方法(我有很多地址)。我曾考虑先合并这两个字段,但这种方法的问题是地址字段中的 4 位街道号码会与缺少前导 0 的邮政编码混淆,后者只能出现在邮政编码字段中。 (也有 5 位数的门牌号,但我想这也无济于事。)

这是 df 办公室的示例(不包括所有可能的用例,例如,任一字段都可能为空):

structure(list(address = c("Headquarters 2355 E. Camelback Road Suite 300 Phoenix", 
"Headquarters 1401 Constitution Ave NW  Washington", "Headquarters 80 State Street 7th Floor  Albany", 
"Headquarters Spray Gaarde 46  Nieuwegein", "HQ 1055 Washington Blvd., 7th Floor  Stamford", 
"Headquarters Village Khubavali, PO Paud Taluka Mulshi  Pune", 
"Headquarters 231 Lagrange Street  Boston", "Headquarters 401 Chestnut St Suite 410 Chattanooga", 
"Israel Office st. ha Rav Bar Shaul 6  Rehovot", "Headquarters 7721 New Market Street  Olympia", 
"HQ Bernrieder Str. 15  Niederwinkling", "Headquarters 2810 Sydney Road  Plant City", 
"Headquarters 1350 Avenue of the Americas 9th Floor New York", 
"Headquarters Askanischer Platz 3  Berlin", "Australian Head Office Level 2, 145 Flinders Lane  Melbourne", 
"HQ 13303 Washington Avenue  Racine", "HQ 9150 E. Del Camino Dr., Ste 112  Scottsdale", 
"Arcadia Corporate Merchandise Ltd - Promotional Giveaways Grove Place, Wellington Road  High Wycombe", 
"Israel Office Shorashim, D.N.Misgav  ", "HQ 6009 Penn Avenue S.  Minneapolis"
), postal_code = c("85016", "20230", "12207", "3436", "6901", 
"412 108", "2132", "37402", "7625149", "98501", "94559", "33566-1173", 
"10019", "10963", "3000", "53406", "85258", "HP12 3PR", "20164", 
"55419")), .Names = c("address", "postal_code"), row.names = c(1L, 
2L, 4L, 5L, 6L, 8L, 10L, 11L, 12L, 14L, 15L, 18L, 19L, 21L, 22L, 
23L, 24L, 25L, 27L, 28L), class = "data.frame")

【问题讨论】:

  • 您能根据示例显示预期的输出吗?
  • 基于此数据框的预期结果是什么?因此,您的解释可以简化(提供预期的结果也会有很大帮助!)。

标签: r regex gis


【解决方案1】:

以下内容清理了 postal_code 列,但您没有在地址字段中提供带有 zip 的数据帧切片,因此在不知道该列中的“真实世界”数据是什么样子的情况下,这将是潜在的没有功效的时间沉没。一旦您为您的问题提供了更具代表性的数据,我就可以从地址字段中添加 zip 提取。

library(stringi)
library(purrr)

df$zip_1 <- stri_trim_both(df$postal_code) %>%
  stri_match_last_regex("((?:[[:digit:]]{5}-[[:digit:]]{4})|(?:[[:digit:]]{4,5}))") %>%
  ifelse(nchar(.)==4, 0 %s+% ., .) %>%
  .[,2]

df[,2:3]
##    postal_code      zip_1
## 1        85016      85016
## 2        20230      20230
## 4        12207      12207
## 5         3436      03436
## 6         6901      06901
## 8      412 108       <NA>
## 10        2132      02132
## 11       37402      37402
## 12     7625149      76251
## 14       98501      98501
## 15       94559      94559
## 18  33566-1173 33566-1173
## 19       10019      10019
## 21       10963      10963
## 22        3000      03000
## 23       53406      53406
## 24       85258      85258
## 25    HP12 3PR       <NA>
## 27       20164      20164
## 28       55419      55419

【讨论】:

  • 谢谢!我现在在地址中找不到邮政编码的示例,因为我有 358,000 条记录,并且有很多 5 位数的街道号码,但我也不能打折。
  • 从技术上讲,这是我提出的问题的解决方案,但@dimitris_ps 的answer 是我更大问题的解决方案。
【解决方案2】:

您可以使用googlemapsapi,而不是执行一些复杂的regex。我确信有一个 R 包,但下面的代码应该会有所帮助。

library(tidyr)
library(magrittr)
library(dplyr)
library(rvest)
library(jsonlite)
library(data.table)


getInfo <- function(data, address){

    mURL <- "http://maps.googleapis.com/maps/api/geocode/json?address=" %>% 
      paste0(unlist(data[address])) %>% gsub("Headquarters|HQ", "", .) %>% sapply(URLencode)

    temp <- lapply(mURL, function(y) {
                info <- read_html(y) %>% html_text %>% fromJSON(simplifyDataFrame = TRUE)
                if(length(info$results)){
                    info <- info[[1]]$address_components[[1]] %>% as.data.frame %>% select(-short_name)
                    info$types <- sapply(info$types, function(x) x[1])
                    info %<>% group_by(types) %>% summarize(long_name=toString(long_name)) %>% 
                             select(long_name, types) %>% ungroup
                    info %<>% spread(types, long_name)
                } else {
                  info <- data.frame(administrative_area_level_1=NA, administrative_area_level_2=NA, 
                                     country=NA, locality=NA, neighborhood=NA, postal_code=NA, 
                                     route=NA, street_number=NA, subpremise=NA)
                }
                info
      }) %>% rbindlist(fill=TRUE)

cbind(data, temp)

}

df2 <- getInfo(df, "address")

在你的data.frame 的一小部分上测试它,我假设它被命名为df。 你可以阅读更多关于googlemapsapi

df2 %>% View

【讨论】:

  • 1) 不幸的是,我从google maps api 看到您每天最多可以有 2,500 个请求。 0.50 美元/1,000 次额外请求,每天最多 100,000 次(如果启用了计费)。 2) 它将返回 NA
  • github.com/Datactuariat/Rpostal 需要一些麻烦才能开始工作,但它会很好地规范您的所有地址,而无需外部限速 API 的支持。就像parse() 的 R 财富一样,如果答案是“Google 地理编码”,那么您可能问错了问题。
  • @dimitri_ps 对于地址“Euro House Euro Business Park Little Island Cork Cork”,函数返回“错误:行 (4, 5) 的标识符重复”。错误来自指令info %&lt;&gt;% spread(types, long_name)}。这是因为前面的指令info$types &lt;- sapply(info$types, function(x) x[1]) 返回重复的locality。非常感谢您的意见。
  • @syre 我已经更新了我的答案来解决这个问题。我已经添加了代码info %&lt;&gt;% group_by(types) %&gt;% summarize(long_name=toString(long_name)) %&gt;% select(long_name, types) %&gt;% ungroup
  • @syre,我已经更新了我的答案。我有info[[1]]$address_components[[1]] 而不是info[[1]]$address_components,即如果返回多个地址,则只保留第一个地址。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 1970-01-01
  • 2013-02-08
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多