更有效的na.aggregate在R中按最大值分组

时间:2017-07-04 23:49:41

标签: r data.table dplyr rcpp zoo

我的数据框大小约为20,000 x 300,我目前正使用na.aggregate zoo来为每列填充最大值。如果组中有所有NA,则应返回NA。我正在尝试为我的代码块找到一个更有效的解决方案来加快速度。这是一个玩具数据框架,以及dplyr解决方案的尝试速度比na.aggregate版本慢约10倍:

library(zoo)
library(dplyr)
library(microbenchmark)

# toy data frame
DF <- tibble(group=c(rep("A",4),rep("B",2),rep("C",3)),
             Col1=rep(NA_real_,9),
             Col2=c(5,3,2,NA,NA,NA,1,2,NA),
             Col3=c(NA,NA,NA,NA,1,NA,2,3,NA))

# na.aggregate solution
na.agg <- function(DF){

  agg.max <- suppressWarnings(na.aggregate(DF,DF$group,FUN=max))
  agg.max[,2:ncol(agg.max)] <- apply(agg.max[,2:ncol(agg.max)],2,as.numeric)

  return(agg.max)
}

# dplyr solution
dplyr.agg <- function(DF){

  Sol.2 <- DF %>% 
    group_by(group) %>% 
    mutate_all(funs(case_when(
      all(is.na(.))~NA_real_,
      is.na(.)~max(.,na.rm=TRUE),
      TRUE ~ as.numeric(I(.))))) 
}

# Timing test
res <- microbenchmark(na.agg(DF),dplyr.agg(DF))

print(res)

Unit: milliseconds
          expr       min        lq      mean    median        uq       max neval
    na.agg(DF)  1.698281  1.879652  2.122334  2.063809  2.297201  3.763934   100
 dplyr.agg(DF) 10.730040 11.553523 12.484254 12.068044 12.778734 21.239836   100

是否有更快的rcppdata.table或更好的dplyr解决方案?

1 个答案:

答案 0 :(得分:1)

还有更快的速度从na.agg开始。不要在整个na.aggregateDF,因为包含group列意味着所有内容都会转换为字符,这会减慢速度。在DF[-1]上运行,不包括group,您甚至不需要事后进行as.numeric转换。

na.agg2 <- function(DF) {
  DF[-1] <- na.aggregate(DF[-1], DF$group, FUN=function(x) if(all(is.na(x))) NA else max(x) )
  DF
}

与当前na.agg

相比,这显着降低了执行速度
microbenchmark(na.agg(DF),na.agg2(DF))
#Unit: milliseconds
#        expr      min       lq     mean   median       uq       max neval
#  na.agg(DF) 2.127476 2.145621 2.232542 2.163216 2.216366  3.433873   100
# na.agg2(DF) 1.047977 1.055675 1.560371 1.062455 1.080783 49.378785   100

使用data.table似乎再次更快:

DT <- copy(DF)
DT <- as.data.table(DT)
DT[,
  lapply(.SD, function(x) if(all(is.na(x))) NA_real_ else
                             replace(x,is.na(x),max(x,na.rm=TRUE))), 
  by=group
]