我有一个非常大的数据库,其名称如下:
names <- c("William Gates", "Bill Gates", "Gates, William H. III",
"Gates, William III", "William H Gates", "William H. Gates",
"Carlos Slim Helu & family", "Carlos Slim Helu",
"Carlos Slim & Family", "Carlos Slim")
我想像这样自动“清理”:
new_names <- c("William Gates", "William Gates", "William Gates",
"William Gates", "William Gates", "William Gates",
"Carlos Slim Helu & family", "Carlos Slim Helu & family",
"Carlos Slim Helu & family", "Carlos Slim Helu & family")
我有(任意)使用第一次出现的名称来替换它的其他变体。
在此示例中,names
是长度为10的字符向量。我想创建一个“部分匹配值”的10 X 10
矩阵。该矩阵将存储部分匹配范围的0和1之间的“度量”。例如,将names[1]
与names[1]
进行比较会产生完美匹配,因此值为1;将names[1]
与names[2]
进行比较会产生类似于5/12 = 0.41667的内容,这反映了盖茨对两个字符串都很常见而且(忽略空字符串)names[1]
有12个字母;按照相同的逻辑,将names[2]
与names[1]
进行比较会产生类似5/9 = 0.55556的内容。
我可能会忽略案例(家庭和家庭将是一个完美的匹配),只关注匹配子串(但如果有人评论如何匹配,比如说Slim和Silm,那也很棒。
作为第二步,我将创建一个最大值的三角矩阵(在示例中,值5/9 = 0.55556)。然后我会使用这个矩阵来调整情况,并选择一个像0.95这样的阈值,超过这个阈值就会更换字符串,逐渐降低阈值,直到我对数据已被清理感到满意为止。
我希望之前已经完成过这种事情,并且有人能够帮助我开始。我已经阅读过关于Paul Murrell的compare
软件包,并期望它将是一个很好的工具,但是我没有看到太多可以很容易适应的例子,所以如果你知道一个教程或者除了包装小插图,请指出他们。
我确实意识到会有更多代码可以提出一个好问题,而且我为无法提供太多代码而道歉。虽然我对R很熟悉,但我没有使用字符串匹配。如果有人指我到某个地方开始,我可以尝试用一些示例代码重新解释我的问题。
答案 0 :(得分:3)
基于adist
和群集的完整答案。
使用参数partial=TRUE
和ignore.case=TRUE
,函数
来自基地adist
的{{1}}似乎可以解决问题。好久不见
haul,Chris S指出的图书馆R
似乎
有希望,但也可以使用这种方法。
此解决方案通过stringdist
使用群集,使用&#39;单一链接&#39;
需要朋友的朋友的方法。适合的方法
这个问题。
请注意,这需要根据群集高度选择阈值
(在这种情况下累积的广义Levenshtein之间的距离
通过单链接标准查看的名称)。如果聚类也不是
对于您的问题而言,比可视化或检查输出更为昂贵
hclust
也不应该太糟糕。
hclust
答案 1 :(得分:2)
这是一个简单的尝试。只是使用内置函数而不创建任何矩阵,但它似乎适用于这个简单的例子。
names <- c("William Gates", "Bill Gates", "Gates, William H. III",
"Gates, William III", "William H Gates", "William H. Gates",
"Carlos Slim Helu & family", "Carlos Slim Helu",
"Carlos Slim & Family", "Carlos Slim")
new_names <- c("William Gates", "William Gates", "William Gates",
"William Gates", "William Gates", "William Gates",
"Carlos Slim Helu & family", "Carlos Slim Helu & family",
"Carlos Slim Helu & family", "Carlos Slim Helu & family")
nn <- c('Bill Gates','Carlos Slim')
cbind(names, sapply(nn, function(x)
ifelse(agrepl(x, names, max.distance = 5), x, NA)))
# names Bill Gates Carlos Slim
# [1,] "William Gates" "Bill Gates" NA
# [2,] "Bill Gates" "Bill Gates" NA
# [3,] "Gates, William H. III" "Bill Gates" NA
# [4,] "Gates, William III" "Bill Gates" NA
# [5,] "William H Gates" "Bill Gates" NA
# [6,] "William H. Gates" "Bill Gates" NA
# [7,] "Carlos Slim Helu & family" NA "Carlos Slim"
# [8,] "Carlos Slim Helu" NA "Carlos Slim"
# [9,] "Carlos Slim & Family" NA "Carlos Slim"
# [10,] "Carlos Slim" NA "Carlos Slim"
修改
names <- c("William Gates", "Bill Gates", "Gates, William H. III",
"Gates, William III", "William H Gates", "William H. Gates",
"Carlos Slim Helu & family", "Carlos Slim Helu",
"Carlos Slim & Family", "Carlos Slim")
names <- gsub('[[:punct:]]', '', names)
nn <- sort(table(unlist(strsplit(names, ' '))))
nn <- names(nn[nn >= 4])
cbind(names, sapply(nn, function(x)
ifelse(agrepl(x, names, max.distance = 1), x, NA)))
# names Carlos Slim William Gates
# [1,] "William Gates" NA NA "William" "Gates"
# [2,] "Bill Gates" NA NA NA "Gates"
# [3,] "Gates William H III" NA NA "William" "Gates"
# [4,] "Gates William III" NA NA "William" "Gates"
# [5,] "William H Gates" NA NA "William" "Gates"
# [6,] "William H Gates" NA NA "William" "Gates"
# [7,] "Carlos Slim Helu family" "Carlos" "Slim" NA NA
# [8,] "Carlos Slim Helu" "Carlos" "Slim" NA NA
# [9,] "Carlos Slim Family" "Carlos" "Slim" NA NA
# [10,] "Carlos Slim" "Carlos" "Slim" NA NA
答案 2 :(得分:2)
stringdist包可能有助于获取矩阵 - 它也在2014年6月R journal中有所描述。更新:其中一个qgram方法最适用于姓氏,姓氏或姓氏
library(stringdist)
stringdistmatrix(names, names, "jaccard")
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0.0000 0.273 0.286 0.167 0.0909 0.1667 0.632 0.562 0.647 0.571
[2,] 0.2727 0.000 0.467 0.385 0.3333 0.3846 0.684 0.625 0.706 0.643
[3,] 0.2857 0.467 0.000 0.143 0.2143 0.1429 0.636 0.579 0.714 0.667
[4,] 0.1667 0.385 0.143 0.000 0.2308 0.2857 0.667 0.611 0.684 0.625
[5,] 0.0909 0.333 0.214 0.231 0.0000 0.0833 0.579 0.500 0.667 0.600
...