【问题标题】:Compare and link strings with different word orders / word counts比较和链接具有不同字序/字数的字符串
【发布时间】:2019-04-21 05:07:46
【问题描述】:

我正在尝试使用 recordLinkage 包将两个数据集链接在一起,其中一个数据集倾向于给出多个姓氏/中间名,而另一个只给出一个姓氏。目前正在使用的字符串比较函数是 Jaro-Winkler 函数,但是返回的分数取决于字符串是如何偶然匹配的,而不是较短字符串的内容是否包含在较长字符串中的任何位置。这导致创建了许多质量较差的链接。错误权重的可重现示例如下:

library(RecordLinkage)
data1 <- as.data.frame(list("lname" = c("lolli gaggen nazeem", "lolli gaggen nazeem", "lolli gaggen nazeem"),
                           "bday" = c("1908-08-08", "1979-12-12", "1560-06-06") ) )

data2 <- as.data.frame(list("lname" = c("lolli", "gaggen", "nazeem"),
                           "bday" = c("1908-08-08", "1979-12-12", "1560-06-06") ) )

blocking_variable <- c("bday")
pass <- compare.linkage(data1, data2, blockfld = blocking_variable, strcmp = T)
pass_weights <- epiWeights(pass)
getPairs(pass_weights, single.rows = TRUE)

  id1              lname.1     bday.1 id2 lname.2     bday.2    Weight
1   1 lolli gaggen nazheem 1908-08-08   1   lolli 1908-08-08 0.9162463
2   2 lolli gaggen nazheem 1979-12-12   2  gaggen 1979-12-12 0.8697165
3   3 lolli gaggen nazheem 1560-06-06   3 nazheem 1560-06-06 0.6995502

我希望 id 的 2 和 3 获得与 id #1 大致相同的权重,但目前它们的权重要低得多,因为它们的姓在两个数据集中的位置并不完全相同(尽管内容是一致的)。有没有办法可以修改此处使用的字符串比较函数/数据结构,以便考虑不同的顺序?

补充说明:

【问题讨论】:

    标签: r string-comparison fuzzy-comparison record-linkage jaro-winkler


    【解决方案1】:

    您是否考虑过以下方法?

    据我所知,记录链接和名称很困难。理想情况下,您希望屏蔽其他可用信息(性别、唯一标识符、dob、位置信息等),然后对名称进行字符串比较。

    您提到了包含数百万条记录的大型数据集。看看伟大的 Matt Dowle (https://stackoverflow.com/users/403310/matt-dowle) 的 data.table 软件包就知道了。

    相比之下,RecordLinkage 包比较慢。您可以轻松改进以下代码,以考虑使用 soundex、双变音、nysii 等的字符串散列技术。

    # install.packages("data.table")
    library(RecordLinkage)
    library(data.table)
    
    data1 <- as.data.frame(list("lname" = c("lolli gaggen nazeeem", "lolli gaggen nazeem", "lollly gaggen nazeem", "matt dowle", "john-smith"),
                               "bday" = c("1908-08-08", "1979-12-12", "1560-06-06", "1979-12-12", "1560-06-06") ) )
    
    data2 <- as.data.frame(list("lname" = c("lolli", "gaggen", "nazeem", "m dowl", "johnny smith"),
                               "bday" = c("1908-08-08", "1979-12-12", "1560-06-06", "1979-12-12", "1560-06-06") ) )
    
    
    # Coerce to data.tables
    setDT(data1)
    setDT(data2)
    
    # Define a regex split (we will split all words based on space or hyphen)
    split <- " |-"
    
    # Apply a blocking strategy based on bday. Ideally your dataset would allow for additional blocking strategies(?).
    block_pairs <- merge(data1, data2, by = "bday", all = T,
                sort = TRUE, suffixes = c(".x", ".y"))
    
    # Store the split up components of each comparison variable.
    split1 <- strsplit(block_pairs[["lname.x"]], split)
    split2 <- strsplit(block_pairs[["lname.y"]], split)
    
    # Perform jarowinkler comparisons on each combination of components of each string
    fc <- jarowinkler(block_pairs[["lname.x"]], block_pairs[["lname.y"]])
    pc <- mapply(function(x, y) max(outer(x, y, jarowinkler)), split1, split2)
    
    # Store the max of the full and partial comparisons
    block_pairs[, ("winkler.lname") := mapply(function(x,y) max(x,y), fc, pc)]
    
    
    # Sort by the jarowinkler score
    block_pairs <- block_pairs[order(winkler.lname)]
    
    # Inspect
    block_pairs
    
    # 0.96 is an appropriate threshold in this instance
    block_pairs <- block_pairs[winkler.lname >= 0.96]
    

    【讨论】:

    • 这非常适合我的要求,非常感谢。稍后我意识到,某些种族可能会出现一些问题,在这些种族中,姓氏通常以“......来自......”的形式得到比他们应该的更高的权重 - 我为解决这个问题所做的就是粘贴在一起使用每种可能的排列将所有拆分的组件然后执行字符串比较:
    • 很高兴它有帮助!
    【解决方案2】:

    如评论中所述,我对 Khayenes 的回答进行了补充:

    library(gtools)
    
    ...
    
    # Store the split up components of each comparison variable.
    split1 <- strsplit(block_pairs[["lname.x"]], split)
    split2 <- strsplit(block_pairs[["lname.y"]], split)
    
    # Recombine tokens into all possible orderings:
    make_combinations <- function(x) {
          # Use permutations from the gtools package
          split_names <- permutations(length(x),length(x),x)
          apply(X=split_names, MARGIN=1, FUN=paste0, collapse=' ')
    }
    
    split1 <- lapply(X=split1, FUN=`make_combinations`)
    split2 <- lapply(X=split2, FUN=`make_combinations`)
    
    # Perform jarowinkler comparisons on each string combination and append it to the table
    block_pairs[ ,("winkler.lname") := mapply(function(x, y) max(outer(x, y, jarowinkler)), split1, split2)]
    
    # Sort by the jarowinkler score
    block_pairs <- block_pairs[order(winkler.lname)]
    
    # 0.85 is an appropriate threshold in this instance
    block_pairs <- block_pairs[winkler.lname >= 0.85]
    
    
          bday           lname.x             lname.y    winkler.lname
    1: 1908-08-08  lolli gaggen nazeem         lolli     0.8526316
    2: 1560-06-06  lolli gaggen nazeem        nazeem     0.8631579
    3: 1979-12-12  lolli gaggen nazeem        gaggen     0.8631579
    4: 1979-12-12           matt dowle        m dowl     0.9200000
    5: 1560-06-06           john-smith  johnny smith     0.9666667
    

    【讨论】:

      猜你喜欢
      • 2017-01-28
      • 2014-03-18
      • 2018-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      • 2013-11-12
      • 2022-12-05
      相关资源
      最近更新 更多