dplyrで複数カラムを追加したいと思ったときはpurrrでslice_rows+by_slice

r-wakalangでそんな話があった時のメモ。もっといいコードがある気もしつつ、まあこれが割とシンプルなのではないでしょうか。問題は、purrrの使い方をいざという時に思い出せないというだけで。。(重大)

plyr::mdply()

私はポストplyr世代なのでplyrまったく分からないのですが、この関数がやりたいことに近いみたいです。(実は違うけど)

plyr::mdply(data.frame(mean = 1:5, sd = 1:5), rnorm, n = 2)
#>   mean sd        V1        V2
#> 1    1  1 0.1394717 -1.233116
#> 2    2  2 3.4665594  5.460578
#> 3    3  3 1.2068706  2.323817
#> 4    4  4 4.4370674  8.491732
#> 5    5  5 4.5515510 14.603859

dplyrのIssuesを検索

mdply、でdplyrのIssuesを検索すると、gridExtraの人がそれっぽい機能要望を挙げてました。

で、最後にHadleyがこう書いてます。

This now feels more like a job for purrr.

出ました、purrr。

purrr::by_slice()

ということで、purrrを見てみると、それっぽいのがありました。slice_rows()はdplyrでいうgroup_by的なもので、by_slice()summarise()とかdo()にあたるものです。

違うのは、.collateという引数があることです。これに"rows"を指定すると行として、"cols"を指定すると列として、"list"はリストとして、それぞれ結果が追加されます。

library(purrr)

iris %>%
  slice_rows("Species") %>%
  by_slice(~ quantile(.$Sepal.Length),
           .collate = "cols")
#> Source: local data frame [3 x 6]
#> 
#>      Species .out1 .out2 .out3 .out4 .out5
#>       <fctr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1     setosa   4.3 4.800   5.0   5.2   5.8
#> 2 versicolor   4.9 5.600   5.9   6.3   7.0
#> 3  virginica   4.9 6.225   6.5   6.9   7.9

個人的には

purrrの.collate引数は便利なので、いつも思うんですけどtidyrにもつけてほしいです。

もし↓こんな風に書ければ、list()でラップするのが不格好ですが、個人的には分かりやすい気がします。。

iris %>%
  group_by(Species) %>% 
  summarize(sepal_length_quantile = list(quantile(Sepal.Length))) %>%
  tidyr::unnest(sepal_length_quantile, .collate = "cols")