在R中查找字符串的部分匹配

时间:2015-03-09 21:25:19

标签: regex r string compare

我有一个非常大的数据库,其名称如下:

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很熟悉,但我没有使用字符串匹配。如果有人指我到某个地方开始,我可以尝试用一些示例代码重新解释我的问题。

3 个答案:

答案 0 :(得分:3)

基于adist和群集的完整答案。

使用参数partial=TRUEignore.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
 ...