You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用data.table实现含可变列数的full join与update join结合

问题:结合全连接与更新连接动态处理data.table的可变列数

我需要将全连接和更新连接结合起来,处理列数可变的data.table场景,但未找到覆盖该场景的解决方案。核心需求是:

  • updateDT可新增行和任意数量的列
  • updateDT中的数据优先级始终高于DT的现有数据

示例数据

初始数据表 DT

key_col value_col_1 value_col_2
    <char>       <int>       <int>
1:       a           1          11
2:       b           2          12
3:       c           3          13
4:       d           4          14
5:       e           5          15

更新数据表 updateDT

key_col value_col_1 value_col_2 value_col_3
    <char>       <int>       <int>       <int>
1:       d          11          21          31
2:       e          12          22          32
3:       f          13          23          33
4:       g          14          24          34

预期结果 resultDT

key_col value_col_1 value_col_2 value_col_3
    <char>       <int>       <int>       <int>
1:       a           1          11          NA
2:       b           2          12          NA
3:       c           3          13          NA
4:       d          11          21          31
5:       e          12          22          32
6:       f          13          23          33
7:       g          14          24          34

硬编码实现(存在局限性)

我可以通过硬编码列名实现需求,但代码重复,且难以推广到动态处理updateDT中更多列的场景:

library(data.table)

DT <- data.table(key_col = letters[1:5], value_col_1 = 1:5, value_col_2 = 11:15)
updateDT <- data.table(key_col = letters[4:7], value_col_1 = 11:14, value_col_2 = 21:24, value_col_3 = 31:34)
key_column <- "key_col"
value_columns <- setdiff(c(names(DT), names(updateDT)), key_column)

resultDT <- merge(DT, updateDT, by = "key_col", all = TRUE)
resultDT[, value_col_1 := fifelse(is.na(value_col_1.y), yes = value_col_1.x, no = value_col_1.y)]
resultDT[, value_col_2 := fifelse(is.na(value_col_2.y), yes = value_col_2.x, no = value_col_2.y)]
resultDT[, c("value_col_1.x", "value_col_1.y", "value_col_2.x", "value_col_2.y") := NULL]
print(resultDT)

编辑:基准测试结果(选用更快的方案)

接受ismirsehregal的答案,因其速度更快,以下是基准测试代码及结果:

library(microbenchmark)
microbenchmark(
  ismirsehregal = {
    resultDT1 <- rbindlist(list(old = DT, new = updateDT), use.names = TRUE, fill = TRUE, idcol = "origin")[, key_count := .N, by = key_column]
    resultDT1 <- resultDT1[key_count == 1L | key_count > 1L & origin == 'new'][, c("key_count", "origin") := NULL]
    },
  Wimpel = {
    DT[updateDT, on=.(key_col), (value_columns) := mget(paste0("i.", value_columns))]
    resultDT2 <- rbindlist(list(DT, updateDT[!key_col %in% DT$key_col,]), use.names = TRUE, fill = TRUE)
    }, times = 10L)

运行结果:

Unit: milliseconds
          expr    min     lq    mean median     uq    max neval
 ismirsehregal 1.2166 1.2345 1.33790 1.2933 1.3531 1.8147    10
        Wimpel 3.5286 3.5989 3.85624 3.7680 4.0916 4.5984    10

第二次编辑:updateDT不包含DT所有列的场景

当updateDT不包含DT的全部列时,原方案会出现问题:

library(data.table)

DT <- data.table(key_col = letters[1:5], value_col_1 = 1:5, value_col_2 = 11:15)
updateDT <- data.table(key_col = letters[4:7], value_col_2 = 11:14, value_col_3 = 21:24, value_col_4 = 31:34)
key_column <- "key_col"
# value_columns <- setdiff(c(names(DT), names(updateDT)), key_column) # Error: value for ‘i.value_col_1’ not found
value_columns <- setdiff(intersect(names(DT), names(updateDT)), key_column) # does not insert all data from updateDT

DT[updateDT, on=.(key_col), (value_columns) := mget(paste0("i.", value_columns))]
rbindlist(list(DT, updateDT[!key_col %in% DT$key_col,]), use.names = TRUE, fill = TRUE)

内容的提问来源于stack exchange,提问作者ahnungslos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:47:03