大数据框上的group_by和mutate变慢

时间:2018-10-08 20:23:00

标签: r dataframe data.table tidyverse

我正在处理大型行(最少8百万行)dataframes,并且希望基于几个分组变量和rmultinom进行一些基本计算。就我的代码而言,至少需要大约1秒钟才能完成计算,这不是问题,但是我需要做数千次,所以我真的想加快速度。

我目前正在使用dataframestidyverse,但我并不局限于这两个。我尝试使用data.table来实现,但无法弄清楚。关于如何加快速度的任何建议将不胜感激。

一个例子(真实数据可以大于或等于一个数量级):

library(tidyverse)
library(microbenchmark)

# create dummy data
df <- data.frame(fact = rep(letters, each = 312000), 
                 month = rep(month.name, 26), 
                 num = rep(sample(10000:100000, 12), 26), 
                 prob = runif(312))

# Order by month     
df <- df[order(df$month), ]

# group by two factor variables and calculate new variable 
microbenchmark({
  df2 <- df %>%
    group_by(fact, month) %>%
    mutate(res = ifelse(prob > 0, c(rmultinom(1, num[1], prob = prob)), 0))}, times = 10)


 > Unit: milliseconds
 > min      lq       mean     median   uq        max         neval
 > 816.3126 822.4083 840.7966 834.6163 855.5139  879.9345    10

3 个答案:

答案 0 :(得分:4)

评论太久了,所以我在这里发表。

运行

library(profr)
plot(profr(
df %>% group_by(fact, month) %>% 
   mutate(res = ifelse(prob > 0, c(rmultinom(1, num[1], prob = prob)), 0))
))

我得到以下信息:

enter image description here

因此,您似乎真的想为multinom找到一个更快的实现,这似乎是瓶颈。 dplyrdata.table的瓶颈相同,这意味着仅加速rmultinorm会大大提高速度。

答案 1 :(得分:1)

使用,您可以这样做:

dt <- copy(df)
setDT(dt)

dt[, res := 0L][prob > 0, res := c(rmultinom(1, num[1], prob = prob)), by = .(fact, month)]

这会给您带来轻微的速度改进:

microbenchmark(dp = df %>%
                 group_by(fact, month) %>%
                 mutate(res = ifelse(prob > 0, c(rmultinom(1, num[1], prob = prob)), 0)),
               dt = dt[, res := 0L][prob > 0, res := c(rmultinom(1, num[1], prob = prob)), by = .(fact, month)],
               times = 1)
Unit: seconds
 expr      min       lq     mean   median       uq      max neval
   dp 1.356745 1.356745 1.356745 1.356745 1.356745 1.356745     1
   dt 1.063363 1.063363 1.063363 1.063363 1.063363 1.063363     1

答案 2 :(得分:1)

使用dplyrDT语法,还可以减少由管道运算符引起的开销。

为说明管道造成的开销:

microbenchmark(pipe = iris %>%
                 group_by(Species) %>% 
                 mutate(mean = mean(Sepal.Length)),
               no_pipe = mutate(group_by(iris, Species), mean = mean(Sepal.Length)),
               times = 100) %>% autoplot()

enter image description here