purrr 0.1.0を使ってみる(6) cross
crossは、複数ベクトルの直積集合を返す、すなわち、いい感じのexpand.grid()
です。
完。
...というくらいに特筆すべきことはないと思うんですけど、↓こんな記事もあったので、いちおう書いときます。
cross_d()
これがexpand.grid()
と同じ動作をするものです。
とヘルプに書いてあったのでさっそく↑の記事と同じコードを試してみると、エラーになります。
library(purrr) age <- seq(from = 20, to = 40, by = 5) cross_d(性別 = c("男性", "女性"), 年齢 = age) #> Error in cross_d(性別 = c("男性", "女性"), 年齢 = age) : #> unused arguments (性別 = c("男性", "女性"), 年齢 = age)
これはなぜかと言うと、cross_d()
は引数にデータを並べるタイプ(...
)ではなく、データをまとめてlistにしたものを渡すタイプだからです。
引数の型が違うときにすることは何か。一つしかありませんね。そう、liftです。
...
(d)をlist(l)にしたいので、lift_ld()
を使います。これでgrid.expand()
と同じ動作ができます。
lift_ld(cross_d)(性別 = c("男性", "女性"), 年齢 = age) #> Source: local data frame [10 x 2] #> #> 性別 年齢 #> (chr) (dbl) #> 1 男性 20 #> 2 女性 20 #> 3 男性 25 #> 4 女性 25 #> 5 男性 30 #> 6 女性 30 #> 7 男性 35 #> 8 女性 35 #> 9 男性 40 #> 10 女性 40
いや、素直にlistを渡してもいいんですけど。
cross_d(list(性別 = c("男性", "女性"), 年齢 = age))
cross_n()
、cross2()
、cross3()
cross_d()
はdata.frameを返してましたが、cross_n()
はそのlist版です。ヘルプに載ってる例はこんな感じ。
data <- list( id = c("John", "Jane"), greeting = c("Hello.", "Bonjour."), sep = c("! ", "... ") ) # 組み合わせのlistのlistをつくる data_crossed <- cross_n(data) str(data_crossed, list.len = 4) #> List of 8 #> $ :List of 3 #> ..$ id : chr "John" #> ..$ greeting: chr "Hello." #> ..$ sep : chr "! " #> $ :List of 3 #> ..$ id : chr "Jane" #> ..$ greeting: chr "Hello." #> ..$ sep : chr "! " #> $ :List of 3 #> ..$ id : chr "John" #> ..$ greeting: chr "Bonjour." #> ..$ sep : chr "! " #> $ :List of 3 #> ..$ id : chr "Jane" #> ..$ greeting: chr "Bonjour." #> ..$ sep : chr "! " #> [list output truncated] map_chr(data_crossed, lift_dl(paste)) #> [1] "John! Hello." "Jane! Hello." "John! Bonjour." "Jane! Bonjour." "John... Hello." "Jane... Hello." "John... Bonjour." #> [8] "Jane... Bonjour."
cross2()
とかcross3()
はlistじゃなくて、引数を2つ、3つ並べる版です。
↑の例はこんな感じにも書けます。ただし、列名にあたるものを指定することはできないので、あとでid
、greeting
のような列名でデータを参照したい場合は向いていません。今回は特に列名を必要しないので問題ないですけど。
cross3( c("John", "Jane"), c("Hello.", "Bonjour."), c("! ", "... ") ) %>% map_chr(lift_dl(paste))
.filter
引数
expand.grid()
とひとつ違う点として、.filter
に叙述関数(predicate function)を指定できるということがあります。これを指定すると、その関数がTRUE
を返す行は除外されます。
たとえば「1~3の数字で、別々の数字の組み合わせ」というのは以下のように書けます。ヘルプに載ってる例です。
seq_len(3) %>% list(x = ., y = .) %>% cross_n(.filter = `==`) #> Source: local data frame [6 x 2] #> #> x y #> (int) (int) #> 1 2 1 #> 2 3 1 #> 3 1 2 #> 4 3 2 #> 5 1 3 #> 6 2 3
これはちょっと便利かも。でも、ふつうにdiscard()
を使えばいい気もするけど。。
感想
expand.grid()
でそんなに困らない気もしますが、たしかに関数にmap()
するときとかはdata.frameよりlistの方が便利なこともあるので、これはこれで有用かも。でも、わざわざ覚えるほどでもないかなあ...というのが個人的な感想です。