优雅地更新多个data.table列

jea*_*ain 10 r data.table

我正在尝试做一个简单的事情,按照它们的平均值划分40列data.table.我无法提供实际数据(并非所有列都是数字,我有> 8M行),但这是一个例子:

library(data.table)   

dt <- data.table(matrix(sample(1:100,4000,T),ncol=40))
colmeans <- colMeans(dt)
Run Code Online (Sandbox Code Playgroud)

接下来我以为我会这样做:

for (col in names(colmeans)) dt[,col:=dt[,col]/colmeans[col]]   
Run Code Online (Sandbox Code Playgroud)

但是这会返回一个错误,因为dt[,col]要求不引用列名.使用as.name(col)不削减它.现在,

res <- t(t(dt[,1:40,with=F]/colmeans))
Run Code Online (Sandbox Code Playgroud)

包含扩展结果,但我无法将其插回到data.table中,如

dt[,1:40] <- res
Run Code Online (Sandbox Code Playgroud)

不起作用,也不起作用dt[,1:40:=res, with=F].

以下作品,但我发现它很难看:

for (i in seq_along(colmeans)) dt[,i:=dt[,i,with=F]/colmeans[i],with=F]
Run Code Online (Sandbox Code Playgroud)

当然,我也可以重新通过调用一个新的data.table data.table()res和其他非数值列我data.table有,但不是自己的东西更有效率?

tal*_*lat 26

怎么样

dt[, (names(dt)) := lapply(.SD, function(x) x/mean(x))]
Run Code Online (Sandbox Code Playgroud)

如果需要指定某些列,可以使用

dt[, 1:40 := lapply(.SD, function(x) x/mean(x)), .SDcols = 1:40]
Run Code Online (Sandbox Code Playgroud)

要么

cols <- names(dt)[c(1,5,10)]
dt[, (cols) := lapply(.SD, function(x) x/mean(x)), .SDcols = cols]
Run Code Online (Sandbox Code Playgroud)

  • `colMeans`复制整个data.table并将其转换为矩阵.对于相对较大的数据,您希望避免此类副本.此外,如果您有许多列,您可能有一个"宽格式"表,并应考虑它是否应该是"长格式".data.table比几列和几行更有效,只有很少的列和很多行. (4认同)
  • @jeanlain我不这么认为. (3认同)

akr*_*run 5

我们也可以使用set. [.data.table在这种情况下,使用with应该没有明显的区别:=,但在[.data.table必须多次调用的情况下,使用set()有助于避免这种开销并且可以明显更快。

for(j in names(dt)) {
 set(dt, i=NULL, j = j, value = dt[[j]]/mean(dt[[j]]))
}
Run Code Online (Sandbox Code Playgroud)

也可以在选定的列上完成,即

nm1 <- names(dt)[1:5]
for(j in nm1){
 set(dt, i = NULL, j = j, value = dt[[j]]/mean(dt[[j]]))
}
Run Code Online (Sandbox Code Playgroud)

数据

set.seed(24)
dt <- as.data.frame(matrix(sample(1:100,4000,TRUE),ncol=40))
setDT(dt)
Run Code Online (Sandbox Code Playgroud)

  • `for( ) set( )` 构造是一个非常好的解决方案,并且避免了讨厌的 `.SD` 分配 (2认同)