【问题标题】:Cartesian joining half of a table with data.table使用 data.table 连接半个表的笛卡尔
【发布时间】:2019-12-02 15:57:27
【问题描述】:

假设我有以下data.table:

library(data.table)
dt <- data.table(
  a = c(1L, 1L, 2L, 2L), 
  b = c('A', 'A', NA_character_, NA_character_),
  Amount = 1:4
)

我想执行笛卡尔连接以将权重应用于金额列,假设我有下表的权重:

set.seed(42L)
weights <- data.table(
  a = c(rep(1L, 5L), rep(2L, 5L)),
  b = LETTERS[1:10],
  Weight = runif(10L)
)

获得我想要的东西的一种方法是:

rbind(
  dt[a == 1L],
  dt[a != 1L][
    weights,
    .(a, b = i.b, Amount = Amount * Weight),
    on = c('a'),
    nomatch = 0L,
    allow.cartesian = TRUE
  ]
)

编辑:我同事的一项改进是对第一项的改进,但对性能没有太大帮助:

rbind(
  dt[a == 1L],
  dt[a != 1L][
    weights,
    modifyList(.SD, .(b = i.b, Amount = Amount * Weight)),
    on = c('a'),
    nomatch = 0L,
    allow.cartesian = TRUE
    ]
)

导致:

    a b    Amount
 1: 1 A 1.0000000
 2: 1 A 2.0000000
 3: 2 F 1.5572878
 4: 2 F 2.0763838
 5: 2 G 2.2097649
 6: 2 G 2.9463533
 7: 2 H 0.4039998
 8: 2 H 0.5386664
 9: 2 I 1.9709769
10: 2 I 2.6279692
11: 2 J 2.1151944
12: 2 J 2.8202591

我想知道/改进两件事:

  1. 如果列数增加,则需要在最后的代码块中指定所有列.(a, b = i.b, Amount = Amount * Weight) 继续增加。
  2. 如果行数增加到数百万,是否有更有效的方法?

【问题讨论】:

  • 我很困惑。这是否会产生预期的结果:dt[, Amount := Amount + 0]; dt[weights, Amount := Amount * i.Weight, on = .(a, b)]?
  • @mt1022 我不这么认为,请参阅我的编辑。

标签: r join data.table


【解决方案1】:

对于 qn1,可能是这样的:

othercols <- setdiff(names(weights), "Weight")
rbindlist(list(
    dt[a == 1L],
    dt[a != 1L][
        weights,
        c(setNames(mget(paste0("i.", othercols)), othercols), .(Amount = Amount * Weight)),
        on=.(a),
        nomatch = 0L,
        allow.cartesian = TRUE
    ]
), use.names=TRUE)

对于qn2,真的需要笛卡尔join来扩展数据这么多吗?

【讨论】:

    猜你喜欢
    • 2018-11-27
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-12
    相关资源
    最近更新 更多