【问题标题】:R String match for address using stringdist, stringdistmatrixR字符串匹配地址使用stringdist,stringdistmatrix
【发布时间】:2017-02-27 12:46:43
【问题描述】:

我有两个大型数据集,一个大约一百万条记录,另一个大约 70K。这些数据集有地址。如果较小数据集中的任何地址存在于较大数据集中,我想匹配。正如您想象的那样,地址可以用不同的方式和不同的情况编写,所以看到应该匹配的时候没有匹配,而不应该匹配的却有匹配,这很烦人。我做了一些研究,并找出了可以使用的包 stringdist。但是我被卡住了,我觉得我没有充分利用它的功能,对此提出一些建议会有所帮助。

下面是一个示例虚拟数据以及我为解释这种情况而创建的代码

Address1 <- c("786, GALI NO 5, XYZ","rambo, 45, strret 4, atlast, pqr","23/4, 23RD FLOOR, STREET 2, ABC-E, PQR","45-B, GALI NO5, XYZ","HECTIC, 99 STREET, PQR")
df1 <- data.table(Address1)

Address2 <- c("abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR")
df2 <- data.table(Address2)

df1[, key_match := gsub("[^[:alnum:]]", "", Address1)]
df2[, key_match := gsub("[^[:alnum:]]", "", Address2)]

fn_match = function(str, strVec, n){
  strVec[amatch(str, strVec, method = "dl", maxDist=n,useBytes = T)]
}

df1[!is.na(key_match)
       , address_match := 
      fn_match(key_match, df2$key_match,3)
       ]

如果您看到输出,它会在 df1 中的 address_match 下为我提供匹配项。如果我在我的主要数据上应用相同的代码,则代码从最近 30 小时开始仍在运行。虽然我已经转换为 data.table。不知道如何加快速度。

我在进一步阅读时遇到了 stringdist 矩阵。这似乎更有帮助,我可以根据空间拆分地址并检查每个地址列表中是否存在每个单词,并且根据最大匹配,可以创建匹配摘要。但是我不太擅长循环。如何循环遍历每个单词的较小文件中的每个地址并检查较大文件中的单个地址并创建匹配矩阵?任何帮助!

【问题讨论】:

  • 也许您可以先通过拆分数字和街道名称来规范化地址,然后使用 stringdist 库进行模糊匹配这是一个示例:r-bloggers.com/… 告诉我是否有帮助
  • @Slim,我看过这个链接,但作为 R 的初学者,发现在我的地址匹配情况下很难实现。一些解决方案会有所帮助!

标签: r loops data.table string-matching stringdist


【解决方案1】:

我有一个不需要data.table 的解决方案,但如果集合很大,可以使用package:parallel 运行

 rbind.pages(
  parallel::mclapply(Address1, function(i){
    data.frame(
       src = i, 
       match = Address2[which.min(adist(i, Address2))]
     )
   }, mc.cores = parallel::detectCores() - 2)) %>% 
 select(`src (Address1)`= 1, `match (Address2)` = 2)

然后给出输出解决方案:

                          src (Address1)                     match (Address2)
1                    786, GALI NO 5, XYZ                   786, GALI NO 4 XYZ
2       rambo, 45, strret 4, atlast, pqr del, 546, strret2, towards east, pqr
3 23/4, 23RD FLOOR, STREET 2, ABC-E, PQR                  23/4, STREET 2, PQR
4                    45-B, GALI NO5, XYZ                  45B, GALI NO 5, XYZ
5                 HECTIC, 99 STREET, PQR                  23/4, STREET 2, PQR

编辑:

我意识到如果不查看距离计算,这可能不是很有帮助,因此您可以根据需要进行调整;所以我将数据复制到更大的随机集合中,然后修改函数以显示字符串距离计算和处理时间

rand_addy_one <- rep(Address1, 1000)[sample(1:1000, 1000)]
rand_addy_two <- rep(Address2, 3000)[sample(1:3000, 3000)]


system.time({
  test_one <<- rbind.pages(parallel::mclapply(rand_addy_one, function(i) {
    calc <- as.data.frame(drop(attr(adist(i, rand_addy_two, counts = TRUE), "counts")))
    calc$totals <- (rowSums(calc))
    calc %>% mutate(src = i, target = rand_addy_two) %>% 
      filter(totals == min(totals))
  }, mc.cores = parallel::detectCores() - 2))  %>% 
    select(`source Address1` = src, `target Address2(matched)` = target,
           insertions = ins, deletions = del, substitutions = sub,
           total_approx_dist = totals)
})

   user  system elapsed 
 24.940   1.480   3.384 

> nrow(test_one)
[1] 600000

现在反转并将较大的集合应用于较小的集合:

system.time({
   test_two <<- rbind.pages(parallel::mclapply(rand_addy_two, function(i) {
    calc <- as.data.frame(drop(attr(adist(i, rand_addy_one, counts = TRUE), "counts")))
    calc$totals <- (rowSums(calc))
    calc %>% mutate(src = i, target = rand_addy_one) %>% 
        filter(totals == min(totals))
}, mc.cores = parallel::detectCores() - 2))  %>% 
    select(`source Address2` = src, `target Address1(matched)` = target,
           insertions = ins, deletions = del, substitutions = sub,
           total_approx_dist = totals)
})

   user  system elapsed 
 27.512   1.280   4.077 

nrow(test_two)
[1] 720000

【讨论】:

  • 感谢您的解决方案。我尝试为较小的数据集复制代码,并观察到对于单个地址,它会返回多个匹配项以及距离。这样,如果数据有 2000 条记录要匹配,它会返回大约 5000 条不同匹配的数据。我想知道我们怎样才能只取回距离最小的匹配,或者至少 2 距离匹配。
猜你喜欢
  • 2015-10-07
  • 1970-01-01
  • 2014-04-17
  • 2016-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多