先日、ggplot2の2.2.0がリリースされました。この変更点については以下の記事にまとめました。
で、一点、
これはちょっとわかりづらいので、別途解説を書こうと思います。。
と言ってた宿題があるのでそれをこの記事では書きます。
発端
実は、r-wakalangでの議論から発展して、以下のIssueを報告したのが発端です(すみません...)。
そもそもggplot2のna.rm
とはどういう意味だったのか
例えば、手元のRで?geom_point
と打ってみてください。ヘルプに以下のような記述があるはずです。
na.rm
IfFALSE
, the default, missing values are removed with a warning. IfTRUE
, missing values are silently removed.
...おわかりいただけただろうか。
え、FALSE
でもTRUE
でも欠損値は消えるの??という疑問符が頭の中を駆け巡っていることでしょう。そう、ggplot2においてそもそもna.rm
はNA
を取り除くかどうかをコントロールする引数ではなかったんです。いずれにしてもNA
は取り除かれて、ただ警告が出るかどうかが変わるだけです。この記述は実に2011年から変わっていません。
さて、頭が混乱してきたことだと思いますが、いったん深呼吸しましょう。ここからもう一段階ややこしくなります。
NA
を取り除くか取り除かないかを選びたい
上の記述を見ると常にNA
が取り除かれるように読めますが、実はggplot2の挙動はそうはなっていません。NA
を可能な限りプロットに含めようとします。「可能な限り」というのは、離散値であればNA
を軸のラベルの一つとして扱うことができますが、連続値の場合にはできないからです。
しかし、NA
を軸から取り除きたい場合というのはよくあります。この挙動をコントロールする方法がほしい、というのでできたのがna.translate
引数です。これをTRUE
にすると、NA
を軸のラベルに含めます(これがデフォルト)。FALSE
にするとNA
は無視します。ちなみに、na.translate
は、na.rm
のようにgeom_*()
とかstat_*()
の引数ではなく、scale_*_discrete()
の引数に指定します。「NA
を軸から取り除くか」というのは軸に属する要素だからです。
実際に使ってみましょう。こういう欠損値を含むデータがあるとします。
library(ggplot2) set.seed(1) x_levels <- c("A", "B", NA) df <- tibble::data_frame( x = sample(x_levels, size = 100, replace = TRUE, prob = c(0.7, 0.2, 0.1)), y = rnorm(100) )
これをそのままプロットすると以下のようになります。NA
も軸のひとつとして扱われています。
ggplot(df) + geom_boxplot(aes(x, y))
次に、X軸にna.translate = FALSE
を指定してみましょう。今度は、警告とともにNA
が取り除かれています。
ggplot(df) + geom_boxplot(aes(x, y)) + scale_x_discrete(na.translate = FALSE) #> Warning message: #> Removed 6 rows containing non-finite values (stat_boxplot).
そしてここでおもむろにna.rm = TRUE
を指定してみましょう。なんと...警告が出なくなります。えっ、それだけ...?
ggplot(df) + geom_boxplot(aes(x, y), na.rm = TRUE) + scale_x_discrete(na.translate = FALSE)
はい、それだけです。かつてNA
をすべて消し去るほどの力があると信じられていたna.rm
も、今では警告を消す程度が関の山です。泣けますね。
na.rm
の説明をもう一度読み返してみましょう。
na.rm
IfFALSE
, the default, missing values are removed with a warning. IfTRUE
, missing values are silently removed.
英語力が足りなくて理解できてませんでしたが、これは「NA
を警告あり/なしで取り除くよ」と言ってるのではなくて、「NA
が取り除かれるときに警告が出る/出ないようになるよ」という意味みたいです。うーん、英語難しい...
個人的な感想
そんなわけで、ggplot2でNA
を軸から取り除くためにはscale_x_discrete(na.translate = FALSE)
と打つか、あるいは元データからあらかじめNA
を取り除いておかなくてはいけません。はっきり言って面倒です。
ですが、NA
が黙って消されるとするとそれはそれで危険です。データの探索に上の例のようなコードを打つことはよくあります。もしここで、NA
が無視されてデータにNA
が大量に含まれていることに気づかなければ、NA
が含まれないきれいなデータだと思い込んで分析を進めてしまいかねません。
きれいなグラフを描くのは手間をかければできます。探索的にやるときのために、安全側に倒してなるべく元データの情報を失わないように、汚いデータは汚いグラフになるように、という方針になっているんだと思います。てことで、まあ仕方ないのかなあと個人的には思います。
そうは言ってもna.rm
わかりにくすぎだろ...