在data.table或dplyr中的列之间计算?

Big*_*hao 8 r dplyr data.table

我想用来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))
Run Code Online (Sandbox Code Playgroud)

在这里,我想计算每个ID,val1和val2的平均值.

另请注意,在每个ID中,都有不同的级别.但是对于每个唯一ID,我只想要一个包含不同级别val1和val2的意思.

--- ID | 意思 - -

- ID1 | ...

- ID2 | ...

- ID3 | ...

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

topagents <- DT[, mean = mean(list(val1,val2)), 
                    by = ID]
Run Code Online (Sandbox Code Playgroud)

但它不起作用.我知道怎么做reshape2,先是melt然后dcast.

但原始数据集相对较大,有20M行和12个字段,进行计算需要相当长的时间.

所以我更喜欢使用data.tabledplyr.

Mat*_*erg 7

将调用封装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
Run Code Online (Sandbox Code Playgroud)

要获得单个值,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
Run Code Online (Sandbox Code Playgroud)

使用j此处单个元素的列表是命名结果列的简单方法.

  • 对于答案的第一部分,在某些情况下使用`.SDcols`可能更优雅,如`DT [,lapply(.SD,mean),by = ID,.SDcols = c("val1", "val2的")]` (2认同)

Jer*_*myS 5

topagents <- DT[, mean(c(val1,val2)), by = ID]
Run Code Online (Sandbox Code Playgroud)

意思是只能拿一个矢量,它不理解一个列表.

你的问题是"计算每个ID的val1和val2的平均值." 但根据Mathew的回答,你可能想要"为每个ID计算val1和val2的平均值(复数)."?


Aru*_*run 5

您提到您的数据维度是包含12列的2000万行,但未提及"ID"的唯一值的数量.我将在这里假设为20,000.

如果你正在寻找您的解决方案既1)快速和2)内存效率,那么马修(或杰里米的)解决方案,以拼写出所有的变量将会有更好的表现-也就是说,直到unlist(.SD)进行了优化.基本上最好的是@ codoremifa的语法和@Matthew的表现.

这篇文章的目的是为了说明性能提升一个可以通过具有setkeydata.table(这种巨大的尺寸)前聚集(在哪些方面在写作的时候还没有被覆盖的答案).

setkey通常使用,因为它是(joinfast subset基于二进制搜索)是必需的.但是对于像你这样的数据维度(可以说是安全的BIG数据),你可以通过设置密钥来获得A LOT.这是因为,setkey按键列对数据进行排序,这样可以在以后聚合列位于连续的内存位置,因此非常有效.

v1.8.11中有很多增强功能(当前的开发版本setkey也增加了很多).因此,此处显示的基准将与CRAN上当前的稳定版本1.8.10不同.如果您没有使用开发版本,那也没关系.希望这能让你相信它的实用性,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
Run Code Online (Sandbox Code Playgroud)

没有设置键:

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
Run Code Online (Sandbox Code Playgroud)

通过设置键:

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
Run Code Online (Sandbox Code Playgroud)

你可以看到,通过设置键,你需要大约8.4秒,而没有它的时间大于40秒.这是很多加速.


The*_*Man 3

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

colstomean <- setdiff(colnames(DT),c('ID','level'))
Run Code Online (Sandbox Code Playgroud)

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

DT[, lapply(.SD, mean, na.rm=TRUE), 
   by=ID, 
   .SDcols = colstomean
   ]
Run Code Online (Sandbox Code Playgroud)

输出 -

    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
Run Code Online (Sandbox Code Playgroud)

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

DT[, mean(unlist(.SD), na.rm = TRUE), 
    by=ID, 
   .SDcols = colstomean
   ]
Run Code Online (Sandbox Code Playgroud)

输出

    ID        V1
1: ID1 0.9382404
2: ID2 1.4725758
3: ID3 2.7133682
4: ID4 3.5614724
5: ID5 4.7096230
Run Code Online (Sandbox Code Playgroud)