9

我想用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 的意思。

--- 身份证 | 意思是 - -

-- ID1 | ...

-- ID2 | ...

-- ID3 | ...

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

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

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

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

所以我更喜欢使用data.tableor dplyr

4

4 回答 4

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

要获得单个值,即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是命名结果列的一种简单方法。

于 2014-01-15T04:37:54.207 回答
5
topagents <- DT[, mean(c(val1,val2)), by = ID]

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

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

于 2014-01-15T04:39:52.620 回答
5

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

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

这篇文章的目的是说明在聚合之前(在如此巨大的维度setkeydata.table)可以获得的性能增益(在撰写本文时,答案尚未涵盖该方面)。

setkey通常使用它,因为它是joinfast subset(基于二分搜索)是必需的。但是在诸如您的数据维度(可以肯定地说,大数据)上,您可以通过设置密钥受益很多。这是因为,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

不设置键:

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 秒。这是一个很大的加速。

于 2014-01-16T23:57:28.997 回答
3

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

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
于 2014-01-15T05:01:45.287 回答