基于正则模式匹配实现两个data.table的高效左连接方法
基于正则模式匹配实现两个data.table的高效左连接方法
嗨,我明白你想要的效果——把dat1里的每一行正则模式,和dat2里所有匹配该模式的行做左连接,生成你要的结果集。你尝试的on=.(grepl(pat,q))写法之所以不行,是因为data.table的on=参数主要是用来定义列之间的对等或范围匹配的,这种动态的逐行正则判断没法直接用在这里。
针对你的场景(dat1几百行,dat210-100万行),我推荐两种实用的方法,其中第一种更适合大数据量的高效处理:
方法一:循环匹配+快速合并(推荐)
因为dat1的行数很少(几百行),我们可以循环遍历dat1的每一行,筛选出dat2中匹配当前正则的行,再和dat1的行合并,最后用rbindlist快速整合结果。这种方式内存占用低,效率也更高,不会像全交叉连接那样生成海量中间数据。
代码示例:
library(data.table) # 初始化你的数据 dat1 <- setDT(data.frame(pat=c("A.C",".BC"),val=c(1,2))) dat2 <- setDT(data.frame(q=c("ABC","AXC","XBC"),val2=c(10,11,12))) # 核心逻辑 dat3 <- rbindlist(lapply(1:nrow(dat1), function(i) { # 取出dat1当前行的模式和值 current_pat <- dat1[i, .(pat, val)] # 筛选dat2中匹配该模式的行 matched_rows <- dat2[grepl(current_pat$pat, q)] # 合并当前行和匹配结果 cbind(current_pat, matched_rows) })) # 查看结果 dat3
运行后你会得到完全符合预期的输出:
pat val q val2 1: A.C 1 ABC 10 2: A.C 1 AXC 11 3: .BC 2 ABC 10 4: .BC 2 XBC 12
方法二:交叉连接后过滤(适合小数据量)
如果你觉得循环不够“简洁”,也可以先做全交叉连接,再过滤出匹配正则的行。但要注意,这种方法会先生成dat1行数 × dat2行数的中间表,当dat2是100万行时,中间表会有几十亿行,内存压力会很大,所以只适合dat2行数较少的场景。
代码示例:
# 生成全交叉连接(allow.cartesian=TRUE允许笛卡尔积) cross_join <- dat1[dat2, on=.(), allow.cartesian=TRUE] # 过滤匹配正则的行 dat3 <- cross_join[grepl(pat, q)]
额外优化提示
- 如果dat1里有重复的正则模式,可以先对dat1去重,处理完后再合并回原数据,减少循环次数;
- 如果你的“正则”其实是固定字符串匹配(只是用了点号),可以给
grepl加上fixed=TRUE参数,能大幅提升匹配速度。
备注:内容来源于stack exchange,提问作者isthisthat
相关产品推荐
相关产品推荐

