基于密钥在数据框中聚合值

mat*_*cey 9 r idiomatic

我有一段聚合代码,运行得很好,但对10e6行的数据帧运行有点慢.我不是那种经验丰富的R,所以为我那令人畏缩的代码道歉!

我只是想做一个基本的汇总和共同键的值的总和...

例如从...来

  key val
1   a   5
2   b   7
3   a   6
Run Code Online (Sandbox Code Playgroud)

至...

  key val
1   a   11
2   b   7
Run Code Online (Sandbox Code Playgroud)

我能管理的最好的是......

keys = unique(inp$key)
vals = sapply(keys, function(x) { sum(inp[inp$key==x,]$val) })
out = data.frame(key=keys, val=vals)
Run Code Online (Sandbox Code Playgroud)

我有这种直觉,认为这inp[inp$key==x,]不是最好的方法.是否有明显的加速我错过了?我可以在Hadoop中做到这一点(因为10e6数据集实际上已经是来自2e9行数据集的汇总)但我正在尝试改进我的R.

干杯,垫子

koh*_*ske 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
Run Code Online (Sandbox Code Playgroud)

你也可以使用ddplyhadley的plyr包:

> ddply(d, .(key), summarize, val=sum(val))
  key val
1   a  11
2   b   7
Run Code Online (Sandbox Code Playgroud)


Cha*_*ase 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
Run Code Online (Sandbox Code Playgroud)

我的测试表明这是这项特殊练习的最快方法,显然你的里程可能会有所不同:

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

请注意,将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
Run Code Online (Sandbox Code Playgroud)