【问题标题】:updating data.table in a for loop and with a grouping variable在 for 循环中更新 data.table 并使用分组变量
【发布时间】:2026-01-14 04:50:01
【问题描述】:

我想在 for 循环中更新 data.table 中的某些列。列名是动态设置的。不幸的是,我使用的解决方案非常慢,因此不可行。我已经尝试了我在这个主题上找到的所有东西,但似乎没有什么能让它变得更快。

我的数据集有 682446 行。似乎让它如此缓慢的是动态列名。当我使用静态版本时,它工作得非常好,而且速度很快:

dt[education>0, educationNewVersion:=education]

当i,j(LHS,RHS)中的变量名是动态的时候就会出现问题:

varName = 'education'
newVarName <- paste0(varName, 'NewVersion')

# This is much slower as the static version from above, takes almost half a minute.
dt[dt[[varName]]>0, (newVarName):=get(varName)]
# This is very slow too
dt[dt[[varName]]>0, (newVarName):=eval(varName)]
# This one throws an error (see below)
dt[dt[[varName]]>0, (newVarName):=dt[[varName]]]

这是第三个表达式抛出的错误: “提供了 682446 项分配给列 'gerSpeakImp' 的 76451 项。如果您希望 '回收' RHS,请使用 rep() 向您的代码读者明确这一意图。”

请不要建议我使用 set()。 set() 不支持分组,我需要分组(我只是没有在此处包含以保持示例简单)。我不知道还能做什么。目前,我正在使用自己的语句更新每一列,这确实是非常多余的,当我从 Stata 迁移到 R(和 data.table)时,这不是我所期望的。

【问题讨论】:

  • 你试过.SDcolsdt[, (newVarName) := .SD[[1]], .SDcols = varName]

标签: r data.table


【解决方案1】:

使用.I.SDcols 可能会更好。

dt[dt[, .I[.SD[[1]] > 0], .SDcols = varName], (newVarName) := .SD[[1]], 
       .SDcols = varName]

在第三个表达式中,发生错误是因为它试图从长度不同的整个数据集中对列进行子集化。相反,我们可以使用.SD

dt[dt[[varName]]>0, (newVarName):= .SD[[varName]]]

基准测试

set.seed(24)
dt <- data.table(education = sample(0:50, 682446, replace = TRUE))
dt1 <- copy(dt)

varName <- 'education'
newVarName <- paste0(varName, 'NewVersion')

system.time(dt[dt[[varName]]>0, (newVarName):= .SD[[varName]]])
#   user  system elapsed 
#  0.022   0.003   0.026 

system.time(  dt1[dt1[, .I[.SD[[1]] > 0], .SDcols = varName],
     (newVarName) := .SD[[1]], 
            .SDcols = varName])
#   user  system elapsed 
#  0.023   0.003   0.024 

【讨论】:

  • @MaikSchürmann 可能是系统规格不同。我使用 OSX 32 GB memory2.3 GHz
  • 这很奇怪,我已经尝试了您提出的所有三个版本,它们的速度非常不同:system.time(dt[dt[[varName]]&gt;0,(newVarName):=.SD[[varName]]]) # user system elapsed # 3.627 16.228 34.008 system.time(dt[dt[[varName]]&gt;0, (newVarName):=.SD[[1]], .SDcols = varName]) #user system elapsed #0.009 0.004 0.012 system.time(dt[dt[, .I[.SD[[1]] &gt; 0], .SDcols=varName], (newVarName):=.SD[[1]], .SDcols=varName]) #user system elapsed #0.012 0.004 0.021 OSX with 2,4 GHz DualCore Intel Core i5 & 8GB跨度>
  • @MaikSchürmann 我看到你的内存是 8GB。我用了 32GB。而且,我创建的数据只有一列。如果您有很多列,那么它也可能会产生一些影响
  • 这也是我的猜测。感谢您的宝贵时间!