purrr 0.1.0を使ってみる(3) zip
追記(2015/11/15): zip_*()
はtranspose()
になるらしいです。やっぱりまだコロコロ変わりますね...。
zipは、複数のベクトルを、各要素のペアのリストにします。PythonとかHaskellにあるのと同じやつです。
ただ、すべてがベクトルなRだと、他の言語とちょっと感覚が違います。purrrにはunzipがありません(正確には、昔あったけどなくなりました)。zip_n()
がzipもunzipも兼ねています。後述しますが、matrixをt()
するたびに行と列が入れ替わるように、zip_n()
するたび行と列(?)が入れ替わるイメージです。
ちなみに、タイトルに「zip」と書いといてあれなんですが、RではutilsにZIPファイルをつくるzip()
という関数があるので、purrrにはzip()
という名前の関数はありません。ややこしい…
zip2, zip3
zip2は、2つのベクトルを、zip3は3つのベクトルをzipします。
こんな感じです。
zip2(1:3, letters[1:3]) %>% str #> List of 3 #> $ :List of 2 #> ..$ : int 1 #> ..$ : chr "a" #> $ :List of 2 #> ..$ : int 2 #> ..$ : chr "b" #> $ :List of 2 #> ..$ : int 3 #> ..$ : chr "c"
1,2,3
と"a", "b", "c"
という2つのベクトルが、それぞれの1番目の要素のペア(1
, "a"
)、2番目の要素のペア(2
、"b"
)、3番目の要素のペア(3
、"c"
)のリストに変換されています。
型が同じものをzipするときは、.simplify = TRUE
をつけるとベクトルにしてくれます。
zip2(1:3, 11:13, .simplify = TRUE) %>% str List of 3 $ : int [1:2] 1 11 $ : int [1:2] 2 12 $ : int [1:2] 3 13
zip_n
zip_n
は、ベクトルのリストをzipします。
zip2
とzip3
は実はzip_n
のラッパーです。たとえばzip2(x, y)
はzip_n(list(x, y))
のショートカットです。
これは、冒頭に書いたように行と列を入れ変える感覚で使えます。たとえば、ヘルプに書いてある例はこんな感じです(スペース節約のため、ちょっと短めバージョンでやります。)
set.seed(1) x <- rerun(3, x = runif(1), y = runif(3)) x %>% str() #> List of 3 #> $ :List of 2 #> ..$ x: num 0.266 #> ..$ y: num [1:3] 0.372 0.573 0.908 #> $ :List of 2 #> ..$ x: num 0.202 #> ..$ y: num [1:3] 0.898 0.945 0.661 #> $ :List of 2 #> ..$ x: num 0.629 #> ..$ y: num [1:3] 0.0618 0.206 0.1766 x %>% zip_n() %>% str() #> List of 2 #> $ x:List of 3 #> ..$ : num 0.266 #> ..$ : num 0.202 #> ..$ : num 0.629 #> $ y:List of 3 #> ..$ : num [1:3] 0.372 0.573 0.908 #> ..$ : num [1:3] 0.898 0.945 0.661 #> ..$ : num [1:3] 0.0618 0.206 0.1766 x %>% zip_n() %>% zip_n() %>% str() #> List of 3 #> $ :List of 2 #> ..$ x: num 0.266 #> ..$ y: num [1:3] 0.372 0.573 0.908 #> $ :List of 2 #> ..$ x: num 0.202 #> ..$ y: num [1:3] 0.898 0.945 0.661 #> $ :List of 2 #> ..$ x: num 0.629 #> ..$ y: num [1:3] 0.0618 0.206 0.1766
なんということでしょう。zip_n()
を2回適用すると、元の形になっています。
これをうまく使うと、データの方向を入れ変えながら処理を進めていくことができます。たぶん(具体的なユースケースがまだピンときていない...)。
zipの注意点
行と列を入れ変える感覚で使える、と書きましたが、元の情報が失われてしまうパターンもあるので油断は禁物です。今のところ、使ってる中で気づいたのは以下の2つですが、もっと留意事項は多いと思います。
長さが違う場合
ベクトルの長さが違う場合は短い方に合わせられます。特にエラーとかでないので、この挙動にはちょっと注意が必要な気がします。(足りない分はNA
で埋めたりするオプションほしい…)
zip2(1:2, 11:13, .simplify = TRUE) %>% str #> List of 2 #> $ : int [1:2] 1 11 #> $ : int [1:2] 2 12
factorの扱い。
今のところfactorをうまく扱えません。数値変換されてしまいます。
list(x = factor(letters[1:3]), y = factor(LETTERS[1:3])) %>% zip_n #> [[1]] #> [[1]]$x #> [1] 1 #> #> [[1]]$y #> [1] 1 #> #> #> [[2]] #> [[2]]$x #> [1] 2 #> #> [[2]]$y #> [1] 2 #> #> #> [[3]] #> [[3]]$x #> [1] 3 #> #> [[3]]$y #> [1] 3
Issueは上がっているのでそのうち改善されるような気はしますが、けっこう悩ましい問題だと思います。
感想
zipはけっこうR独特の世界ですね。手ごわい。。