【问题标题】:Imperfect string match using data.table使用 data.table 进行不完美的字符串匹配
【发布时间】:2014-03-12 21:12:43
【问题描述】:

好的,所以我不久前发布了一个关于编写 R 函数以加速大型文本文件的字符串匹配的问题。我睁大眼睛看到“data.table”,我的问题得到了完美的回答。

这是该线程的链接,其中包含所有数据和详细信息:

Accelerate performance and speed of string match in R

但现在我遇到了另一个问题。有时,由于在 DMV 填写汽车信息时的人为错误,提交的 VIN#s(在“vinDB”文件中)与“carFile”文件中的一个或两个字符不同。有没有办法编辑

dt[J(car.vins), list(NumTimesFound=.N), by=vin.names]

该代码行(由上述链接中的@BrodieG 提供)以允许识别相差一两个字符的 VIN#?

如果这是一个简单的更正,我深表歉意。我对 R 中的“data.table”包的强大功能感到不知所措,并且很想尽可能多地了解它的实用性,而且这个论坛的知识渊博的成员对我来说绝对是关键。

**编辑:

所以我一直在按照建议使用“lapply”和“agrep”函数,我一定是做错了什么:

我尝试替换这一行:

dt[J(car.vins), list(NumTimesFound=.N), by=vin.names]

用这个:

dt <- dt[lapply(vin.vins, function(x) agrep(x,car.vins, max.distance=2)), list(NumTimesFound=.N), vin.names, allow.cartesian=TRUE]

但出现以下错误:

Error in `[.data.table`(dt, lapply(vin.vins, function(x) agrep(x,car.vins,  : 
x.'vin.vins' is a character column being joined to i.'V1' which is type 'integer'. 
Character columns must join to factor or character columns.

但它们都是“chr”类型。有谁知道我为什么会收到这个错误?我是否以正确的方式考虑这一点,即:我在这里正确使用 lapply 吗?

谢谢!

【问题讨论】:

  • 这并不容易。我建议您匹配将匹配的内容,然后对于剩下的任何内容,针对不匹配的汽车的 VIN 计算不匹配的项目的 Levenshtein Distance(未链接包不适用于 R3.0.2,仅供参考)匹配。如果您确定错误出现在 VIN 字符串的特定区域,那么这会变得更容易,但从您的描述来看,听起来并非如此。
  • 谢谢@BrodieG。有没有办法将“agrep”函数合并到代码中?
  • 是的,但是昂贵的操作不会在data.table 中。您需要 lapply agrep 到您尝试匹配的每个 VIN。它可以完成,但它不是微不足道的,它可能会很慢,具体取决于您有多少坏 VINS 以及有多少候选 VINS。当然不是小修改,至少不是我能想到的。
  • 我明白了。非常感谢您,您对此的见解非常有帮助。是否有您推荐的链接可以用作“data.table”操作的教程(除了它的包描述页面)?
  • 您可以查看 [data.table 常见问题解答](datatable.r-forge.r-project.org/datatable-faq.pdf) 以及查看评价较高的问题/答案data.table SO 上的标签。

标签: string r performance text data.table


【解决方案1】:

我终于明白了。

agrep-函数有一个value-选项,需要将FALSE(默认)更改为TRUE

dt <- dt[lapply(car.vins, agrep, x = vin.vins, max.distance = c(cost=2, all=2), value = TRUE)
         , .(NumTimesFound = .N)
         , by = vin.names]

注意:max.distance 参数可以根据 Levenshtein 距离、替换、删除等进行更改。“agrep”是一个迷人的功能!

再次感谢大家的帮助!

【讨论】:

    【解决方案2】:

    感谢您对部分匹配问题的回答。这是我在自己的机器上工作的完整代码(包括 BrodieG 在您的链接帖子中提供的可重现示例)。我不得不将lapply 更改为sapply

    library(data.table)
    set.seed(1)
    makes <- c("Toyota", "Ford", "GM", "Chrysler")
    years <- 1995:2014
    cars <- paste(sample(makes, 500, rep=T), sample(years, 500, rep=T))
    vins <- unlist(replicate(500, paste0(sample(LETTERS, 16), collapse="")))
    vinDB <- data.frame(c(cars, vins)[order(rep(1:500, 2))])               
    carFile <- data.frame(c(rep("junk", 1000), sample(vins, 1000, rep=T), rep("junk", 2000))[order(rep(1:1000, 4))])
    
    vin.names <- vinDB[seq(1, nrow(vinDB), 2), ]
    vin.vins <- vinDB[seq(2, nrow(vinDB), 2), ]
    car.vins <- carFile[seq(2, nrow(carFile), 4), ]`
    
    #Add some errors to car.vins strings
    s <- sample(length(car.vins),100)
    car.vins.err <- as.character(car.vins)
    car.vins.err[s] <- gsub("A","B",car.vins.err[s])
    s <- sample(length(car.vins.err),100)
    car.vins.err[s] <- gsub("E","F",car.vins.err[s])
    s <- sample(length(car.vins.err),100)
    car.vins.err[s] <- gsub("I","J",car.vins.err[s])
    car.vins.err <- as.factor(car.vins.err)`
    
    dt <- data.table(vin.names, vin.vins, key="vin.vins")
    dt1 <- dt[J(car.vins), list(NumTimesFound=.N), keyby=vin.names]
    dt1.err <- dt[J(car.vins.err), list(NumTimesFound=.N), keyby=vin.names]
    dt2 <- dt[sapply(car.vins, agrep, x=vin.vins, max.distance=c(cost=2, all=2), value=TRUE), list(NumTimesFound=.N), keyby=vin.names]
    dt2.err <- dt[sapply(car.vins.err, agrep, x=vin.vins, max.distance=c(cost=2, all=2), value=TRUE), list(NumTimesFound=.N), keyby="vin.names"]
    
    dt1[dt1.err][dt2.err]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 2023-03-23
      • 2020-10-16
      • 2018-10-13
      • 2013-09-08
      相关资源
      最近更新 更多