【问题标题】:Why is dplyr slower than plyr for data aggregation?为什么 dplyr 在数据聚合方面比 plyr 慢?
【发布时间】:2016-12-02 05:06:22
【问题描述】:

背景问题:

假设我们有这样一个数据集:

ID DRIVE_NUM FLAG
 1         A PASS
 2         A FAIL
 3         A PASS
-----------------
 4         B PASS
 5         B PASS
 6         B PASS
-----------------
 7         C PASS
 8         C FAIL
 9         C FAIL

我想通过以下规则聚合 DRIVE_NUM 的数据集:

对于特定的 DRIVE_NUM 组,

如果 DRIVE_NUM 组中有任何 FAIL 标志,我想要第一行 带有 FAIL 标志。

如果组中没有 FAIL 标志,则只取组中的第一行 组。

所以,我会得到以下套装:

  ID DRIVE_NUM FLAG
   2         A FAIL
   4         B PASS
   8         C FAIL

更新:

dplyr 的解决方案似乎比 plyr 还要慢。我是否使用不当?

#Simulate Data

X = data.frame(
  group = rep(paste0("NO",1:10000),each=2),
  flag = sample(c("F","P"),20000,replace = TRUE),
  var = rnorm(20000)
)



library(plyr)
library(dplyr)

#plyr

START = proc.time()
X2 = ddply(X,.(flag),function(df) {
  if( sum(df$flag=="F")> 0){
    R = df[df$flag=="F",]
    if(nrow(R)>1) {R = R[1,]} else {R = R}
  } else{
    R = df[1,]
  }
  R
})
proc.time() - START   

#user  system elapsed 
#0.03    0.00    0.03 

#dplyr method 1

START = proc.time()
X %>%
  group_by(group) %>% 
  slice(which.min(flag))
proc.time() - START  

#user  system elapsed 
#0.22    0.02    0.23 

#dplyr method 2

START = proc.time()
X %>%
  group_by(group, flag) %>%
  slice(1) %>%
  group_by(group) %>% 
  slice(which.min(flag))
proc.time() - START  

#user  system elapsed 
#0.28    0.00    0.28 

有没有比 plyr 快得多的 data.table 版本?

【问题讨论】:

    标签: r dplyr plyr aggregation


    【解决方案1】:

    嗯,这并不比data.table 快,但绝对是一个改进:

    START = proc.time()
    m3 <- X %>%
        group_by(group) %>% 
        arrange(flag) %>%
        slice(1)
    proc.time() - START
    
    #user  system elapsed 
    #0.03    0.00    0.05 
    
    # OP - method 1
    START = proc.time()
    m1 <- X %>%
        group_by(group) %>% 
        slice(which.min(flag))
    proc.time() - START
    
    #user  system elapsed 
    #0.31    0.00    0.33 
    
    # OP - method 2
    START = proc.time()
    m2 <- X %>%
        group_by(group, flag) %>%
        slice(1) %>%
        group_by(group) %>% 
        slice(which.min(flag))
    proc.time() - START 
    
    #user  system elapsed 
    #0.39    0.02    0.45 
    
    identical(m2, m3)
    [1] TRUE
    

    【讨论】:

      【解决方案2】:

      使用data.table

      library(data.table)
      START = proc.time()
       X3 = as.data.table(X)[X[, .I[which.min(flag)] , by = group]$V1]
      proc.time() - START
      #   user  system elapsed 
      #  0.00    0.02    0.02 
      

      或使用order

      START = proc.time()
       X4 = as.data.table(X)[order(flag), .SD[1L] , by = group]
      proc.time() - START
      #    user  system elapsed 
      #    0.02    0.00    0.01 
      

      dplyrplyr使用OP的代码对应的时序是

      #   user  system elapsed 
      #  0.28    0.04    2.68 
      
      #   user  system elapsed 
      #  0.01    0.06    0.67 
      

      正如@Frank 所评论的,base R 方法的时机是

      START = proc.time()
      Z = X[order(X$flag),]
      X5 = with(Z, Z[tapply(seq(nrow(X)), group, head, 1), ])
      proc.time() - START
      #    user  system elapsed 
      #    0.15    0.03    0.65 
      

      我猜slice 正在减慢dplyr

      【讨论】:

      • @Frank 你之前有没有提到slice 更慢?我想有人对此发表了评论
      • 嗯,我不确定。我喜欢 slice 因为它很简单,但是是的,这听起来很熟悉。
      • 在基础上,这并不算太慢:system.time({Z = X[order(X$flag),]; res = with(Z, Z[tapply(seq(nrow(X)), group, head, 1), ])}) 类似于 .SD[1L] 方式的想法。
      猜你喜欢
      • 1970-01-01
      • 2012-07-17
      • 2020-11-25
      • 2019-06-16
      • 2019-06-05
      • 2018-12-23
      • 2021-03-12
      • 1970-01-01
      • 2011-03-23
      相关资源
      最近更新 更多