我有一段聚合代码可以运行得很好,但对10e6行的数据帧运行有点慢。我不是那种经验丰富的R,所以为我畏缩的代码道歉!
我只想做一个基本的汇总和公共密钥值的总和......
例如从...开始
key val
1 a 5
2 b 7
3 a 6
为...
key val
1 a 11
2 b 7
我能管理的最好的是......
keys = unique(inp$key)
vals = sapply(keys, function(x) { sum(inp[inp$key==x,]$val) })
out = data.frame(key=keys, val=vals)
我有这种直觉,inp[inp$key==x,]
并不是最好的方法。是否有明显的加速我错过了?我可以在Hadoop中做到这一点(因为10e6数据集实际上已经是来自2e9行数据集的汇总)但我正在努力改进我的R。
干杯, 垫
答案 0 :(得分:8)
您可以使用aggregate
:
> d
key val
1 a 5
2 b 7
3 a 6
> aggregate(val~key, sum, data=d)
key val
1 a 11
2 b 7
你也可以使用hadley的plyr包中的ddply
:
> ddply(d, .(key), summarize, val=sum(val))
key val
1 a 11
2 b 7
答案 1 :(得分:5)
使用tapply
的另一个选项:
dat <- data.frame(key = c('a', 'b', 'a'), val = c(5,7,6))
> with(dat, tapply(val, key, FUN = sum))
a b
11 7
我的测试表明这是这项特殊练习的最快方法,显然你的里程可能会有所不同:
fn.tapply <- function(daters) with(daters, tapply(val, key, FUN = sum))
fn.aggregate <- function(daters) aggregate(val~key, sum, data = daters)
fn.ddply <- function(daters) ddply(daters, .(key), summarize, val = sum(val))
library(rbenchmark)
benchmark(fn.tapply(dat), fn.aggregate(dat), fn.ddply(dat)
, columns = c("test", "elapsed", "relative")
, order = "relative"
, replications = 100
)
test elapsed relative
1 fn.tapply(dat) 0.03 1.000000
2 fn.aggregate(dat) 0.20 6.666667
3 fn.ddply(dat) 0.30 10.000000
请注意,将tapply
解决方案转换为data.frame会将这一差异减少约40%,以便与前两个进行真正的苹果对比。
使用评论中指示的1M行数据集似乎改变了一点:
dat2 <- data.frame(key = rep(letters[1:5], each = 200000), val = runif(1e6))
> benchmark(fn.tapply(dat2), fn.aggregate(dat2), fn.ddply(dat2)
+ , columns = c("test", "elapsed", "relative")
+ , order = "relative"
+ , replications = 100
+ )
test elapsed relative
1 fn.tapply(dat2) 39.114 1.000000
3 fn.ddply(dat2) 62.178 1.589661
2 fn.aggregate(dat2) 157.463 4.025745
答案 2 :(得分:4)
使用sapply
和split
是另一种选择。我将通过@ Chase的优秀答案扩展数据和基准。
fn.tapply <- function(daters) with(daters, tapply(val, key, FUN = sum))
fn.split <- function(daters) with(daters, sapply(split(val, key), sum))
str(dat)
# 'data.frame': 1000000 obs. of 2 variables:
# $ key: Factor w/ 5 levels "a","b","c","d",..: 1 1 1 1 1 1 1 1 1 1 ...
# $ val: num 0.186 0.875 0.42 0.294 0.878 ...
benchmark(fn.tapply(dat), fn.split(dat)
, columns = c("test", "elapsed", "relative")
, order = "relative"
, replications = 100
)
# test elapsed relative
# 2 fn.split(dat) 4.106 1.00000
# 1 fn.tapply(dat) 69.982 17.04384
答案 3 :(得分:4)
从技术上讲,你说“数据框架”,但另一个更快的选择(超过22倍)是使用data.table
包。
这是10个10e6行代表的基准。
library(rbenchmark)
library(plyr)
library(data.table)
key <- sample(letters,10e6, replace = T)
val <- sample.int(10,10e6, replace = T)
big_df <- data.frame(key,val)
rm(key)
rm(val)
big_dtable <- data.table(big_df)
setkey(big_dtable,key)
fn.data_table <- function(mydata) mydata[,list(sum = sum(val)), by = 'key']
fn.tapply <- function(mydata) with(mydata, tapply(val, key, FUN = sum))
fn.aggregate <- function(mydata) aggregate(val~key, sum, data = mydata)
fn.ddply <- function(mydata) ddply(mydata, .(key), summarize, val = sum(val))
现在是基准......
benchmark(fn.data_table(big_dtable)
, fn.tapply(big_df)
, fn.aggregate(big_df)
, fn.ddply(big_df)
, fn.ddply(big_dtable)
, columns = c("test","elapsed","relative")
, order = "relative"
, replications = 10
)
结果......
test elapsed relative
1 fn.data_table(big_dtable) 1.98 1.00000
5 fn.ddply(big_dtable) 37.59 18.98485
4 fn.ddply(big_df) 44.36 22.40404
2 fn.tapply(big_df) 51.03 25.77273
3 fn.aggregate(big_df) 238.52 120.46465