dplyr::mutate_all と purrr::modify の比較

カテゴリ: r

dplyr::mutate_all はデータフレーム中の各変数 (列) に対して関数を適用する。

purrr::modify はリストライクなオブジェクトの各要素に対して関数を適用するが、返り値は入力したオブジェクトと同じクラスになる。このため、データフレームを入力するとデータフレームを返すので、 dplyr::mutate_all のように振る舞うことができる。

purrr::modify の方が色々なデータを受け取れる上位互換なのだろうか? と疑問に思ったので調べてみた。

引数を比べてみる

library(dplyr)
library(purrr)
args(dplyr::mutate_all)
#> function (.tbl, .funs, ...) 
#> NULL

args(purrr::modify)
#> function (.x, .f, ...) 
#> NULL

さっそく違いが分かるが、mutate_all の第二引数は funs で複数の関数を取る。つまり複数の関数をデータフレームに適用できる。一方で modify の第二引数は .f で一つの関数しか取れない。

なお、Ellipsis (...) には、 .funs.f に追加で指定したい引数を与えることができる。

mutate_all を使ってみる

シンプルなデータフレームを容易しておこう。

d <- data.frame(x = 1:3, y = 4:6)

.funs に単一の関数として sqrt を指定すると、xy の平方根が得られる。

d %>% mutate_all(sqrt)
#>          x        y
#> 1 1.000000 2.000000
#> 2 1.414214 2.236068
#> 3 1.732051 2.449490

一方、.funs = list(sqrt, log) として複数の関数を指定すると、x 列、 y 列はそのままに、関数を適用した結果の列が x_fn1, y_fn1, x_fn2, y_fn2 といった具合に追加される。

d %>% mutate_all(list(sqrt, log))
#>   x y    x_fn1    y_fn1     x_fn2    y_fn2
#> 1 1 4 1.000000 2.000000 0.0000000 1.386294
#> 2 2 5 1.414214 2.236068 0.6931472 1.609438
#> 3 3 6 1.732051 2.449490 1.0986123 1.791759

_fn* というサフィックスをコントロールしたい場合は、 .funs に与える関数のリストを名前付きリストにしておけばいい。

d %>% mutate_all(list(sqrt = sqrt, log = log))
#>   x y   x_sqrt   y_sqrt     x_log    y_log
#> 1 1 4 1.000000 2.000000 0.0000000 1.386294
#> 2 2 5 1.414214 2.236068 0.6931472 1.609438
#> 3 3 6 1.732051 2.449490 1.0986123 1.791759

modify を使ってみる

d %>% modify(sqrt)
#>          x        y
#> 1 1.000000 2.000000
#> 2 1.414214 2.236068
#> 3 1.732051 2.449490

d %>% mutate_all(sqrt) と同じ結果が得られた。

ベンチマーク

適用する関数が一つだけの時は mutate_allmodify は同じ結果を返すので、それぞれの性能を比較してみることにした。

library(bench)
library(ggplot2)
autoplot(mark(
  mutate_all(d, identity),
  modify(d, identity)
))
#> Loading required namespace: tidyr

機能がシンプルな分 modify が圧倒的ですね。

Enjoy!