StatsFragments

Python, R, Rust, 統計, 機械学習とか

R {ggplot2} で 少し複雑なサブプロットが描きたい

この記事は R Advent Calendar 2015 4 日目の記事です。


{ggplot2} でのサブプロット

{ggplot2} でサブプロットを描画したいことがある。同じ種類のプロットを水準別に描画するなど、単純なものであれば facet で描ける。例えば 適当な散布図を Species 別に描きたい場合、

library(dplyr)
library(ggplot2)

ggplot(iris, aes(Sepal.Width, Sepal.Length)) + 
  geom_point() + 
  facet_wrap(~ Species)

f:id:sinhrks:20151204000924p:plain

facet では異なる種類のプロットをサブプロットとすることはできない。例えば散布図の縦軸/横軸をそれぞれ変えたい、といった場合には gridExtra::grid.arrange を使う。

library(gridExtra)
p1 <- ggplot(iris, aes(Sepal.Width, Sepal.Length)) + geom_point()
p2 <- ggplot(iris, aes(Petal.Width, Petal.Length)) + geom_point()
p3 <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()
p4 <- ggplot(iris, aes(Sepal.Width, Petal.Width)) + geom_point()
grid.arrange(p1, p2, p3, p4)

f:id:sinhrks:20151204000934p:plain

が、grid.arrange は引数を ドット ... で受け取ること、また呼び出すと直ちに描画を行うことから、以下のような場合は少し面倒だ。

  • 描画が直ちに行われるため、サブプロットの描画設定をまとめて変更できない。単一のテーマを使う場合でも、個々のプロットそれぞれについてあらかじめ処理を行っておく必要がある。
  • リストが渡せない。サブプロットの数を動的に変更したい場合は、リストに入れて do.call が必要である。

というわけで、自作パッケージ {ggfortify} でこの辺の操作を少し便利にできるようにした。サブプロット周りの処理は ggmultiplot クラスで行う。

コンストラクタの引数にはリストが渡せるため、サブプロット数が変わる場合でも楽だと思う。また、インスタンスに対して + 演算子を用いることで ggplot と同じ処理を各プロットに対して適用できる。

# install.packages('ggfortify')
library(ggfortify)
p <- new('ggmultiplot', plots = list(p1, p2, p3, p4))
p
# 略。上記 grid.arrange と同じ出力

# theme_bw を全サブプロットに適用
p + theme_bw()

f:id:sinhrks:20151204000945p:plain

サブプロットの要素には リストと同じようにアクセスできる。[ でのアクセスは ggmultiplot インスタンスに、[[ での アクセスは ggplot インスタンスになる。代入もできるため、一部のプロットにだけ処理を適用したい場合は以下のように書ける。

p[2:3] <- p[2:3] + stat_smooth(method = 'lm')
p

f:id:sinhrks:20151204000953p:plain

+ 演算子の右項に ggplot インスタンスを指定するとサブプロットが追加できる。

p + (ggplot(iris, aes(Sepal.Width, fill = Species)) + geom_density())

f:id:sinhrks:20151204002618p:plain

ggmultiplot 作成時に ncol もしくは nrow キーワードを指定することで、サブプロットの列数 / 行数が指定できる。

p <- new('ggmultiplot', plots = list(p1, p2, p3, p4), ncol = 3)
p

f:id:sinhrks:20151204002529p:plain

autoplot との組み合わせ

{ggfortify} を使うと主要なクラスを ggplot2::autoplot 関数で描画できるようになる。autoplot にリストを渡した場合は、リストの各要素が autoplot 可能であればそれらをサブプロットとして描画する。

サンプルとして、{broom} パッケージの vignettes にある例をやってみたい。ここでは、適当なデータに対して クラスタ数を変えながら kmeans クラスタリングを行った結果をプロットしている。

これは autoplot を使って以下のように書ける。

kclusts <- data.frame(k=1:9) %>% 
  dplyr::group_by(k) %>%
  dplyr::do(kclust = kmeans(iris[-5], .$k))
autoplot(kclusts$kclust, data = iris[-5], ncol = 3) + theme(legend.position = "none")

f:id:sinhrks:20151204001005p:plain

一行目は今なら {purrr} を使ったほうが簡潔でわかりやすくなると思う。

library(purrr)
kclusts <- purrr::map(1:9, ~ kmeans(iris[-5], .))
autoplot(kclusts, data = iris[-5], ncol = 3) + theme(legend.position = "none")
# 略

まとめ

{ggfortify} では ggmultiplot クラス、ならびに autoplot を利用してサブプロットを簡単に扱うことができる。

Rグラフィックスクックブック ―ggplot2によるグラフ作成のレシピ集

Rグラフィックスクックブック ―ggplot2によるグラフ作成のレシピ集