LoginSignup
84
79

More than 3 years have passed since last update.

patchworkを使って複数のggplotを組み合わせる

Last updated at Posted at 2018-07-25

以下の文章はpatchworkパッケージ(thomasp85/patchwork: The Composer of ggplots)のREADMEの翻訳に少し解説を追加したものです。

patchwork

patchworkggplot2により作成された複数のプロットをめちゃ簡単に組み合わせられるようにすることを目標として開発されているパッケージだ。

同様の目的のパッケージ、関数として、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

unnamed-chunk-1-1.png

もちろん、プロットをオブジェクトに代入せずに直接組み合わせても同じ結果が得られる。

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))

unnamed-chunk-3-1.png

もしスペースが欲しければ、plot_spacer()を使用すれば良い。

p1 + plot_spacer() + p2

unnamed-chunk-4-1.png

ブレース(波括弧)でくくることで、ネストされたレイアウトを作成することもできる。この場合、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)

unnamed-chunk-5-1.png

もう少し進んだ解説

patchworkの定義する演算子は他にもある。-+と似たように動作するが、演算子の左側全体のネストレベルと右側のネストレベルが対等であるように結合する。以下の例では、{p1 + p2}と、p3のネストレベルが等しくなる(+では、p1p2p3のネストレベルが等しくなる)。

p1 + p2 - p3 + plot_layout(ncol = 1)

unnamed-chunk-6-1.png

patchworkはさらに可読性に優れた演算子を2つ用意している。複数のプロットのレイアウトを考えるとき、主な興味の対象となる事項はどのプロットとどのプロットが隣り合わせになるか、上下になるか、といったことだろう。|は横方向の結合に、/は縦方向の結合に使える演算子である。これを使うと、非常に直感的なシンタックスでレイアウトを記述できる。

(p1 | p2 | p3) / 
      p4

unnamed-chunk-7-1.png

また、主に繰り返しを減らすという目的のために、さらに2つの演算子を定義している。レイアウトのパーツとなっているプロット全てに同じ変更(例えばthemeの変更)を行いたいとしよう。このとき、それぞれのプロットに同じ変更をする代わりに、&または*を使ってまとめて変更を適用できる。

この2つの演算子の機能は似ているが、*は同じネストレベルにしか作用しないという点で異なる。例えば、以下の例ではp2p3にはthemeが適用されない。

(p1 / (p2 | p3) / p4) * theme_bw()

unnamed-chunk-8-1.png

&を使えば、再帰的に全てのプロットに変更を適用できる。

(p1 / (p2 | p3) / p4) & theme_bw()

unnamed-chunk-9-1.png

その他の関数について

以下はREADME.mdに記載されていないもの(のうち、私が使い道をある程度理解できたもの)を紹介する。

wrap_plots()

演算子を使ったプロットの結合は、プロットの総数がわかっていないとできない。任意の個数のプロットを結合したい場合に、wrap_plots()は利用できる。

任意個のプロットを並べて指定してもいいし、

wrap_plots(p1, p2, p3, p4)

リストでまとめて渡してもいい。

plots <- list(p1, p2, p3, p4)
wrap_plots(plots)

結果は次のようになる。

unnamed-chunk-10-1.png

…が、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 = ":"
  )

unnamed-chunk-13-1.png

84
79
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
84
79