【问题标题】:Using dynamic column names in `data.table`在 `data.table` 中使用动态列名
【发布时间】:2013-02-02 22:25:49
【问题描述】:

我想计算 data.table 中几列中每一列的平均值,并按另一列分组。我的问题与其他两个关于 SO(one 和 two)的问题类似,但我无法将这些问题应用于我的问题。

这是一个例子:

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

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    您应该使用.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 的更多信息。我发现这在这里是相关的。谢谢@大卫。

    编辑 2:假设您有一个 data.table,有 1000 行和 301 列(一列用于分组和 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 过滤到这些列,然后计算平均值(这又不一定是最佳解决方案,因为每次您想要对某些列进行操作时,您都必须创建一个额外的子集 data.table .

      dt.sub <- dt[, c(1, 251:300)]
      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 将仅与我们指定的这些列一起提供。

    希望这会有所帮助。

    【讨论】:

    • +1 在这种情况下是否需要.SDcols=2:4? .SD 已经排除了分组列。如果需要非分组列的子集,则只需要 .SDcols。
    • @MatthewDowle,谢谢。我已编辑以反映您的评论。
    • @Arun 你能解释一下 .SDcols 的语法吗,我找不到任何相关的文档
    • @DavidD,希望edit 2 有所帮助。对于其他人,如果某些内容不正确或可以改进以更好地理解,请随时编辑编辑。
    • +1 惊人的答案。我自己不知道.SDcols 选项,这绝对有帮助!
    猜你喜欢
    • 2012-07-29
    • 2016-02-02
    • 2019-05-19
    • 2020-04-13
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多