比较和 link 具有不同词序/字数的字符串
Compare and link strings with different word orders / word counts
我正在尝试使用 recordLinkage 包 link 将两个数据集放在一起,其中一个数据集倾向于给出多个姓氏/中间名,而另一个只给出一个姓氏。目前使用的字符串比较函数是 Jaro-Winkler 函数,但返回的分数取决于字符串是如何偶然匹配的,而不是较短字符串的内容是否包含在较长字符串中的任何位置。这导致创建了许多质量较差的 link。错误权重的可重现示例如下:
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 大致相同的权重,但目前它们要低得多,因为它们的姓氏在两个数据集中的位置并不完全相同(尽管内容是一致的)。有没有办法修改这里使用的字符串比较函数/数据结构,以便我可以考虑不同的排序?
补充说明:
两个数据集都有数百万行,所以内存效率是
这里绝对重要!
有时其他数据集可能不止一个最后一个
name 所以我们将 3 个词与 2 个词进行比较 - 可能是
不过最好先从简单的情况着手
- 名字往往会有拼写差异
在两个数据集之间
- 目前我们正在使用 IBM 的质量阶段来做到这一点 linking 并且
他们使用 "MULT_UNCERT" 比较函数
(https://www.ibm.com/support/knowledgecenter/en/SSZJPZ_11.7.0/com.ibm.swg.im.iis.ds.design.help.doc/topics/r_qresfgde_MULT_UNCERT_comparison.html)。
我想在 R 中复制它。
你有没有想过下面的方法?
如我所知,记录链接和名称很困难。理想情况下,您希望阻止其他可用信息(性别、唯一标识符、出生日期、位置信息等),然后对名称进行字符串比较。
您提到了包含数百万条记录的大型数据集。看看伟大的 Matt Dowle (https://whosebug.com/users/403310/matt-dowle) 的 data.table
包就知道了。
RecordLinkage 包比较慢。您可以轻松改进以下代码,以考虑使用 soundex、double metaphone、nysiis 等
的字符串哈希技术
# 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]
我对评论中概述的 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
我正在尝试使用 recordLinkage 包 link 将两个数据集放在一起,其中一个数据集倾向于给出多个姓氏/中间名,而另一个只给出一个姓氏。目前使用的字符串比较函数是 Jaro-Winkler 函数,但返回的分数取决于字符串是如何偶然匹配的,而不是较短字符串的内容是否包含在较长字符串中的任何位置。这导致创建了许多质量较差的 link。错误权重的可重现示例如下:
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 大致相同的权重,但目前它们要低得多,因为它们的姓氏在两个数据集中的位置并不完全相同(尽管内容是一致的)。有没有办法修改这里使用的字符串比较函数/数据结构,以便我可以考虑不同的排序?
补充说明:
两个数据集都有数百万行,所以内存效率是 这里绝对重要!
有时其他数据集可能不止一个最后一个 name 所以我们将 3 个词与 2 个词进行比较 - 可能是 不过最好先从简单的情况着手
- 名字往往会有拼写差异 在两个数据集之间
- 目前我们正在使用 IBM 的质量阶段来做到这一点 linking 并且 他们使用 "MULT_UNCERT" 比较函数 (https://www.ibm.com/support/knowledgecenter/en/SSZJPZ_11.7.0/com.ibm.swg.im.iis.ds.design.help.doc/topics/r_qresfgde_MULT_UNCERT_comparison.html)。 我想在 R 中复制它。
你有没有想过下面的方法?
如我所知,记录链接和名称很困难。理想情况下,您希望阻止其他可用信息(性别、唯一标识符、出生日期、位置信息等),然后对名称进行字符串比较。
您提到了包含数百万条记录的大型数据集。看看伟大的 Matt Dowle (https://whosebug.com/users/403310/matt-dowle) 的 data.table
包就知道了。
RecordLinkage 包比较慢。您可以轻松改进以下代码,以考虑使用 soundex、double metaphone、nysiis 等
的字符串哈希技术# 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]
我对评论中概述的 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