以下の文章はpatchworkパッケージ(thomasp85/patchwork: The Composer of ggplots)のREADMEの翻訳に少し解説を追加したものです。
patchwork
patchwork
はggplot2
により作成された複数のプロットをめちゃ簡単に組み合わせられるようにすることを目標として開発されているパッケージだ。
同様の目的のパッケージ、関数として、gridExtra::grid.arrange()
やcowplot::plot_grid()
があるが、これらで上手くやろうとすると試行錯誤が結構必要となる。
これに対し、patchwork
では演算子を使った直感的な記法でプロットを組み合わせられるので、より簡単で可読性に優れた記述が可能となる。
インストール
現在はCRANからインストールできる。
# devtools::install_github("thomasp85/patchwork")
install.packages("patchwork")
簡単な例
patchworkでプロットを組み合わせるのはとても簡単だ。足せばいい。つまり、+
でプロットを組み合わせるのだ。
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
p1 + p2
もちろん、プロットをオブジェクトに代入せずに直接組み合わせても同じ結果が得られる。
ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear))
細かい調整の方法ももちろんある。plot_layout()
を足すことで、縦横の配置方法や、行や列それぞれに割り当てる領域を好きなように指定できる。
p1 + p2 +
plot_layout(ncol = 1, heights = c(3, 1))
もしスペースが欲しければ、plot_spacer()
を使用すれば良い。
p1 + plot_spacer() + p2
ブレース(波括弧)でくくることで、ネストされたレイアウトを作成することもできる。この場合、plot_layout()
は適用された階層を理解し、それぞれ別々に機能する。
p3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
p4 <- ggplot(mtcars) + geom_bar(aes(carb))
p4 + {
p1 + {
p2 + p3 + plot_layout(ncol = 1)
}
} + plot_layout(ncol = 1)
もう少し進んだ解説
patchwork
の定義する演算子は他にもある。-
は+
と似たように動作するが、演算子の左側全体のネストレベルと右側のネストレベルが対等であるように結合する。以下の例では、{p1 + p2}
と、p3
のネストレベルが等しくなる(+
では、p1
、p2
、p3
のネストレベルが等しくなる)。
p1 + p2 - p3 + plot_layout(ncol = 1)
patchwork
はさらに可読性に優れた演算子を2つ用意している。複数のプロットのレイアウトを考えるとき、主な興味の対象となる事項はどのプロットとどのプロットが隣り合わせになるか、上下になるか、といったことだろう。|
は横方向の結合に、/
は縦方向の結合に使える演算子である。これを使うと、非常に直感的なシンタックスでレイアウトを記述できる。
(p1 | p2 | p3) /
p4
また、主に繰り返しを減らすという目的のために、さらに2つの演算子を定義している。レイアウトのパーツとなっているプロット全てに同じ変更(例えばthemeの変更)を行いたいとしよう。このとき、それぞれのプロットに同じ変更をする代わりに、&
または*
を使ってまとめて変更を適用できる。
この2つの演算子の機能は似ているが、*
は同じネストレベルにしか作用しないという点で異なる。例えば、以下の例ではp2
とp3
にはthemeが適用されない。
(p1 / (p2 | p3) / p4) * theme_bw()
&
を使えば、再帰的に全てのプロットに変更を適用できる。
(p1 / (p2 | p3) / p4) & theme_bw()
その他の関数について
以下はREADME.mdに記載されていないもの(のうち、私が使い道をある程度理解できたもの)を紹介する。
wrap_plots()
演算子を使ったプロットの結合は、プロットの総数がわかっていないとできない。任意の個数のプロットを結合したい場合に、wrap_plots()
は利用できる。
任意個のプロットを並べて指定してもいいし、
wrap_plots(p1, p2, p3, p4)
リストでまとめて渡してもいい。
plots <- list(p1, p2, p3, p4)
wrap_plots(plots)
結果は次のようになる。
…が、cowplot::plot_grid(plotlist = plots)
とやってもほぼ同様の結果が得られるので、この関数だけの利点というのはよく分からなかった。返ってくるのはggassemble
クラスのオブジェクトになるので、patchworkでさらに何かする場合には役に立つ場面があるのかもしれない。
patchworkGrob()
今上で少し触れたが、patchworkの演算子による操作で作られるオブジェクトはggassemble
というクラスになっている。
class(p1 + p2)
## [1] "ggassemble" "gg" "ggplot"
patchworkGrob()
はggassemble
クラスのオブジェクトからgrobを生成するもので、ggplot2
におけるggplotGrob()
の役割を担っている。
class(patchworkGrob(p1 + p2))
## [1] "gtable" "gTree" "grob" "gDesc"
plot_annotation()
この関数はplot_layout()
と同じようにggassemble
クラスのオブジェクトに+
演算子で追加することで効果を発揮する。
plot_layout()
と違うのは、この関数はトップレベルの階層に影響を及ぼす、という点である。関数名や引数が示唆するように、この関数はタイトルやキャプション、各図のラベルなど、最終的な注釈の記入に使うためのものである。
(p1 + p2) / p3 +
plot_annotation(
title = "Title",
subtitle = "Subtitle",
caption = "Caption",
tag_levels = "A",
tag_prefix = "fig ",
tag_suffix = ":"
)