在`data.table`中使用动态列名

时间:2013-02-18 13:11:15

标签: r data.table

我想计算data.table中每个列的平均值,按另一列分组。我的问题类似于关于SO的其他两个问题(onetwo),但我不能将这些问题应用于我的问题。

以下是一个例子:

library(data.table)
dtb <- fread(input = "condition,var1,var2,var3
      one,100,1000,10000
      one,101,1001,10001
      one,102,1002,10002
      two,103,1003,10003
      two,104,1004,10004
      two,105,1005,10005
      three,106,1006,10006
      three,107,1007,10007
      three,108,1008,10008
      four,109,1009,10009
      four,110,1010,10010")

dtb
#    condition var1 var2  var3
# 1:       one  100 1000 10000
# 2:       one  101 1001 10001
# 3:       one  102 1002 10002
# 4:       two  103 1003 10003
# 5:       two  104 1004 10004
# 6:       two  105 1005 10005
# 7:     three  106 1006 10006
# 8:     three  107 1007 10007
# 9:     three  108 1008 10008
# 10:     four  109 1009 10009
# 11:     four  110 1010 10010

每个单一均值的计算很容易;例如对于“var1”:dtb[ , mean(var1), by = condition]。但是如果有很多变量并且你需要编写所有变量,我很快就会变得很麻烦。因此,dtb[, list(mean(var1), mean(var2), mean(var3)), by = condition]是不合需要的。我需要列名称是动态的,我希望最终得到类似的东西:

   condition  var1   var2    var3
1:       one 101.0 1001.0 10001.0
2:       two 104.0 1004.0 10004.0
3:     three 107.0 1007.0 10007.0
4:      four 109.5 1009.5 10009.5

1 个答案:

答案 0 :(得分:44)

您应该使用.SDcols(特别是如果列太多而您需要仅对列的子集执行特定操作(除了分组变量列之外)。

dtb[, lapply(.SD, mean), by=condition, .SDcols=2:4]

#    condition  var1   var2    var3
# 1:       one 101.0 1001.0 10001.0
# 2:       two 104.0 1004.0 10004.0
# 3:     three 107.0 1007.0 10007.0
# 4:      four 109.5 1009.5 10009.5

您还可以在变量中获取您想要的所有列名称,然后将其传递给.SDcols,如下所示:

keys <- setdiff(names(dtb), "condition")
# keys = var1, var2, var3
dtb[, lapply(.SD, mean), by=condition, .SDcols=keys]

编辑正如Matthew Dowle正确指出的那样,因为在condition分组之后,您需要在每个其他列上计算平均值,您可以这样做:

dtb[, lapply(.SD, mean), by=condition]

David的编辑(被拒绝):从this post了解.SD的更多信息。我发现这与此相关。谢谢@David。

编辑2 :假设您有一个包含1000行和301列的data.table(一列用于分组和300个数字列):

require(data.table)
set.seed(45)
dt <- data.table(grp = sample(letters[1:15], 1000, replace=T))
m  <- matrix(rnorm(300*1000), ncol=300)
dt <- cbind(dt, m)
setkey(dt, "grp")

你想找到列的平均值,比如说,单独的251:300,

  • 您可以计算所有列的平均值,然后对这些列进行子集(这不是非常有效,因为您将对整个数据进行计算)。

    dt.out <- dt[, lapply(.SD, mean), by=grp]
    dim(dt.out) # 15 * 301, not efficient.
    
  • 您可以先将data.table过滤到这些列,然后计算平均值(这也不一定是最佳解决方案,因为每次你必须创建一个额外的子集数据.table想要对某些列进行操作。

    dt.sub <- dt[, c(1, 251:300), with=FALSE]
    setkey(dt.sub, "grp")
    dt.out <- dt.sub[, lapply(.SD, mean), by=grp]
    
  • 您可以像往常一样逐个指定每个列(但这对于较小的data.tables来说是理想的)

    # if you just need one or few columns
    dt.out <- dt[, list(m.v251 = mean(V251)), by = grp]
    

那么什么是最好的解决方案?答案是 .SDcols

  

正如文档所述,对于 data.table x .SDcols 指定 .SD 中包含的列。

这基本上隐含地过滤了将传递给.SD而不是创建子集的列(如前所述),只是它非常高效且快速!

我们怎么做?

  • 通过指定列号:

    dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = 251:300]
    dim(dt.out) # 15 * 51 (what we expect)
    
  • 或者通过指定列ID:

    ids <- paste0("V", 251:300) # get column ids
    dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = ids]
    dim(dt.out) # 15 * 51 (what we expect)
    

它接受列名和数字作为参数。在这两种情况下,.SD仅与我们指定的列一起提供。

希望这有帮助。