如何在join操作过程中同时更新两个data.table对象
问题描述
我想要追踪一个data.table中的哪些行被合并到了另一张data.table中,是否可以在合并的同时一次性完成该操作?以下是我的示例以及我目前常用的实现方式,但该方式效率偏低。
示例
library(data.table) # 初始数据 DT = data.table(x = c(1,1,1,2,2,1,1,2,2), y = c(1,3,6)) # 待合并数据 DTx <- data.table(x = 1:3, y = 1, k = "X") # 常规更新连接 copy(DT)[DTx, on = .(x, y), k := i.k][] #> x y k #> 1: 1 1 X #> 2: 1 3 <NA> #> 3: 1 6 <NA> #> 4: 2 1 X #> 5: 2 3 <NA> #> 6: 1 6 <NA> #> 7: 1 1 X #> 8: 2 3 <NA> #> 9: 2 6 <NA> # DTx未发生变化 DTx #> x y k #> 1: 1 1 X #> 2: 2 1 X #> 3: 3 1 X
目前常用的实现方式
# 新增Id变量 DTx[, Id := .I] # 合并时赋值Id DT[DTx, on = .(x, y), `:=`(k = i.k, matched_id = i.Id)][] #> x y k matched_id #> 1: 1 1 X 1 #> 2: 1 3 <NA> NA #> 3: 1 6 <NA> NA #> 4: 2 1 X 2 #> 5: 2 3 <NA> NA #> 6: 1 6 <NA> NA #> 7: 1 1 X 1 #> 8: 2 3 <NA> NA #> 9: 2 6 <NA> NA # 用matched_id筛选被合并的行 DTx[, matched := fifelse(Id %in% DT$matched_id, TRUE, FALSE)] DTx #> x y k Id matched #> 1: 1 1 X 1 TRUE #> 2: 2 1 X 2 TRUE #> 3: 3 1 X 3 FALSE
解决方案
可以通过data.table的.EACHI分组特性一次性完成操作,无需额外创建Id列、也无需后续做低效的向量匹配,性能提升非常明显,尤其适合大数据量场景。
实现代码
方案1:无需保留DT中的匹配Id,仅标记DTx的匹配状态
# 初始化原始数据 DT = data.table(x = c(1,1,1,2,2,1,1,2,2), y = c(1,3,6)) DTx <- data.table(x = 1:3, y = 1, k = "X") # 一步完成DT更新 + DTx匹配标记 DT[DTx, on = .(x,y), # 给DT更新k字段 `:=`(k = i.k), # 按DTx的每一行匹配结果分组 by = .EACHI, # 给DTx的对应匹配行打标记 DTx[.BY, matched := TRUE] ][] # 查看DTx结果,默认未匹配的行matched为NA,可按需赋值为FALSE DTx[is.na(matched), matched := FALSE][] #> x y k matched #> 1: 1 1 X TRUE #> 2: 2 1 X TRUE #> 3: 3 1 X FALSE
方案2:需要保留DT中的匹配Id
DT = data.table(x = c(1,1,1,2,2,1,1,2,2), y = c(1,3,6)) DTx <- data.table(x = 1:3, y = 1, k = "X")[, Id := .I] # 一次连接完成所有操作 DT[DTx, on = .(x,y), `:=`(k = i.k, matched_id = i.Id), by = .EACHI, DTx[.BY, matched := TRUE] ][] DTx[is.na(matched), matched := FALSE][] #> x y k Id matched #> 1: 1 1 X 1 TRUE #> 2: 2 1 X 2 TRUE #> 3: 3 1 X 3 FALSE
原理说明
.EACHI会按右表(此处为DTx)的每一行匹配结果分组,.BY存储了当前分组的连接键值,直接用该键值对DTx做更新操作,全程使用data.table的二分查找索引,避免了原方案中%in%操作的全量向量扫描,时间复杂度更低。
内容的提问来源于stack exchange,提问作者mnist
相关产品推荐
相关产品推荐

