受到实验的启发fuzzy_join
函数从statar https://github.com/matthieugomez/statar我自己编写了一个函数,它结合了精确匹配和模糊匹配(按字符串距离)。我必须做的合并工作相当大(导致多个字符串距离矩阵,单元格略少于十亿),我的印象是fuzzy_join
函数的编写效率不高(就内存使用而言),并且并行化以一种奇怪的方式实现(如果存在多个模糊变量,则字符串距离矩阵的计算,而不是字符串距离本身的计算是并行化的) 。至于fuzzy_join
函数的想法是如果可能的话匹配精确的变量(以保持矩阵更小),然后在这个精确匹配的组中进行模糊匹配。我实际上认为该功能是不言自明的。我把它发布在这里是因为我想得到一些反馈来改进它,并且因为我想我不是唯一一个尝试在 R 中做类似事情的人(尽管我承认 Python、SQL 和类似的东西可能会在这种情况下会更有效率。但是我们必须坚持做自己感觉最舒服的事情,并且用同一种语言进行数据清理和准备对于可重复性来说是很好的)
merge.fuzzy = function(a,b,.exact,.fuzzy,.weights,.method,.ncores) {
require(stringdist)
require(matrixStats)
require(parallel)
if (length(.fuzzy)!=length(.weights)) {
stop(paste0("fuzzy and weigths must have the same length"))
}
if (!any(class(a)=="data.table")) {
stop(paste0("'a' must be of class data.table"))
}
if (!any(class(b)=="data.table")) {
stop(paste0("'b' must be of class data.table"))
}
#convert everything to lower
a[,c(.fuzzy):=lapply(.SD,tolower),.SDcols=.fuzzy]
b[,c(.fuzzy):=lapply(.SD,tolower),.SDcols=.fuzzy]
a[,c(.exact):=lapply(.SD,tolower),.SDcols=.exact]
b[,c(.exact):=lapply(.SD,tolower),.SDcols=.exact]
#create ids
a[,"id.a":=as.numeric(.I),by=c(.exact,.fuzzy)]
b[,"id.b":=as.numeric(.I),by=c(.exact,.fuzzy)]
c <- unique(rbind(a[,.exact,with=FALSE],b[,.exact,with=FALSE]))
c[,"exa.id":=.GRP,by=.exact]
a <- merge(a,c,by=.exact,all=FALSE)
b <- merge(b,c,by=.exact,all=FALSE)
##############
stringdi <- function(a,b,.weights,.by,.method,.ncores) {
sdm <- list()
if (is.null(.weights)) {.weights <- rep(1,length(.by))}
if (nrow(a) < nrow(b)) {
for (i in 1:length(.by)) {
sdm[[i]] <- stringdistmatrix(a[[.by[i]]],b[[.by[i]]],method=.method,ncores=.ncores,useNames=TRUE)
}
} else {
for (i in 1:length(.by)) { #if a is shorter, switch sides; this enhances parallelization speed
sdm[[i]] <- stringdistmatrix(b[[.by[i]]],a[[.by[i]]],method=.method,ncores=.ncores,useNames=FALSE)
}
}
rsdm = dim(sdm[[1]])
csdm = ncol(sdm[[1]])
sdm = matrix(unlist(sdm),ncol=length(by))
sdm = rowSums(sdm*.weights,na.rm=T)/((0 + !is.na(sdm)) %*% .weights)
sdm = matrix(sdm,nrow=rsdm,ncol=csdm)
#use ids as row/ column names
rownames(sdm) <- a$id.a
colnames(sdm) <- b$id.b
mid <- max.col(-sdm,ties.method="first")
mid <- matrix(c(1:nrow(sdm),mid),ncol=2)
bestdis <- sdm[mid]
res <- data.table(as.numeric(rownames(sdm)),as.numeric(colnames(sdm)[mid[,2]]),bestdis)
setnames(res,c("id.a","id.b","dist"))
res
}
setkey(b,exa.id)
distances = a[,stringdi(.SD,b[J(.BY[[1]])],.weights=.weights,.by=.fuzzy,.method=.method,.ncores=.ncores),by=exa.id]
a = merge(a,distances,by=c("exa.id","id.a"))
res = merge(a,b,by=c("exa.id","id.b"))
res
}
以下几点会很有趣:
- 我不太确定如何在中编写多个完全匹配的变量
data.table
我上面使用的样式(我认为这是最快的选择)。
- 是否可以进行嵌套并行化?这意味着可以在计算字符串距离矩阵的基础上使用并行的 foreach 循环。
- 我也对提高整个事情效率的想法感兴趣,即消耗更少的内存。
- 也许您可以建议一个更大的“现实世界”数据集,以便我可以创建一个可行的示例。不幸的是,我无法与您分享哪怕是一小部分数据样本。
- 将来,除了经典的左内连接之外,做一些其他的事情也很好。因此,关于这个主题的想法也非常受欢迎。
欢迎您提出宝贵意见!