为什么在数据聚合方面dplyr比plyr慢?

3

背景问题:

假设我们有一个类似于以下数据集的数据:

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标志的数据。

如果DRIVE_NUM组中不存在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版本?

2个回答

6
使用 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 

使用 OP 的代码,与 dplyrplyr 相应的时间如下:

#   user  system elapsed 
#  0.28    0.04    2.68 

#   user  system elapsed 
#  0.01    0.06    0.67 

同意Frank的评论,使用基础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 的速度。


1
@Frank,你之前提到过slice比较慢,是吗?我记得有人评论过这个。 - akrun
嗯,我不确定。我喜欢切片因为它很简单,但是是的,那听起来很熟悉。 - Frank
1
在基础语言中,这并不太慢:system.time({Z = X[order(X$flag),]; res = with(Z, Z[tapply(seq(nrow(X)), group, head, 1), ])})。与 .SD[1L] 的方法类似。 - Frank

3

嗯,虽然不如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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接