我在长列表中有数千个名称的数据框中有一个列表。许多名称在它们之间存在细微差别,这使它们略有不同。我想找到一种匹配这些名称的方法。例如:
names <- c('jon smith','jon, smith','Jon Smith','jon smith et al','bob seger','bob, seger','bobby seger','bob seger jr.')
我查看了amatch
函数中的stringdist
以及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
这样的事情可能吗?
答案 0 :(得分:4)
利用找到的帖子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
我通过删除诸如&#34; ranch&#34;等常用术语来进一步微调聚类。和&#34;等人&#34;使用gsub
包中的grep
工具并将群集数量增加一个:
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,但最容易使用反复试验。
答案 1 :(得分:2)
Roman在自然语言处理评论中的建议可能是最好的起点。但是对于背面信封类型的方法,你可以用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
填写是一个黑客攻击,如果你决定走这条路,你需要用你的样本的平均ascii代码替换它。
对于相对于整体人口的相似性得分,您可以将平均距离与每个其他单词的倒数相反。