在R

时间:2017-12-25 15:39:17

标签: r dataframe matching fuzzy

我有两个大数据集(每个500k obs),我想通过模糊字符串匹配链接到个人'名称,但也利用其他变量的信息。问题类似于此处描述的问题:How can I match fuzzy match strings from two datasets?

但是,在那里发布的解决方案需要首先通过expand.grid生成所有成对的潜在匹配,但是我的数据无法完成。如果您有两个数据集,每个数据集10,000个,则会产生100,000,000个潜在成对匹配的总数据集。

我想首先产生多对一合并,其中来自数据集A的观察$ k $与来自数据集B的5个最接近的匹配观察(由Jaro Winkler字符串距离判断)匹配,在某个年龄段,比如加/减5年。

例如,如果数据A中的$ k $是

name          birthyear
John Smith    1984

和数据集B中的其他观察结果是

serial   name           birthyear
1        John Smith     1983
2        Sara Pinkert   1973
3        John Smyth     1999
4        John Smithe    1985
5        John Smith     1984
6        Jon Smith      1984
然后是五个最好的"对于$ k $的匹配,数据B中的观察值应该是数字1,4,5,6,对于出生年限制为±5年。在这种情况下没有。 2(Sara Pinkert)由于名称不匹配,并且没有。 3(John Smyth)不应该匹配,因为这个观察的出生年份为时已晚。

fastLinkstringdistrecordLinkage等其他图书馆提供的功能和命令既好又快,但它们只会产生一对一的匹配(而且很少有结合来自出生年份频段的信息以限制匹配问题的维度的能力。)

到目前为止,我能够弄清楚的关闭解决方案是使用compare.linkage中的recordLinkage函数,但是阻塞选项(blockfld)似乎严格阻止特定变量,因此它“#1;}并不清楚如何使用出生年份信息的范围:

rpairs = compare.linkage(dataA, 
                     dataB, 
                     blockfld = c("birthyear"), 
                     identity1 = dataA$id1, 
                     identity2 = dataB$id2, 
                     n_match = 5, 
                     strcmpfun = jarowinkler)

但是这只能在完美的分娩期间阻止,因此它将返回两场比赛,这将是没有。 5和6(John Smith 1984,Jon Smith 1984)。

以下是匹配问题的一些示例数据。由于体积小,看起来微不足道,但在整个样本中每个有50万个盲(其中一些出现在一个但不出现在其他数据中,有些出现在两个中,但可能在其名称中有拼写错误)它'更棘手。

name1 = c("John Smith", "Adam Bower", "Felix von Epstein", "Charles Sawyer", "Benjamin Hoynes")
yob1 = c(1980, 1977, 1981, 1981, 1978)
dataA = data.frame(name1, yob1)

name2 = c("Jon Smyth", "Perry Bower", "Felix Epstein", "Terry Barnes", "John Smith", "Benamin Hoynes", "Frank Sawyer", "Charles Sawer", "Charles Sauer", "Philip Smith", "Franklin Sawyer", "Jonathan Smith", "Gabriel Bars", "Aron Bow", "Harry Haynes")
yob2 = c(1981, 1983, 1981, 1982, 1983, 1980, 1980, 1986, 1982, 1978, 1977, 1981, 1979, 1975, 1980)
dataB = data.frame(name2, yob2)

1 个答案:

答案 0 :(得分:2)

根据评论

编辑其他代码

也许这会对你有所帮助

您的数据

model.docvecs

近似字符串匹配和年龄段过滤的功能

name1 = c("John Smith", "Adam Bower", "Felix von Epstein", "Charles Sawyer", "Benjamin Hoynes")
yob1 = c(1980, 1977, 1981, 1981, 1978)
dataA = data.frame(name1, yob1)

name2 = c("Jon Smyth", "Perry Bower", "Felix Epstein", "Terry Barnes", "John Smith", "Benamin Hoynes", "Frank Sawyer", "Charles Sawer", "Charles Sauer", "Philip Smith", "Franklin Sawyer", "Jonathan Smith", "Gabriel Bars", "Aron Bow", "Harry Haynes")
yob2 = c(1981, 1983, 1981, 1982, 1983, 1980, 1980, 1986, 1982, 1978, 1977, 1981, 1979, 1975, 1980)
dataB = data.frame(name2, yob2)

其核心是

top_five_amatch <- function(A_row, B) {
                require(stringdist)
                ans <- intersect(order(stringdist(A_row$name1, dataB$name2, method="jw")), which(abs(A_row$yob1 - dataB$yob2) <= 5))
                return(head(ans, 5))
            }
2中的

library(stringdist) order(stringdist(dataA$name1[1], dataB$name2, method="jw")) # order of string-distance # [1] 5 1 12 10 14 7 8 9 6 11 3 2 4 15 13 which(abs(dataA$yob1[1] - dataB$yob2) <= 5) # age band filter # [1] 1 2 3 4 5 6 7 9 10 11 12 13 14 15 将仅保留年龄段过滤后出现的值

主要
抓取每行intersect

最接近匹配的索引
dataA

I <- lapply(seq_len(nrow(dataA)), function(i) top_five_amatch(dataA[i,], dataB)) # [[1]] # [1] 5 1 12 10 14 # [[2]] # [1] 14 7 1 4 6 # [[3]] # [1] 3 1 2 6 11 # [[4]] # [1] 8 9 7 11 2 # [[5]] # [1] 6 15 4 2 11

每行的前5个匹配项
dataA

要使用多列保存为“宽”格式,请尝试使用

matchB <- dataB[unlist(I), ]
               # name2 yob2
# 5         John Smith 1983
# 1          Jon Smyth 1981
# 12    Jonathan Smith 1981
# 10      Philip Smith 1978
# 14          Aron Bow 1975
# 14.1        Aron Bow 1975
# 7       Frank Sawyer 1980
# 1.1        Jon Smyth 1981
# 4       Terry Barnes 1982
# 6     Benamin Hoynes 1980
# 3      Felix Epstein 1981
# 1.2        Jon Smyth 1981
# 2        Perry Bower 1983
# 6.1   Benamin Hoynes 1980
# 11   Franklin Sawyer 1977
# 8      Charles Sawer 1986
# 9      Charles Sauer 1982
# 7.1     Frank Sawyer 1980
# 11.1 Franklin Sawyer 1977
# 2.1      Perry Bower 1983
# 6.2   Benamin Hoynes 1980
# 15      Harry Haynes 1980
# 4.1     Terry Barnes 1982
# 2.2      Perry Bower 1983
# 11.2 Franklin Sawyer 1977