8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

按组计算平均值

Thomas Wouters 1月前

43 0

我有一个类似于此的大型数据框:df <- data.frame(dive = factor(sample(c(\'dive1\',\'dive2\'), 10, replace=TRUE)), speed = runif(10) ...

我有一个与此类似的大数据框:

df <- data.frame(dive = factor(sample(c("dive1","dive2"), 10, replace=TRUE)),
                 speed = runif(10)
                 )
> df
    dive      speed
1  dive1 0.80668490
2  dive1 0.53349584
3  dive2 0.07571784
4  dive2 0.39518628
5  dive1 0.84557955
6  dive1 0.69121443
7  dive1 0.38124950
8  dive2 0.22536126
9  dive1 0.04704750
10 dive2 0.93561651

我的目标是当另一列等于某个值时获取一列值的平均值,并对所有值重复此操作。即在上面的例子中,我想为该列 speed 的每个唯一值 dive 。因此 dive==dive1 ,当的平均值 speed 是这样的,对于每个值也是如此 dive .

帖子版权声明 1、本帖标题:按组计算平均值
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Thomas Wouters在本站《for-loop》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 在 R 中有很多方法可以做到这一点。具体来说,, by , aggregate , split plyr , cast , tapply , data.table , dplyr ,等等。

    广义上讲,这些问题属于拆分-应用-合并的形式。Hadley Wickham 写了一篇 精彩的文章 ,可以让您更深入地了解整个问题类别,非常值得一读。他的 plyr 软件包实现了通用数据结构的策略,并且 dplyr 是针对数据框架进行性能调整的较新的实现。它们允许解决与此形式相同但更复杂的问题。作为解决数据操作问题的通用工具,它们非常值得学习。

    性能是处理非常大的数据集时的一个问题,因此很难找到基于 的解决方案 data.table 。但是,如果你只处理中等或更小的数据集,花时间学习 data.table 可能不值得。 dplyr 也可以很快,所以如果你想加快速度,但不需要 的可扩展性,这是一个不错的选择 data.table .

    下面的许多其他解决方案不需要任何额外的软件包。其中一些甚至在中大型数据集上也相当快。它们的主要缺点是隐喻或灵活性。我所说的隐喻是指它是一种设计用于强制其他东西以“聪明”的方式解决这种特定类型问题的工具。我所说的灵活性是指它们缺乏解决广泛类似问题或轻松产生整洁输出的能力。


    示例

    base 功能

    点击应用:

    tapply(df$speed, df$dive, mean)
    #     dive1     dive2 
    # 0.5419921 0.5103974
    

    总计的

    aggregate 接收数据框,输出数据框,并使用公式界面。

    aggregate( speed ~ dive, df, mean )
    #    dive     speed
    # 1 dive1 0.5790946
    # 2 dive2 0.4864489
    

    经过

    在其最用户友好的形式中,它接受向量并对其应用函数。但是,其输出的形式不太易于操作。:

    res.by <- by(df$speed, df$dive, mean)
    res.by
    # df$dive: dive1
    # [1] 0.5790946
    # ---------------------------------------
    # df$dive: dive2
    # [1] 0.4864489
    

    为了解决这个问题,可以简单使用 by 中的 as.data.frame 方法 taRifx

    library(taRifx)
    as.data.frame(res.by)
    #    IDX1     value
    # 1 dive1 0.6736807
    # 2 dive2 0.4051447
    

    分裂

    顾名思义,它只执行拆分-应用-合并策略中的“拆分”部分。为了使其余部分正常工作,我将编写一个用于 sapply 应用-合并的小函数。 sapply 自动尽可能简化结果。在我们的例子中,这意味着一个向量而不是一个数据框,因为我们只有 1 个结果维度。

    splitmean <- function(df) {
      s <- split( df, df$dive)
      sapply( s, function(x) mean(x$speed) )
    }
    splitmean(df)
    #     dive1     dive2 
    # 0.5790946 0.4864489 
    

    外部包

    数据表

    library(data.table)
    setDT(df)[ , .(mean_speed = mean(speed)), by = dive]
    #    dive mean_speed
    # 1: dive1  0.5419921
    # 2: dive2  0.5103974
    

    dplyr

    library(dplyr)
    group_by(df, dive) %>% summarize(m = mean(speed))
    

    plyr (前身为 dplyr )

    以下是 官方页面 上的说明 plyr :

    R 函数(例如 base split 函数系列) apply 已经可以做到这一点 plyr 使用以下命令可以使一切变得更容易一些:

    • 完全一致的名称、参数和输出
    • 通过 foreach
    • 数据框、矩阵和列表的输入和输出
    • 进度条用于跟踪长时间运行的操作
    • 内置错误恢复和信息错误消息
    • 在所有转换中维护的标签

    换句话说,如果你学习一种拆分-应用-组合操作工具,它应该是 plyr .

    library(plyr)
    res.plyr <- ddply( df, .(dive), function(x) mean(x$speed) )
    res.plyr
    #    dive        V1
    # 1 dive1 0.5790946
    # 2 dive2 0.4864489
    

    重塑2

    reshape2 库的设计主要重点不是拆分-应用-合并。相反,它使用两部分熔化/铸造策略来执行 各种数据重塑任务 。但是,由于它允许聚合函数,因此可以用于此问题。它不是我进行拆分-应用-合并操作的首选,但它的重塑功能非常强大,因此您也应该学习这个包。

    library(reshape2)
    dcast( melt(df), variable ~ dive, mean)
    # Using dive as id variables
    #   variable     dive1     dive2
    # 1    speed 0.5790946 0.4864489
    

    基准

    10行,2组

    library(microbenchmark)
    m1 <- microbenchmark(
      by( df$speed, df$dive, mean),
      aggregate( speed ~ dive, df, mean ),
      splitmean(df),
      ddply( df, .(dive), function(x) mean(x$speed) ),
      dcast( melt(df), variable ~ dive, mean),
      dt[, mean(speed), by = dive],
      summarize( group_by(df, dive), m = mean(speed) ),
      summarize( group_by(dt, dive), m = mean(speed) )
    )
    
    > print(m1, signif = 3)
    Unit: microseconds
                                               expr  min   lq   mean median   uq  max neval      cld
                        by(df$speed, df$dive, mean)  302  325  343.9    342  362  396   100  b      
                  aggregate(speed ~ dive, df, mean)  904  966 1012.1   1020 1060 1130   100     e   
                                      splitmean(df)  191  206  249.9    220  232 1670   100 a       
      ddply(df, .(dive), function(x) mean(x$speed)) 1220 1310 1358.1   1340 1380 2740   100      f  
             dcast(melt(df), variable ~ dive, mean) 2150 2330 2440.7   2430 2490 4010   100        h
                       dt[, mean(speed), by = dive]  599  629  667.1    659  704  771   100   c     
     summarize(group_by(df, dive), m = mean(speed))  663  710  774.6    744  782 2140   100    d    
     summarize(group_by(dt, dive), m = mean(speed)) 1860 1960 2051.0   2020 2090 3430   100       g 
    
    autoplot(m1)
    

    benchmark 10 rows

    与往常一样, data.table 开销稍大一些,因此对于小型数据集来说大约是平均水平。不过,这些是微秒,因此差异很小。任何一种方法都可以在这里正常工作,您应该根据以下情况进行选择:

    • 您已经熟悉或想要熟悉的( plyr 因其灵活性而值得学习; data.table 如果您打算分析庞大的数据集,则值得学习; by 并且 aggregate split 都是基本 R 函数,因此可以普遍使用)
    • 它返回什么输出(数字、data.frame 或 data.table - 后者继承自 data.frame)

    1000 万行,10 组

    但是如果我们有一个大数据集怎么办?让我们尝试将 10^7 行分成 10 组。

    df <- data.frame(dive=factor(sample(letters[1:10],10^7,replace=TRUE)),speed=runif(10^7))
    dt <- data.table(df)
    setkey(dt,dive)
    
    m2 <- microbenchmark(
      by( df$speed, df$dive, mean),
      aggregate( speed ~ dive, df, mean ),
      splitmean(df),
      ddply( df, .(dive), function(x) mean(x$speed) ),
      dcast( melt(df), variable ~ dive, mean),
      dt[,mean(speed),by=dive],
      times=2
    )
    
    > print(m2, signif = 3)
    Unit: milliseconds
                                               expr   min    lq    mean median    uq   max neval      cld
                        by(df$speed, df$dive, mean)   720   770   799.1    791   816   958   100    d    
                  aggregate(speed ~ dive, df, mean) 10900 11000 11027.0  11000 11100 11300   100        h
                                      splitmean(df)   974  1040  1074.1   1060  1100  1280   100     e   
      ddply(df, .(dive), function(x) mean(x$speed))  1050  1080  1110.4   1100  1130  1260   100      f  
             dcast(melt(df), variable ~ dive, mean)  2360  2450  2492.8   2490  2520  2620   100       g 
                       dt[, mean(speed), by = dive]   119   120   126.2    120   122   212   100 a       
     summarize(group_by(df, dive), m = mean(speed))   517   521   531.0    522   532   620   100   c     
     summarize(group_by(dt, dive), m = mean(speed))   154   155   174.0    156   189   321   100  b      
    
    autoplot(m2)
    

    benchmark 1e7 rows, 10 groups

    然后 data.table dplyr 使用对 data.table s 进行操作显然是可行的方法。某些方法( aggregate dcast )开始看起来非常慢。

    1000 万行,1000 组

    如果您有更多组,差异会更加明显。对于 1,000 个组 和相同的 10^7 行:

    df <- data.frame(dive=factor(sample(seq(1000),10^7,replace=TRUE)),speed=runif(10^7))
    dt <- data.table(df)
    setkey(dt,dive)
    
    # then run the same microbenchmark as above
    print(m3, signif = 3)
    Unit: milliseconds
                                               expr   min    lq    mean median    uq   max neval    cld
                        by(df$speed, df$dive, mean)   776   791   816.2    810   828   925   100  b    
                  aggregate(speed ~ dive, df, mean) 11200 11400 11460.2  11400 11500 12000   100      f
                                      splitmean(df)  5940  6450  7562.4   7470  8370 11200   100     e 
      ddply(df, .(dive), function(x) mean(x$speed))  1220  1250  1279.1   1280  1300  1440   100   c   
             dcast(melt(df), variable ~ dive, mean)  2110  2190  2267.8   2250  2290  2750   100    d  
                       dt[, mean(speed), by = dive]   110   111   113.5    111   113   143   100 a     
     summarize(group_by(df, dive), m = mean(speed))   625   630   637.1    633   644   701   100  b    
     summarize(group_by(dt, dive), m = mean(speed))   129   130   137.3    131   142   213   100 a     
    
    autoplot(m3)
    

    enter image description here

    因此, data.table 继续很好地扩展,并且 dplyr 对 的操作 data.table 也运行良好,而 dplyr data.frame 慢了近一个数量级。 split / sapply 策略似乎在组数方面扩展性较差(这意味着 split() 可能很慢, 很快 sapply )。 by 继续相对高效 - 在 5 秒内,用户肯定能注意到,但对于如此大的数据集来说,这仍然不算不合理。不过,如果您经常处理这种大小的数据集, data.table 显然是最佳选择 - 100% data.table 以获得最佳性能或 dplyr 使用 dplyr 作为 data.table 可行的替代方案。

返回
作者最近主题: