【问题标题】:data.table replace data using values from another data.table, conditionallydata.table 有条件地使用另一个 data.table 中的值替换数据
【发布时间】:2017-03-01 16:52:28
【问题描述】:

这类似于Update values in data.table with values from another data.tableR data.table replacing an index of values from another data.table,除了在我的情况下变量的数量非常大,所以我不想明确列出它们。

我拥有的是一个大的data.table(我们称之为dt_original)和一个较小的data.table(我们称之为dt_newdata),它们的ID是第一个的子集,它只有一些变量第一个。我想用dt_newdata 中的值更新dt_original 中的值。为了增加一个转折点,我想有条件地更新这些值 - 在这种情况下,仅当 dt_newdata 中的值大于 dt_original 中的相应值时。

对于可重现的示例,以下是数据。在现实世界中,表格要大得多:

library(data.table)
set.seed(0)

## This data.table with 20 rows and many variables is the existing data set
dt_original <- data.table(id = 1:20)
setkey(dt_original, id)

for(i in 2015:2017) {
  varA <- paste0('varA_', i)
  varB <- paste0('varB_', i)
  varC <- paste0('varC_', i)

  dt_original[, (varA) := rnorm(20)]
  dt_original[, (varB) := rnorm(20)]
  dt_original[, (varC) := rnorm(20)]
}

## This table with a strict subset of IDs from dt_original and only a part of
## the variables is our potential replacement data
dt_newdata <- data.table(id = sample(1:20, 3))
setkey(dt_newdata, id)

newdata_vars <- sample(names(dt_original)[-1], 4)

for(var in newdata_vars) {
  dt_newdata[, (var) := rnorm(3)]
}

这是一种使用循环和pmax 的方法,但必须有更好的方法,对吧?

for(var in newdata_vars) {
  k <- pmax(dt_newdata[, (var), with = FALSE], dt_original[id %in% dt_newdata$id, (var), with = FALSE])
  dt_original[id %in% dt_newdata$id, (var) := k, with = FALSE]
}

似乎应该有一种使用连接语法的方法,可能是前缀i. 和/或.SD 或类似的东西,但我尝试过的任何方法都不足以保证在这里重复。

【问题讨论】:

  • 是的,您的 pmax 看起来很像 dt_original[dt_newdata, on=.(id), pmax(x.varB, i.varB)],唯一的问题是坚持将“varB”作为字符传递...可能可以通过将数据放入长格式而不是宽格式来解决。
  • 请使用set.seed 使此可重现
  • 已添加set.seed(0),谢谢。
  • varB 视为数百个变量名称的异构混合,它是dt_original 中变量的任意子集。这个例子可能会让它看起来有点太规则了。
  • 名称是否异类无关紧要。它们显然都是数字的(根据您对 pmax 的使用判断),这意味着它们可以在长格式数据集中的单个列中很好地协同工作。那是melt(dt_original, id="id")。从那里,您可能还希望将 var 名称拆分为组件部分......(变量和年份)。如果您对此感兴趣,我建议您查看 Hadley 的文章:jstatsoft.org/article/view/v059i10

标签: r data.table


【解决方案1】:

根据您的标准,此代码应以当前格式工作。

dt_original[dt_newdata, names(dt_newdata) := Map(pmax, mget(names(dt_newdata)), dt_newdata)]

它加入到data.tables之间匹配的ID,然后使用:=进行赋值因为我们要返回一个列表,所以我使用Map通过data.tables匹配的列运行pmax以 dt_newdata 的名称命名。请注意,dt_newdata 的所有名称都必须在 dt_original 数据中。

根据 Frank 的评论,您可以删除 Map 列表项的第一列和使用 [-1] 的列名称,因为它们是 ID,不需要计算。从Map 中删除第一列避免了pmax 的一次传递,并且还保留了id 上的密钥。感谢 @brian-stamper 指出 cmets 中的密钥保存。

dt_original[dt_newdata,
            names(dt_newdata)[-1] := Map(pmax,
                                         mget(names(dt_newdata)[-1]),
                                         dt_newdata[, .SD, .SDcols=-1])]

注意[-1]的使用假设ID变量位于new_data的第一个位置。如果它在其他地方,您可以手动更改索引或使用grep

【讨论】:

  • 是的,看起来不错。不知道如果你想要它在 all 列上,但是;可能需要排除id
  • 我想你可能在编辑之前就已经有了它 - 我正在尝试两种方式,[-1] 看起来pmax 正在尝试比较 dt_original 的所有行与dt_newdata.
  • 好的。我返回了原始代码,然后添加了忽略 id 列的工作代码。
  • 是的,就是这样,非常棒。有趣的附注 - 如果您不跳过 ID 列,这将消除它是键的事实,因此您必须在结果上再次setkey(如果您想要键控数据)。 [-1] 版本保留密钥。
猜你喜欢
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 2015-12-24
相关资源
最近更新 更多