如何加快按组转换data.frame?

时间:2013-01-31 13:38:02

标签: r dataframe

我的data.frame具有相同长度的组(id

id  |  amount 
--------------
 A  |   10   
 A  |   54   
 A  |   23   
 B  |   34   
 B  |   76    
 B  |   12    

我想将按组 id转换为:

 id |
----------------------
 A  | 10  |  54 | 23  
 B  | 34  |  76 | 12

最有效的方法是什么?

我之前使用的是reshapedcast,但确实非常慢! (我有很多数据,并希望加快这个瓶颈)

有更好的策略吗?使用data.table或矩阵?任何帮助将不胜感激!

# Little data.frame
df <- data.frame(id=c(2,2,2,5,5,5), amount=as.integer(c(10,54,23,34,76,12)))

# Not so little data.frame
set.seed(10)
df <- data.frame(id = rep(sample(1:10000, 10000, replace=F),100), amount=as.integer(floor(runif(1000000, -100000,100000))))

# Create time variable
df$time <- ave(as.numeric(df$id), df$id, FUN = seq_along)

# The base R reshape strategy
system.time(df.reshape <-reshape(df, direction = "wide", idvar="id", timevar="time"))
user  system elapsed 
6.36    0.31    6.69 

# The reshape2 dcast strategy
require(reshape2)
a <- system.time(mm <- melt(df,id.vars=c('id','time'),measure.vars=c('amount')))
b <- system.time(df.dcast <- dcast(mm,id~variable+time,fun.aggregate=mean))
a+b
user  system elapsed 
14.44    0.00   14.45 

更新 使用每个组长度相等的事实,您可以使用matrix - 函数。

df.matrix <- data.frame(id=unique(df$id), matrix(df$amount, nrow=(length(unique(df$id))), byrow=T))
user  system elapsed 
0.03    0.00    0.03 

注意:此方法假定data.frame由id预先排序。

3 个答案:

答案 0 :(得分:2)

矩阵方法将使用:

  system.time({ df.reshape <-matrix(df$amount, nrow=10000, byrow=TRUE); 
               rownames(df.reshape)<- df$id[1:10000]
             } )
   user  system elapsed 
  0.010   0.006   0.016 

答案 1 :(得分:1)

试试这个:

 dFrame<-data.frame(id = c(rep("A",3),rep("B",3)),amount = c(10,54,23,34,76,12))
 newFrame<-cbind(data.frame(id = unique(dFrame$id)),matrix(as.numeric(unlist(tapply(dFrame$amount,dFrame$id,identity))),nrow=length(unique(dFrame$id)),byrow=T))

包围可能会关闭,我试着小心 - 我目前没有可用的R口译员

基于您提供的df示例代码的基准测试结果:

  replications elapsed relative user.self sys.self user.child sys.child
   1            1   4.193        1     4.056    0.064          0         0

答案 2 :(得分:1)

这不是reshape的问题。来自基地的aggregate应该能够处理这个问题。

df.out <- aggregate(amount ~ id, data=df, c)
# running on the small data
#   id amount.1 amount.2 amount.3
# 1  2       10       54       23
# 2  5       34       76       12

这不是你想要的吗?


好吧,似乎DWin解决方案的改编版本是最快的。但是,结果将按id排序。如果你不想那样,那么Aditya似乎就是那个用的。

以下是功能和基准测试结果:

  • 使用aggregate

    AGG <- function() {
        df.agg <- aggregate(amount ~ id, data=df, c)
    }
    
  • 使用Aditya

    SEC <- function() {
        df.sec <- cbind(data.frame(id = unique(df$id)), 
                matrix(as.numeric(unlist(tapply(df$amount, df$id, identity))), 
                nrow = length(unique(df$id)), byrow = T))
    }
    
  • 使用Dwin的修改版本:

    DWIN_M <- function() {
        df1 <- df[with(df, order(id)), ]
        idx <- df$id[!duplicated(df$id)]
        df.dwin <- cbind(data.frame(id=idx), 
                    as.data.frame(matrix(df1$amount, 
                    nrow=length(idx), byrow=TRUE)))
    }
    
  • 基准:

    require(rbenchmark)
    benchmark(AGG(), SEC(), DWIN_M(), replications=3, order="elapsed")
    
    #      test replications elapsed relative user.self sys.self user.child sys.child
    # 3 DWIN_M()            3   4.175    1.000     4.148    0.000          0         0
    # 2    SEC()            3  17.568    4.208    17.449    0.016          0         0
    # 1    AGG()            3  24.529    5.875    24.306    0.044          0         0
    

如果我犯了任何错误,请告诉我。