gtableを使ってggplot2のグラフ部分の大きさを揃えてknitrで出力する
論文で使うグラフを作るのにggplot2は大変に強力なのですが、
そのグラフの大きさを指定するにはコツがあります。
問題
私はR Sweave+knitrで論文を書くことが多いのですが、
たとえばchunk optionにfig.height=6, fig.width=6
を設定して
library(ggplot2) g0 <- ggplot(data=iris, aes(x=Sepal.Length, y=Petal.Length, color=Species)) + geom_point() + scale_colour_grey() + theme_classic() print(g0)
したりすると
こんなグラフになります。
一見問題ないように見えるんですが、縦軸と横軸の長さが合ってなかったりするんです。
g0 + theme(legend.position='none')
して凡例を非表示にすると
このようによくわかります。
つまり、凡例があるかないか、あるいはその大きさによって、
グラフ自体の大きさが変わってしまうということです。
普通のレポートならこれでもいいのですが、やはり論文となると、
カッチリ全部揃った形で出力したくなります。
どうするか
結論からいうと、gtableパッケージを使います。 gistにラッパー関数を用意しました。
使い方
grid.newpage() grid.draw(combine_plots(list(g0), T, 6, 1.5))
簡単でしょ?
解説
ひとつひとつ解説していきます。
まず、
grid.newpage()
ですが、自身でgtableオブジェクトを作っていますので手動で呼ぶ必要があります。 (そうしないとグラフが重なって描画されます)
次に、
grid.draw(combine_plots(list(g0), T, 6, 1.5))
ですが、grid.draw()
関数にgtableオブジェクトを渡して描画します。
combine_plots()
関数がそれに当たります。
引数は、
- リスト
- 論理値
- 数字
- 数字
になっています。
まず最初のリストですが、そうです、リストです。
ggplotオブジェクトが格納されたリストを投げます。
実はcombine_plots()
は大きさを揃えるだけではなく、
複数のグラフを結合することもしちゃいます。
次の論理値ですが、最初で渡されたリストを結合する向きを示します。
TRUEで横向きに結合、FALSEで縦に結合します。
今のところは横向きしか対応してないのでFを渡すとエラーを吐きます。
最後の数字二つですが、最初は、グラフ部分の長さ(=高さ=幅)
次が、凡例の幅になっています。
この場合6×6のグラフ+凡例の幅が1.5になるということです。
実践
combine_plots
だけでも便利っちゃ便利なんですが、
このままだと毎回毎回きちんとchunkでfig.width
なりfig.height
なりを指定してあげないといけません。
これはめんどくさい。
なので、自分でデバイスに出力してそれを読み込むことにします。 例のごとくirisを使います。
g1 <- ggplot(data=iris, aes(x=Sepal.Width, y=Petal.Width, color=Species)) + geom_point() + scale_colour_grey() + theme_classic() + theme(legend.position='none') g2 <- ggplot(data=iris, aes(x=Sepal.Width, y=Petal.Length, color=Species)) + geom_point() + scale_colour_grey() + theme_classic() g <- combine_plots(list(g1,g2), T, 6, 1.5) wt <- foreach(wt = g$widths, .combine='+') %do% wt ht<- foreach(ht = g$heights, .combine='+') %do% ht pdf(file='figures/ggplot.pdf', width=wt, height=ht) grid.draw(g) dev.off()
Petal.Widthに対してSepal.WidthとSepal.Lengthから相関があるか調べます。
このように、二つのグラフの大きさは揃えながら、一つだけ凡例を表示するなんかもできます。
あとはこれを<img>
タグなり\begin{figure}
なりしてやればよいということになります。
しかしそれも手動でいろいろ指定するのはめんどくさいです。
ここまで来たらそれも自動化しましょう。
埋め込み自動化
latex_draw <- function(identifier, caption="", landscape = TRUE, fig.pos='!hp'){ t <- paste0("\\begin{figure}[", fig.pos, "]\n\\caption{", caption, "\\label{fig:", identifier, "}}\n\\includegraphics[width=\\maxwidth]{figures/", identifier, ".pdf}\n\\end{figure}") t <- ifelse(landscape == TRUE, paste("\\begin{landscape}\n", t, "\n\\end{landscape}"), t) cat(t) }
このような関数を定義します。
(ファイル名もpaste0()
を使って全部関数化しておくとさらに便利です。)
横方向に結合したグラフは普通のレポートでは見難いので、pdflscapeパッケージを使います。
プリアンブルに\usepackage{pdflscape}
を追加し、
chunkにresults='asis'
を指定した上で、
latex_draw('ggplot', 'Patal.Width against Sepal')
すると、この通り。
快適な論文ライフを!