メモ:カテゴリごとにデータフレーム単位で処理をしたいときはdplyr::do()とsplit() + purrr::map()のどちらが速い

ベクトルを引数に取る関数はgroup_by()でいいわけですが、データフレームを引数に取るような関数はどうしますか?

do()しますか?

ダーッ!しますか!?

という話を書いたのがこれでした。

元ネタはこっち:

そういえば速度的にはどうなんだろう?と思ってやってみたらあんまり変わらなかったです。 サイズがでかくなるとほとんど差がなくなるのは、データコピーのコストが時間のほとんどを占めるようになるからだと思われます。 あんまりちゃんと調べてないけど、とりあえずメモ。

library(tidyverse)
#> Loading tidyverse: ggplot2
#> Loading tidyverse: tibble
#> Loading tidyverse: tidyr
#> Loading tidyverse: readr
#> Loading tidyverse: purrr
#> Loading tidyverse: dplyr
#> Conflicts with tidy packages ----------------------------------------------
#> filter(): dplyr, stats
#> lag():    dplyr, stats


f_do <- function(d) {
  d %>%
    group_by(category) %>%
    do(mod = lm(y ~ x, data = .))
}

f_split <- function(d) {
  d %>%
    split(.$category) %>%
    map_df(~ tibble(mod = list(lm(y ~ x, data = .))), .id = "category")
}

f_split_simple <- function(d) {
  d %>%
    split(.$category) %>%
    map(~ lm(y ~ x, data = .))
}

set.seed(8)
d1 <- tibble(
  x = runif(100000),
  y = runif(100000),
  category = sample(letters, size = 100000, replace = TRUE)
)

d2 <- tibble(
  x = runif(10000000),
  y = runif(10000000),
  category = sample(letters, size = 10000000, replace = TRUE)
)

microbenchmark::microbenchmark(f_do(d1), f_split(d1), f_split_simple(d1))
#> Unit: milliseconds
#>                expr      min       lq     mean   median       uq      max
#>            f_do(d1) 128.3619 133.5916 147.2779 138.3858 147.8468 269.6922
#>         f_split(d1) 206.0919 213.7532 230.3181 219.4978 227.8549 368.6876
#>  f_split_simple(d1) 109.2449 116.4557 126.8158 120.3789 126.8085 187.0286
#>  neval
#>    100
#>    100
#>    100

microbenchmark::microbenchmark(f_do(d2), f_split(d2), f_split_simple(d2))
#> Unit: seconds
#>                expr      min       lq     mean   median       uq      max
#>            f_do(d2) 10.65072 11.10749 11.71277 11.43427 12.17508 15.46999
#>         f_split(d2) 10.56382 11.22494 11.71088 11.53959 12.13664 15.07528
#>  f_split_simple(d2) 10.39703 11.04642 11.64527 11.51452 12.06741 14.59655
#>  neval
#>    100
#>    100
#>    100