【问题标题】:approximate string matching within single list - r单个列表中的近似字符串匹配 - r
【发布时间】:2015-02-12 15:23:14
【问题描述】:

我在一个数据框中有一个列表,其中包含一个长列表中的数千个名称。许多名称之间存在细微差异,这使它们略有不同。我想找到一种方法来匹配这些名称。例如:

names <- c('jon smith','jon, smith','Jon Smith','jon smith et al','bob seger','bob, seger','bobby seger','bob seger jr.')

我查看了stringdist 函数中的amatch 以及agrep,但这些都需要master list of names 用于匹配另一个名称列表。就我而言,我没有这样的主列表,所以我想通过识别具有高度相似模式的名称来从数据中创建一个,这样我就可以查看它们并确定它们是否是同一个人(在许多他们是)。我想要一个新列中的输出,它可以帮助我知道这些是可能的匹配项,并且可能是基于 Levenshtein 距离或其他东西的某种相似性得分。也许是这样的:

            names   match      SimilarityScore
1       jon smith     a               9
2      jon, smith     a               8
3       Jon Smith     a               9
4 jon smith et al     a               5
5       bob seger     b               9
6      bob, seger     b               8
7     bobby seger     b               7
8   bob seger jr.     b               5

这样的事情可能吗?

【问题讨论】:

标签: r string-matching agrep


【解决方案1】:

根据here 找到的帖子,我发现分层文本聚类可以满足我的要求。

  names <- c('jon smith','jon, smith','Jon Smith','jon smith et al','bob seger','bob, seger','bobby seger','bob seger jr.','jake','jakey','jack','jakeyfied')

# Levenshtein Distance
e  <- adist(names)
rownames(e) <- names
hc <- hclust(as.dist(e))
plot(hc)
rect.hclust(hc,k=3) #the k value provides the number of clusters
df <- data.frame(names,cutree(hc,k=3))

如果您选择正确数量的集群(在本例中为三个),输出看起来非常好:

                       names             cutree.hc..k...3.
jon smith             jon smith                 1
jon, smith           jon, smith                 1
Jon Smith             Jon Smith                 1
jon smith et al jon smith et al                 1
bob seger             bob seger                 2
bob, seger           bob, seger                 2
bobby seger         bobby seger                 2
bob seger jr.     bob seger jr.                 2
jake                       jake                 3
jakey                     jakey                 3
jack                       jack                 3
jakeyfied             jakeyfied                 3

但是,名称通常比这更复杂,并且在添加了一些更难的名称后,我发现默认的 adist 选项并没有提供最好的聚类:

names <- c('jon smith','jon, smith','Jon Smith','jon smith et al','bob seger','bob, seger','bobby seger','bob seger jr.','jake','jakey','jack','jakeyfied','1234 ranch','5678 ranch','9983','7777')

d  <- adist(names)
rownames(d) <- names
hc <- hclust(as.dist(d))
plot(hc)
rect.hclust(hc,k=6)

我可以通过将替换值的成本增加到 2 并将插入和删除成本保持在 1 并忽略大小写来改进这一点。这有助于最大限度地减少完全不同的四个字符数字字符串的错误分组,我不想分组:

d  <- adist(names,ignore.case=TRUE, costs=c(i=1,d=1,s=2)) #i=insertion, d=deletion s=substitution
rownames(d) <- names
hc <- hclust(as.dist(d))
plot(hc)
rect.hclust(hc,k=6

我通过使用grep 包中的gsub 工具删除常用术语(例如“牧场”和“等”)进一步微调了集群,并将集群数量增加了一倍:

names<-gsub("ranch","",names)
names<-gsub("et al","",names)
d  <- adist(names,ignore.case=TRUE, costs=c(i=1,d=1,s=2))
rownames(d) <- names
hc <- hclust(as.dist(d))
plot(hc)
rect.hclust(hc,k=7)

虽然有一些方法可以让数据整理出最好的簇数,而不是手动尝试挑选数字,但我发现使用反复试验最容易,尽管有信息here about that approach

【讨论】:

    【解决方案2】:

    Roman 在 cmets 中关于自然语言处理的建议可能是最好的起点。但是对于粗略的方法,您可以根据 ascii 代码查看距离:

    mynames = c("abcd efghijkl mn","zbcd efghijkl mn","bbcd efghijkl mn","erqe")
    asc <- function(x) { strtoi(charToRaw(x),16L) }
    namesToChar= sapply(mynames, asc)
    maxLength= max(unlist(lapply(namesToChar,length)))
    namesToChar =lapply(namesToChar, function(x) { c(x, rep(-1, times = maxLength-length(x) )) } )
    namesToChar = do.call("rbind",namesToChar)
    dist(namesToChar,method="euclidean")
    dist(namesToChar,method="canberra")
    

    虽然它似乎为样本提供了足够的数字,

    > dist(namesToChar,method="manhattan")
                     abcd efghijkl mn zbcd efghijkl mn bbcd efghijkl mn
    zbcd efghijkl mn               25                                  
    bbcd efghijkl mn                1               24                 
    erqe                          257              274              256
    

    这种方法的缺点是,对于您想要做的事情,dist 函数似乎没有足够的距离方法。一个元素方面的二进制比较,然后可能是一个更标准的距离(“曼哈顿”似乎最接近你的需要)?当然,您总是可以自己实现。此外,-1 填写在这里是一个 hack,如果你决定走这条路,你需要用你的样本的平均 ascii 代码替换它。

    对于与总体人口的相似度得分,您可以取其他词的平均距离的倒数。

    【讨论】:

      猜你喜欢
      • 2013-04-15
      • 2011-05-11
      • 2013-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 2010-09-08
      • 2016-03-28
      相关资源
      最近更新 更多