【问题标题】:Elegant way to solve ddply task with aggregate (hoping for better performance)用聚合解决 ddply 任务的优雅方法(希望有更好的性能)
【发布时间】:2011-12-13 11:00:21
【问题描述】:

我想通过名为ensg 的标识符变量聚合data.frame。数据框如下所示:

  chromosome probeset               ensg symbol    XXA_00    XXA_36    XXB_00
1          X  4938842 ENSMUSG00000000003   Pbsn  4.796123  4.737717  5.326664

我想计算具有相同ensg 值的行上每个数字列的平均值。这里的问题是我想保留其他身份变量染色体和符号不变,因为它们对于相同的ensg 也是相同的。

最后,我想要一个 data.frame 与标识列 chromosomeensgsymbol 以及具有相同标识符的行上的数字列的平均值。我在ddply 中实现了这个,但是与aggregate 相比它非常慢:

spec.mean <- function(eset.piece)
  {
    cbind(eset.piece[1,-numeric.columns],t(colMeans(eset.piece[,numeric.columns])))
  }
t
mean.eset <- ddply(eset.consensus.grand,.(ensg),spec.mean,.progress="tk")

我的第一个聚合实现如下所示,

mean.eset=aggregate(eset[,numeric.columns], by=list(eset$ensg), FUN=mean, na.rm=TRUE);

而且速度更快。但是aggregate 的问题是我必须重新附加描述变量。我还没有弄清楚如何将我的自定义函数与aggregate 一起使用,因为aggregate 不传递数据帧而只传递向量。

aggregate 有没有优雅的方法来做到这一点?或者有没有更快的方法来使用ddply

【问题讨论】:

    标签: r aggregate plyr


    【解决方案1】:

    如果速度是首要考虑因素,您应该查看data.table 包。当行数或分组列数很大时,data.table 似乎真的很闪耀。该软件包的 wiki 是 here,并且有几个指向其他优秀介绍性文档的链接。

    以下是使用 data.table() 进行聚合的方法

    library(data.table)
    #Turn the data.frame above into a data.table
    dt <- data.table(df)
    #Aggregation
    
      dt[, list(XXA_00 = .Internal(mean(XXA_00)),
              XXA_36 = .Internal(mean(XXA_36)),
              XXB_00 = .Internal(mean(XXB_00))),
        by = c("ensg", "chromosome", "symbol")
       ]
    

    给我们

         ensg chromosome symbol      XXA_00      XXA_36    XXB_00
    [1,]   E1          A     S1  0.18026869  0.13118997 0.6558433
    [2,]   E2          B     S2 -0.48830539  0.24235537 0.5971377
    [3,]   E3          C     S3 -0.04786984 -0.03139901 0.5618208
    

    通过比较rbenchmark 包的输出,在使用 30 行 data.frame 时,上面提供的聚合解决方案似乎效果很好。但是,当 data.frame 包含 3e5 行时,data.table() 显然是赢家。这是输出:

     benchmark(fag(), fdt(), replications = 10)
       test replications elapsed relative user.self sys.self 
    1 fag()           10   12.71 23.98113     12.40     0.31     
    2 fdt()           10    0.53  1.00000      0.48     0.05         
    

    【讨论】:

    • 不喜欢鼓励用户通过.Internal 以交互方式调用函数,我怀疑它在这种情况下会产生多大的不同。
    • @JoshuaUlrich - 我从来没有被.Internal 咬过,所以不能这么说。 data.table 的 wiki 页面显示,在他们的测试用例中,.Internal(mean()) 的速度比 mean() 提高了近 10 倍。这就是我养成那个(可能是坏的)习惯的地方。我将使用标准 mean() 更新测试,看看实际发生了什么。
    • 您可以使用mean.default 而不是mean 来避免方法分派,这样可以节省您一点时间。我不介意将.Internal 显示为选项,只要它后面带有“这里有龙”警告。
    • @JoshuaUlrich -- 使用mean.default 是一个不错的建议。我刚刚用 wiki 页面 (rwiki.sciviews.org/…) 上的示例进行了尝试,mean.default() 相对于mean() 购买了 4 倍的加速。 (.Internal(mean()) 实现了 8 倍的加速。)
    • 这个可以做一个编辑。无需再担心.Internal,因为@JoshuaUlrich 说的很对。从几个版本开始,mean 现在会自动进行优化。它应该是最后添加的编辑,还是只是更改答案并再次进行基准测试?不确定。
    【解决方案2】:

    首先让我们定义一个玩具示例:

    df <- data.frame(chromosome = gl(3,  10,  labels = c('A',  'B',  'C')),
                 probeset = gl(3,  10,  labels = c('X',  'Y',  'Z')),
                 ensg =  gl(3,  10,  labels = c('E1',  'E2',  'E3')),
                 symbol = gl(3,  10,  labels = c('S1',  'S2',  'S3')),
                 XXA_00 = rnorm(30),
                 XXA_36 = rnorm(30),
                 XXB_00 = rnorm(30))
    

    然后我们将aggregate与公式接口一起使用:

    df1 <- aggregate(cbind(XXA_00, XXA_36, XXB_00) ~ ensg + chromosome + symbol,  
        data = df,  FUN = mean)
    
    > df1
      ensg chromosome symbol      XXA_00      XXA_36      XXB_00
    1   E1          A     S1 -0.02533499 -0.06150447 -0.01234508
    2   E2          B     S2 -0.25165987  0.02494902 -0.01116426
    3   E3          C     S3  0.09454154 -0.48468517 -0.25644569
    

    【讨论】:

    • 感谢这个漂亮的解决方案!与 ddply 相比,它也快如闪电。不知道公式界面的威力!我已经使用 R 几个月了,但它一直让我感到惊讶(以一种好的方式)。令人尴尬的是,我仍然缺乏对您的解决方案进行投票的声誉......
    • @Johannes:FWIW,公式界面比其他的慢。
    • 我现在已经修改了 hadley 发布的here 的解决方案。它依赖于在应用平均值之前将数据转换为矩阵,从而大幅提高速度。我的数据集尺寸为 10^5 x 43,速度提高了 25 倍!
    猜你喜欢
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多