【问题标题】:plyr outperforms dplyr and data.table - What's wrong?plyr 优于 dplyr 和 data.table - 怎么了?
【发布时间】:2016-03-10 21:31:38
【问题描述】:

我必须对大表​​的每一行(~ 2M 行)应用一个函数。我曾经为此使用plyr,但表格不断增长,当前的解决方案开始接近不可接受的运行时间。我以为我可以切换到data.tabledplyr,一切都很好,但事实并非如此。

这是一个例子:

library(data.table)
library(plyr)
library(dplyr)

dt = data.table("ID_1" = c(1:1000), # unique ID
                "ID_2" = ceiling(runif(1000, 0, 100)), # other ID, duplicates possible
                "group" = sample(LETTERS[1:10], 1000, replace = T), 
                "value" = runif(1000),
                "ballast1" = "X", # keeps unchanged in derive_dt
                "ballast2" = "Y", # keeps unchanged in derive_dt
                "ballast3" = "Z", # keeps unchanged in derive_dt
                "value_derived" = 0)
setkey(dt, ID_1)
extra_arg = c("A", "F", "G", "H")

ID_1 保证不包含重复项。现在我定义一个函数应用于每一行/ID_1:

derive = function(tmprow, extra_arg){
  if(tmprow$group %in% extra_arg){return(NULL)} # exlude entries occuring in extra_arg
  group_index = which(LETTERS == tmprow$group)
  group_index = ((group_index + sample(1:26, 1)) %% 25) + 1
  new_group = LETTERS[group_index]
  if(new_group %in% unique(dt$group)){return(NULL)}
  new_value = runif(1)
  row_derived = tmprow
  row_derived$group = new_group
  row_derived$value = runif(1)
  row_derived$value_derived = 1
  return(row_derived)
}

这个没有做任何有用的事情(实际有)。关键是该函数取一行并计算相同格式的新行。

现在比较一下:

set.seed(42)
system.time(result_dt <- dt[, derive(.SD, extra_arg), by = ID_1])
set.seed(42)
system.time(result_dplyr <- dt %>% group_by(ID_1) %>% do(derive(., extra_arg)))
set.seed(42)
system.time(results_plyr <- x <- ddply(dt, .variable = "ID_1", .fun = derive, extra_arg))

plyrdata.tabledplyr 快大约 8 倍。显然我在这里做错了什么,但是什么?


编辑

感谢 eddi 的回答,我可以将 data.tabledplyr 的运行时间分别减少到 plyr 版本的 ~ 0.6 和 0.8。我将row_derived 初始化为data.frame:row_derived = as.data.frame(tmprow)。这很酷,但我仍然希望这些软件包能带来更高的性能提升......还有什么建议吗?

【问题讨论】:

  • 您正在对 data.table 使用 data.frame 语法,因此您不应期望获得 data.table 速度 :) 正如 eddi 指出的那样,您应该寻找矢量化解决方案,目前您基本上是在循环@ 987654335@ 超过ID_1
  • @jangorecki:您的意思是一次修改一列?我怀疑这在我的情况下是否可行。
  • 我的意思是使用$&lt;-而不是使用:=

标签: r performance data.table dplyr plyr


【解决方案1】:

问题是您使用的分配在data.table 中具有非常高的开销,并且plyr 在传递给您的derive 函数之前将该行转换为data.frame,从而避免了它:

library(microbenchmark)

df = as.data.frame(dt)

microbenchmark({dt$group = dt$group}, {df$group = df$group})
#Unit: microseconds
#                        expr      min       lq       mean    median       uq      max neval
# {     dt$group = dt$group } 1895.865 2667.499 3092.38903 3080.3620 3389.049 4984.406   100
# {     df$group = df$group }   26.045   45.244   64.13909   61.6045   79.635  157.266   100

我不能建议一个好的解决方法,因为你说你的例子不是真正的问题,所以没有必要更好地解决它。一些基本的建议是 - 对代码进行矢量化,并改用 :=set(取决于你最终会做什么)。

【讨论】:

  • 好的,这就是开始。谢谢!
猜你喜欢
  • 2014-11-08
  • 1970-01-01
  • 2016-02-27
  • 2013-04-15
  • 2022-01-06
  • 2014-11-05
  • 2015-01-08
  • 2015-05-02
  • 1970-01-01
相关资源
最近更新 更多