R Markdownで楽々レポートづくり

第4回 レポつく自由自在 ~R Markdownにまつわるエトセトラ~

この記事を読むのに必要な時間:およそ 4.5 分

パッケージオプションとフック

R Markdownで.Rmdファイルからレポートを作る際,.Rmdファイル内のRコードを実行して結果を出力する処理自体は{knitr}パッケージにより行われています。{knitr}のパッケージオプションを設定することで,変換処理をカスタマイズできます。ここでは役に立ちそうなパッケージオプションをいくつか紹介します。

パッケージオプションの設定方法

パッケージオプションはセットアップチャンクで設定するのが良いでしょう。opts_knit$set(オプション名 = 値)という書式で設定します。

```{r setup, include=FALSE}
# セットアップチャンク
library(knitr)
opts_knit$set(progress = FALSE) # 変換処理の際のプログレスバーを非表示に
```

という感じです。

aliases

aliasesオプションを設定すると,チャンクオプションのオプション名を簡単に記述できるようになります。例えば

```{r setup, include=FALSE}
# セットアップチャンク
library(knitr)
opts_knit$set(aliases = c(h="fig.height", w="fig.width"))
```

テキスト・・・

```{r iris-plot, h=12, w=12}
plot(iris)
```

とすれば,iris-plotチャンクではfig.height=12, fig.width=12と指定されたことになります。地味機能ですが,記述ミスを減らすのに役立ちます。

チャンクオプションの評価とeval.after

実はRコードチャンクでは,チャンク内のコードだけでなく,チャンクオプションも同様に評価されます。通常はチャンク内のコードが評価される前にチャンクオプションが評価されますが,パッケージオプションeval.afterで指定されたチャンクオプションは,チャンク内のコードが評価された後に評価されるようになります。

意味がよくわからないと思いますので,具体例を考えてみると良いでしょう。例えば,グラフのキャプションに計算内容を入れたいような場合があります。

```{r setup, include=FALSE}
# セットアップチャンク
library(knitr)
opts_knit$set(eval.after = c("fig.cap"))
```

テキスト・・・

```{r cor-plot, fig.cap = paste0('図1: R = ', corr)}
x = rnorm(100)
y = rnorm(100)
corr = cor(x, y)
plot(x, y)
```

このようにすることで,まずチャンク内のコードでcorrが計算されて,その値がfig.capに反映されます。 サンプル出力はこちらです。

global.parによるグラフィックスパラメータのグローバル化

解析処理のレポートには大量のグラフが含まれていることも多いでしょう。Rではマージンや背景色といったグラフのパラメータはpar()関数で設定します。R Markdownの場合,デフォルトではコードチャンクごとにグラフィックスパラメータが初期化されてしまいます。これでは,全てのコードチャンクでpar()による設定を記述する必要があり,効率的ではありません。パッケージオプションglobal.parTRUEとすることで,チャンク間でグラフィックスパラメータの設定を引き継ぐことができるようになります。

```{r setup, include=FALSE}
# セットアップチャンク
library(knitr)
opts_knit$set(global.par = TRUE)
```

```{r}
par(bg  = "pink")
plot(1:10)
```

```{r}
# 上のチャンクのpar()の設定が再利用される
plot(10:1)
```

これで,2つ目のグラフも背景色がピンクで描画されます(悪趣味ですが⁠⁠。サンプル出力はこちらです。

フック

R Markdown(というか裏で走る{knitr}パッケージ)にはフックという強力な機能があります。フックを設定すると,チャンクの動作や出力を思い通りにカスタマイズすることができます。チャンクフックを用いると,チャンクコードの評価の前後に任意の処理を実行できます。アウトプットフックを用いると,チャンクコードの処理結果に対して任意の変換処理を実行できます。

ここでは,各チャンクコードの行数を自動的に出力するようなチャンクフックを紹介します。レポートにコードを載せるのは冗長ですが,せめてコードの行数くらい表示しておけば,あなたがどれだけ頑張って解析コードを書いたのか,レポートを読んだ人にわかってもらえるかもしれません。

```{r setup, include=FALSE}
# セットアップチャンク
library(knitr)
prow = function(before, options, envir) {
  if (!before) {
    paste0(c("<p>コードは", length(options$code), "行です</p>"))
  }
}   
knit_hooks$set(prow = prow) 
    
```

```{r, prow=TRUE, echo=FALSE}
# ?histでのヒストグラム作成例
r <- hist(sqrt(islands), breaks = c(4*0:5, 10*3:5, 70, 100, 140),
          col = "blue1")
text(r$mids, r$density, r$counts, adj = c(.5, -.5), col = "blue3")
sapply(r[2:3], sum)
sum(r$density * diff(r$breaks)) # == 1
lines(r, lty = 3, border = "purple") # -> lines.histogram(*)
```

サンプル出力はこちらです。

まずセットアップチャンクで,フック関数prowを定義し,knit_hooks$setでフック名prowとしてフックに登録しています。次に,コードチャンクではチャンクオプションprow=TRUEとして,このチャンクでprowフックを使うことを指示します。そうすると,このチャンクの評価時にフック関数prowが呼び出されます。フック関数ではoptions引数を使ってチャンクの情報にアクセスすることができます。

この連載ではフックについての解説はいわゆるbeyond scopeですのでこれ以上の説明はやめておきますが,フックを使えばレポートづくりのオートメーション化の自由度は飛躍的に上がります。ただし,オートメーション化のほうにばかり時間を使って,肝心のレポートづくりが進まないのでは意味ないので気をつけましょう。

なお,フックをさらに使いこなしたいという場合は,ドキュメント生成本で詳しく解説されていますので参考にしてください。

著者プロフィール

高橋康介(たかはしこうすけ)

東京大学先端科学技術研究センター特任助教。専門は認知科学,認知心理学,認知神経科学。どうやったら人が幸せな気持ちになれるのか研究中。著書に「R言語上級ハンドブック」(分担執筆・C&R研究所),「ドキュメント・プレゼンテーション生成」(共立出版)。趣味はラテ・アート。