用于地址匹配的分层模糊匹配策略

时间:2017-10-27 13:40:07

标签: r pattern-matching fuzzy-search geocode street-address

我正在R中构建一个地址匹配模块,在那里我想找到一个inAddress列表与使用R的所有地址dbAddress的数据库的匹配。

假设地址包含要匹配的street number, street name, postal code, city。我想考虑一些匹配规则,例如:

  • 邮政编码应完全匹配

  • 街道号码应该是完全匹配,除非找不到,那么 考虑模糊匹配

  • 街道名称一般应该是模糊匹配,可能是优先级 如果没有找到第一个单词的精确匹配强调(尝试搜索 类似的结果,如华盛顿大街,华盛顿街, 华盛顿路等)

您对策略有何建议以及如何有效地构建策略? 到目前为止,我的几个想法是:

  • 在数据表上放置两个地址列表。也许指数可以帮助 表现?
  • 首先搜索带有硬if的邮政编码并将其限制为 邮政比赛第一
  • 然后将结果级联为街道名称的模糊匹配。也许 首先规范化名称以仅返回关键字(词干和删除 大道,街道,等等)。但我担心这会让我失败 信息。 W Avenue与W Street不同。
  • 级联结果再次显示街道号模糊匹配。

我担心这将是一个很大的表现伤害。另外,有没有办法同时加速多个地址匹配?也许首先加入邮政编码以避免每次都进行全面搜索?并行性?

欢迎任何建议。谢谢

2 个答案:

答案 0 :(得分:1)

levensthein是简单拼写错误的必备条件。找到合适的容差很重要,因为小于0.8会导致过多的误报。

我建议你使用一个你可以纠正的短词词典,例如road / raod或street / stret。

您可能需要检查Ave和Avenue等缩写,它们以相同的字符开头,但Road vs Rd缺少某些字符,因此匹配规则不同。再一次,字典可以帮助。

本文包含12个使用模糊匹配查找地址的测试,这些测试可用于改进算法。谷歌中的许多例子甚至无法匹敌!

示例包括:

  1. 拼写错误
  2. 缺少空间
  3. 不正确的类型(街道与道路)

  4. 毗邻/附近郊区

  5. 缩写
  6. 同义词:Floor vs Level
  7. 单位,公寓或公寓与信件
  8. 号码与信件
  9. 额外词汇(例如前门,部门名称)
  10. Swapped Letters
  11. 听起来像
  12. 令牌化(不同的输入顺序)
  13. 在查看了几个商业地址自动完成小部件之后,这个(https://www.addy.co.nz/address-finder-fuzzy-matching)是新西兰地址中最聪明的小部件。也许你可以获得灵感并提出更好的算法!

答案 1 :(得分:0)

我完全支持这篇文章之前的内容。根据您的数据质量,在运行地址解析器之前,需要执行许多数据清理工作。这是我的并行执行Levensthein模糊匹配算法的代码。有一个名为“ df_ge”的地址列表,该列表与包含地址和地理编码的数据“ X”匹配。分层搜索从邮政编码区域(1)到街道(2)到街道编号(3)。在3 GHz的4个CPU上,对1万个地址进行地址解析大约需要1.71分钟。请注意,该代码远非完美无缺,可以进行优化。

library(foreach)
library(doParallel)
#
#
#
start <- Sys.time()
#setup parallel backend to use 4 processors
cl<-makeCluster(4)
registerDoParallel(cl)
#
#create the dataframe to store the IDs and their respective geocodes
#
k <- data.frame(matrix(ncol = 4))
colnames(k) <- c("ID_US", "ID_Pet", "GKODE", "GKODN")
#
k <- foreach(i = 1:10000, .combine="rbind") %dopar% {
#
#(1) match the data based on ZIP code
#
  temp1 <- X[which(X$PLZ4==df_ge$PostalCodeNumber[i]),] # PLZ stands for ZIP code
  #
  #(2) match the data based on addresses; perfect match with j=0 and then increase the tolerance as long as temp2 is empty
  #
  temp2 <- data.frame(); j <- 0;
  while (nrow(temp2)==0){
    temp2 <- temp1[agrep(df_ge$Street[i], temp1$STRNAME, max.distance = j, ignore.case = T),]
    j <- j+0.01
  }
  #
  #(3) match the data based on street number 
  #
  temp3 <- temp2[which(tolower(temp2$DEINR)==tolower(df_ge$StreetNumber[i])),] # DEINR stands for street number
  #
  #make sure that only one result is returned
  #
  if(nrow(temp3)==1){
    k[i,] <- unlist(c(df_ge[i,c("ID_US", "ID_Pet")],temp3[,c("GKODE", "GKODN")]))
  }

  k[i,]
}
stopCluster(cl)