从地址中提取唯一的美国邮政编码(5 位数字可选后跟连字符和 4 位数字)

Extract unique US zipcodes (5 digits optionally followed by hyphen and 4 digits) from addresses

我在找 U.S。地址和邮政编码 df 中的邮政编码。这些记录未规范化并且包含数据输入错误。普通邮政编码是 5 位数字。我还在邮政编码字段中寻找 4 位数字,因为前导 0 可能因无意中转换为数值而被删除。 5 位邮政编码后可以跟一个连字符和 4 位数字,我也想捕获这些数字。我希望邮政编码字段中只有一个邮政编码,但地址字段中可能有多个邮政编码,因为有些人以一种只用于一个地址的形式键入两个地址。只有在邮政编码中找到 none 时,我才想在地址中查找邮政编码,因为地址中的 5 位数街道号码会产生太多误报。我实现了U.S之外的地址。如果本地邮政编码与 U.S 匹配,将产生误报。格式。

我想要的结果是一个新的 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 offices 的示例(不包括所有可能的用例,例如,任何一个字段都可能为空):

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")

您可以使用 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

以下清理了 postal_code 列,但您没有在地址字段中提供带 zips 的数据帧切片,因此不知道该列中的 "real world" 数据是什么样的会是一个潜在的消磨时间 w/o 的功效。一旦您为您的问题提供更具代表性的数据,我可以从地址字段中添加 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