data.table非等连接:能否避免显式计算列实现区间匹配?
这确实是个很实用的需求——不想多创建一列Z来完成非等连接,data.table其实完全能做到,只是得调整一下连接的写法~你之前尝试直接在on里写X+Y>=X_min失败,大概率是因为用的data.table版本比较旧,对on参数里的表达式支持有限,或者写法需要微调。下面给你两种靠谱的方案:
方案1:反向查找+.SD直接更新
这种方法完全不用创建任何临时列,直接在DT1里完成赋值:
set.seed(2018) DT1 <- data.table(ID = seq(10), X = round(500*runif(10)), Y = round(500*runif(10))) DT2 <- data.table(ID = seq(10), X_min = seq(0,900,100), X_max = seq(99,999,100), Text=LETTERS[1:10]) # 直接给DT1新增Description列,无需显式计算Z DT1[, Description := DT2[.SD, Text, on = .(X_min <= X+Y, X_max >= X+Y)]]
这里的逻辑很直观:
.SD代表DT1的当前行,每次迭代都会自动计算该行的X+YDT2[.SD, ...]会在DT2里找满足X_min ≤ X+Y ≤ X_max的行,返回对应的Text值- 最后把这些
Text值直接赋值给DT1的新列Description
方案2:用foverlaps做区间匹配
如果你的data.table版本支持foverlaps(现在大部分新版本都支持),可以把DT1的X+Y转换成一个“单点区间”(也就是起始和结束值都是X+Y),然后和DT2的[X_min, X_max]区间做重叠匹配:
# 临时生成单点区间并完成匹配,不修改原DT1的结构 matched_result <- foverlaps( DT1[, .(ID, start = X+Y, end = X+Y)], DT2[, .(DT2_ID = ID, X_min, X_max, Text)], by.x = c("start", "end"), by.y = c("X_min", "X_max"), type = "within" # 只匹配完全落在目标区间内的点 )[, .(ID, X = DT1$X[match(ID, DT1$ID)], Y = DT1$Y[match(ID, DT1$ID)], Description = Text)] # 如果要直接更新原DT1,用merge合并即可 DT1 <- merge(DT1, matched_result[, .(ID, Description)], by = "ID", all.x = TRUE)
这种方法更适合复杂的区间匹配场景,比如多维度区间判断,不过需要注意匹配后的列名整理。
补充:新版本的简化写法
如果你用的是data.table 1.14.0及以上的版本,其实你最开始尝试的写法是可以直接运行的!比如:
DT1[DT2, Description := i.Text, on = .(X + Y >= X_min, X + Y <= X_max)]
如果之前这个写法失败,建议先运行packageVersion("data.table")检查版本,版本太低的话更新到最新版就能解决问题啦。
内容的提问来源于stack exchange,提问作者HannesZ
相关产品推荐
相关产品推荐

