优雅地更新多个data.table列

时间:2016-06-09 08:36:24

标签: r data.table

我试图做一件简单的事情,按照他们的意思划分40列data.table。我无法提供实际数据(并非所有列都是数字,而且我有> 8M行),但这里有一个例子:

library(data.table)   

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

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

for (col in names(colmeans)) dt[,col:=dt[,col]/colmeans[col]]   

但是这会返回一个错误,因为dt[,col]要求不引用列名。使用as.name(col)并不会将其删除。 现在,

res <- t(t(dt[,1:40,with=F]/colmeans))

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

dt[,1:40] <- res

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

以下作品,但我觉得它很难看:

for (i in seq_along(colmeans)) dt[,i:=dt[,i,with=F]/colmeans[i],with=F]

当然,我也可以通过调用data.table()上的res和我的data.table所具有的其他非数字列重新创建一个新的data.table,但他们的效率并不高?

4 个答案:

答案 0 :(得分:25)

怎么样

dt[, (names(dt)) := lapply(.SD, function(x) x/mean(x))]

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

dt[, 1:40 := lapply(.SD, function(x) x/mean(x)), .SDcols = 1:40]

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

答案 1 :(得分:3)

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

for(j in names(dt)) {
 set(dt, i=NULL, j = j, value = dt[[j]]/mean(dt[[j]]))
}

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

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

数据

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

答案 2 :(得分:3)

dplyr 0.4.3

要按平均值划分所有列,您可以执行以下操作:

dplyr::mutate_each(dt, funs(. / mean(.)))

或者指定列位置:

dplyr::mutate_each(dt, funs(. / mean(.)), 5:10)

或列名:

dplyr::mutate_each_(dt, funs(. / mean(.)), colnames(dt)[5:10])

dplyr 0.4.3.9000

如果您只想划分数字列,则dplyr的devel版本具有mutate_if,该版本对谓词返回TRUE

的列进行操作
dplyr::mutate_if(dt, is.numeric, funs(. / mean(.)))

答案 3 :(得分:1)

一点meltdcast魔法怎么样?这会将数据转换为“长”格式,然后再转换回原始的“宽”格式。

首先,melt ID上的变量:

# make an ID variable
dt[, idvar := 1:nrow(dt)]
# melt the data on the ID variable
dt2 <- melt(dt, "idvar")

然后通过平均操作对每个组进行划分:

# use data.table by = to do a fast division by group mean
dt2[, divByMean := value / mean(value), by = variable]
dt2
## idvar variable value divByMean
## 1:     1       V1    15 0.2859867
## 2:     2       V1    92 1.7540515
## 3:     3       V1    27 0.5147760
## 4:     4       V1     7 0.1334604
## 5:     5       V1    18 0.3431840
## ---                               
## 3996:    96      V40    54 1.1111111
## 3997:    97      V40    51 1.0493827
## 3998:    98      V40    23 0.4732510
## 3999:    99      V40     8 0.1646091
## 4000:   100      V40    11 0.2263374

然后回到原来的宽幅格式:

# now dcast back to "wide"
dt3 <- dcast(dt2, idvar ~ variable, mean, value.var = "divByMean")
dt3[1:5, 1:5]
##   idvar        V1        V2        V3        V4
## 1     1 0.2859867 0.6913303 0.2110919 1.6156624
## 2     2 1.7540515 0.7847534 0.5948954 1.8817715
## 3     3 0.5147760 0.2615845 0.8827480 0.4181715
## 5     5 0.3431840 0.3550075 0.3646133 0.3231325
## 4     4 0.1334604 1.7937220 1.3241220 1.3685611