ごん(R)、お前だったのか...ベクトルをリサイクルしてくれていたのは...
初めに
どうもです。最近はガルパンを見ています。
(カチューシャが頭から離れない)
(最終章第二話面白かったです)
生きていると、R上でのベクトルのリサイクルを感じたいですよね。
今日は、Rで起こるベクトルリサイクルの性質と留意すべきポイントについてのお話です
こんなミス知らないうちにやっていそう....
結論
- Rは論理式でベクトル評価をするとき、両ベクトルの長さが等しくないと短い方のベクトルを長いベクトルの要素数と等しくなるまでリサイクルする(論理式だけじゃないかも...)
- 上述のため、エラーは返されないが予期せぬアウトプットになっているかもしれないことを留意せよ
現象
> sessionInfo() R version 3.6.0 (2019-04-26) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Ubuntu 18.04.2 LTS > library(tidylog) # library(tidyverse)でも大丈夫 data("diamonds") # A tibble: 53,940 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 4 0.290 Premium I VS2 62.4 58 334 4.2 4.23 2.63 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48 7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47 8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53 9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49 10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39 # … with 53,930 more rows
今回は53940行あるdiamonds
データを使います
みんなのR[第二版]の第12章「dplyrによる高速なグルーピング処理」を読んでいたときのこと。
filter
について、条件式を複数用いて抽出する際の書き方として論理和(積)を使う方法と%in%
(in演算子)を使う方法が書かれています。
参考までに↓↓↓
2つをそれぞれ書いてみると
> diamonds %>% filter(cut %in% c("Ideal", "Good")) filter: removed 27483 out of 53940 rows (51%) # A tibble: 26,457 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 # … with 26,447 more rows > diamonds %>% filter(cut == "Ideal" | cut == "Good") filter: removed 27483 out of 53940 rows (51%) # A tibble: 26,457 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 # … with 26,447 more rows
という感じになります。
どちらも残っている要素は26457となっています。
ここでin演算子について「え?いらなくね?」と考えた愚かな私。
試しに書いてみると
> diamonds %>% filter(cut == c("Ideal", "Good")) filter: removed 40711 out of 53940 rows (75%) # A tibble: 13,229 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 # … with 13,219 more rows
行けたわ...
あり?要素が13229になってる。大体半分くらい?
どういうことだってばよ?
説明
ある塾で聞いてみると、
「基本的に、2つのベクトルオブジェクト(A
, B
)があって、それをマッチさせるような処理( 例えば A + B
とか A == B
など)をする場合、かつその二つの長さが違う場合には、R言語は短い方のベクトルをリサイクルして長さを合わせようとします。」
という解を得られました。
記号の書き方と集合の問題ですね。
in演算子は、要素がベクトルに属するか否かを返すものです。
対して、filter(cut == c("Ideal", "Good"))
という書き方は、
元のdaiamonds
データが53940行要素あるのに対して、c("Ideal", "Good")
の2つの要素しかありません。
他の言語ではわかりませんが、
Rでは論理式を評価するときにどちらかのベクトルが短いと短い方をリサイクルして長さを合わせようとするのです。
つまり、"Ideal", "Good"
を26970回リサイクルして53940要素に増やすのです。
感動....!!!
例えば、diamonds %>% filter(cut == "Ideal")
と書いたとき、R上では
> diamonds %>% slice(1:10) %>% select(carat,cut,color) %>% + mutate(cut_check = rep("Ideal",10), + answer = cut_check == cut) # A tibble: 10 x 5 carat cut color cut_check answer <dbl> <ord> <ord> <chr> <lgl> 1 0.23 Ideal E Ideal TRUE 2 0.21 Premium E Ideal FALSE 3 0.23 Good E Ideal FALSE 4 0.290 Premium I Ideal FALSE 5 0.31 Good J Ideal FALSE 6 0.24 Very Good J Ideal FALSE 7 0.24 Very Good I Ideal FALSE 8 0.26 Very Good H Ideal FALSE 9 0.22 Fair E Ideal FALSE 10 0.23 Very Good H Ideal FALSE
こんな感じで、Ideal
という単一要素をデータ行分リサイクルしています。
ごん、お前だったのか...ベクトルをリサイクルしてくれていたのは...
そして、TRUEだけを抽出しています。
(cut_check
とanswer
を擬似的にたした)
ということはdiamonds %>% filter(cut == c("Ideal", "Good"))
では、
> diamonds %>% slice(1:10) %>% select(carat,cut,color) %>% + mutate(cut_check = rep(c("Ideal","Good"),5), + answer = cut_check == cut) # A tibble: 10 x 5 carat cut color cut_check answer <dbl> <ord> <ord> <chr> <lgl> 1 0.23 Ideal E Ideal TRUE 2 0.21 Premium E Good FALSE 3 0.23 Good E Ideal FALSE 4 0.290 Premium I Good FALSE 5 0.31 Good J Ideal FALSE 6 0.24 Very Good J Good FALSE 7 0.24 Very Good I Ideal FALSE 8 0.26 Very Good H Good FALSE 9 0.22 Fair E Ideal FALSE 10 0.23 Very Good H Good FALSE
c("Ideal", "Good")
を元データに足りるまで繰り返し続けるわけです(暁美ほむら?)
53940は3の倍数でもあるので
> diamonds %>% filter(cut == c("Ideal", "Good", "premium")) filter: removed 45079 out of 53940 rows (84%) # A tibble: 8,861 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 3 0.3 Good J SI1 64 55 339 4.25 4.28 2.73 4 0.31 Good H SI1 64 54 402 4.29 4.31 2.75 5 0.33 Ideal I SI2 61.8 55 403 4.49 4.51 2.78 6 0.26 Good D VS1 58.4 63 403 4.19 4.24 2.46 7 0.23 Ideal G VS1 61.9 54 404 3.93 3.95 2.44 8 0.35 Ideal I VS1 60.9 57 552 4.54 4.59 2.78 9 0.3 Ideal D SI1 62.1 56 552 4.3 4.33 2.68 10 0.32 Ideal I VVS1 62 55.3 553 4.39 4.42 2.73 # … with 8,851 more rows
いけます。更に要素数が減っています。
53940では割り切れない7にしてみると
> diamonds %>% filter(cut == c("Ideal", "Good", "premium","umr","kirie","ebina","Shilfinford")) filter: removed 50186 out of 53940 rows (93%) # A tibble: 3,754 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.23 Good E VS1 64.1 59 402 3.83 3.85 2.46 3 0.26 Good D VS1 58.4 63 403 4.19 4.24 2.46 4 0.3 Ideal D SI1 62.1 56 552 4.3 4.33 2.68 5 0.75 Ideal G SI1 62.2 55 2760 5.87 5.8 3.63 6 0.8 Ideal F SI2 59.9 59 2762 6.01 6.07 3.62 7 0.580 Ideal F VVS1 61.7 56 2772 5.33 5.37 3.3 8 0.71 Good E VS2 59.2 61 2772 5.8 5.88 3.46 9 0.72 Ideal G SI1 61.8 56 2776 5.72 5.75 3.55 10 0.71 Good F VS2 57.8 60 2777 5.87 5.9 3.4 # … with 3,744 more rows 警告メッセージ: 1: `==.default`(cut, c("Ideal", "Good", "premium", "umr", "kirie", で: 長いオブジェクトの長さが短いオブジェクトの長さの倍数になっていません 2: is.na(e1) | is.na(e2) で: 長いオブジェクトの長さが短いオブジェクトの長さの倍数になっていません
いや、いけるのかい!!!
でも、警告メッセージがしっかり出ていますね。
今回は、filter()
で行いましたが、subset()でも
当然起きます。
このように、今回はRの親切心について触れることができました。
間違った書き方でも評価はされてしますので、%in%
を使うことをお忘れないように(自戒)