【问题标题】:Calculate between columns in data.table or dplyr?在 data.table 或 dplyr 中的列之间计算?
【发布时间】:2014-02-03 10:23:11
【问题描述】:

我想使用data.table 为大型数据集实现一个非常简单的任务。

计算每个 ID 的 val1 和 val2 的平均值。

详情请参考附件假数据。

library(data.table)
DT <- data.table(ID = paste0("ID",rep(1:5,each=2)),
      level= rep(c("CTRL","CTRL","ID1","ID2","ID3"),2),
      val1 = 1:10, 
      val2 = rnorm(10))

这里我要计算每个 ID,val1 和 val2 的平均值。

还要注意,在每个 ID 中,都有不同的级别。但对于每个唯一 ID,我只想要一个意味着合并不同级别 val1 和 val2。

--- ID |意思---

-- ID1 | ...

-- ID2 | ...

-- ID3 | ...

我尝试了以下代码,但它不起作用。

topagents <- DT[, mean = mean(list(val1,val2)), 
                    by = ID]

但它不起作用。 我知道如何在reshape2 中进行操作,首先是melt,然后是dcast

但是原始数据集比较大,20M行12个字段,计算时间比较长。

所以我更喜欢使用data.tabledplyr

【问题讨论】:

    标签: r data.table dplyr


    【解决方案1】:

    在列表中封装对mean 的调用,而不是取列表的平均值,这是您无法做到的:

    DT[, j=list(val1=mean(val1), val2=mean(val2)), by=ID]
        ID val1       val2
    1: ID1  1.5  0.1389794
    2: ID2  3.5  0.3392179
    3: ID3  5.5 -0.6336174
    4: ID4  7.5  0.9941148
    5: ID5  9.5  0.1324782
    

    要获得单个值,val1val2 值的平均值,将它们组合并传递给 mean

    DT[, j=list(mean=mean(c(val1,val2))), by=ID]
        ID      mean
    1: ID1 0.8194897
    2: ID2 1.9196090
    3: ID3 2.4331913
    4: ID4 4.2470574
    5: ID5 4.8162391
    

    在这里为j 的单个元素使用列表是命名结果列的一种简单方法。

    【讨论】:

    • 谢谢马修!真是太棒了!
    • 对于答案的第一部分,在某些情况下使用.SDcols 可能更优雅,例如DT[,lapply(.SD,mean),by=ID,.SDcols=c("val1","val2")]
    【解决方案2】:
    topagents <- DT[, mean(c(val1,val2)), by = ID]
    

    mean 只能取向量,不理解列表。

    您的问题是“计算每个 ID 的 val1 和 val2 的平均值”。但根据 Mathew 的回答,您可能想要“计算每个 ID 的 val1 和 val2 的平均值(复数)。”?

    【讨论】:

    • 嗨,杰里米,感谢您的回答!
    【解决方案3】:

    您提到您的数据维度是 2000 万行和 12 列,但没有提到“ID”的唯一值的数量。我在这里假设为 20,000。

    如果您正在寻找 1) 快速和 2) 节省内存的解决方案,那么 Matthew(或 Jeremy)的解决方案说明所有变量会表现得更好 - 也就是说, 直到 unlist(.SD) 被优化。基本上最好的是@codoremifa 的语法与@Matthew 的性能。

    这篇文章的目的是说明在聚合之前setkeydata.table(如此巨大的维度)上可能获得的性能提升(其中的方面尚未包含在写作时间)。

    setkey 通常被使用,因为joinfast subset 需要它(基于二分搜索)是的。但是在诸如您的数据维度(可以肯定地说,大数据)上,您可以通过设置密钥受益很多。这是因为,setkey 按您的键列对数据进行排序,这允许稍后将列聚合到连续的内存位置,因此非常有效。

    v1.8.11 有很多增强功能(当前的开发版本,setkey 也得到了很多更快)。因此,此处显示的基准会因current stable version 1.8.10 on CRAN 而异。如果你不使用开发版也没关系。希望这能让您相信setkey 的用处,并为您提供一些关于下一个版本的预期内容。


    好的,继续说明您的尺寸数据:

    获取一些数据:

    require(data.table)
    set.seed(1L)
    uval <- 2e4 # unique values in ID
    N <- 20e6
    DT <- data.table(ID=sample(uval, N, TRUE)) # for simplicity ID is integer
    cols <- paste("V", 1:11, sep="")
    set(DT, i=NULL, j=cols, value=as.list(1:11))
    dim(DT) # 20e6 by 12
    

    无设置键:

    system.time(ans1 <- DT[, 
        list(val=mean(c(V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11))), 
    by=ID])
    #   user  system elapsed
    # 45.587   0.632  46.251
    

    通过设置键:

    system.time(setkey(DT, ID)) # (note that this'll be much faster on 1.8.11)
    #   user  system elapsed
    #  5.872   0.072   5.948
    
    system.time(ans2 <- DT[, 
        list(val=mean(c(V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11))), 
    by=ID])
    #   user  system elapsed
    #  2.164   0.236   2.400
    
    setkey(ans1, ID)
    identical(ans1, ans2) # [1] TRUE
    

    您会看到,通过设置密钥,您需要大约 8.4 秒,而没有它的情况是 > 40 秒。这是一个很大的加速。

    【讨论】:

      【解决方案4】:

      如果我理解正确,您有十二个字段并且希望将硬编码保持在最低限度。我不太确定您的预期输出是什么,但希望它是以下两个结果之一 -

      colstomean <- setdiff(colnames(DT),c('ID','level'))
      

      选项1,该ID内每个变量值的平均值

      DT[, lapply(.SD, mean, na.rm=TRUE), 
         by=ID, 
         .SDcols = colstomean
         ]
      

      输出 -

          ID val1        val2
      1: ID1  1.5  0.37648090
      2: ID2  3.5 -0.55484848
      3: ID3  5.5 -0.07326365
      4: ID4  7.5 -0.37705525
      5: ID5  9.5 -0.08075406
      

      选项 2,该 ID 内所有变量值的平均值

      DT[, mean(unlist(.SD), na.rm = TRUE), 
          by=ID, 
         .SDcols = colstomean
         ]
      

      输出

          ID        V1
      1: ID1 0.9382404
      2: ID2 1.4725758
      3: ID3 2.7133682
      4: ID4 3.5614724
      5: ID5 4.7096230
      

      【讨论】:

      • 哇,真的很酷!我想要的只是选项2!但是我没有理解 .SD 和 .SDcols = colstomean 的含义。你能提供更多解释吗?
      • 在 data.table 上查找帮助条目。 .SD 在那里得到了很好的解释。否则 - stackoverflow.com/questions/14937165/…
      猜你喜欢
      • 2016-06-03
      • 1970-01-01
      • 2015-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-05
      • 2017-12-24
      相关资源
      最近更新 更多