2 フック
コードチャンク実行前後のカスタマイズ関数, 出力の調整, チャンクオプションの操作について
オリジナルのページ: https://yihui.org/knitr/hooks/
オリジナルの更新日: 2017-02-03
The object knit_hooks
in the knitr package is used to set hooks; the basic usage is knitr::knit_hooks$set(name = FUN)
(see objects for details) where name
is the name of a chunk option (can be arbitrary), and
FUN
is a function. There are two types of hooks: chunk hooks and output hooks. Hook functions may have different forms, depending what they are designed to do.
knitr パッケージの knit_hooks
オブジェクトはフックの設定に使用します. 基本的には, knitr::knit_hooks$set(name = FUN)
というふうに使います. name
の部分は任意の引数名にすることが可能であり, チャンクオプションの名前を指定します. 詳細はオブジェクト (A章) を見てください. FUN
は関数です. フックには2種類あります. チャンクフックと出力フックです. フック関数は, その設計次第で様々な形態をとります.
2.1 チャンクフック
チャンクフックは対応するチャンクオプションの値が NULL
ではないとき, コードチャンクの前後に実行されます. そしてフック関数には以下のような引数を使用できます.
hook_foo = function(before, options, envir, name, ...) {
if (before) {
## チャンク実行前の処理
} else {
## チャンク実行後の処理
}
}
knitr が文書を処理する時, 各チャンクの直前に hook_foo(before = TRUE)
が呼び出され, チャンク直後に hook_foo(before = FALSE)
が呼び出されます. チャンクがキャッシュされていたり評価しないように設定されていない限り, そのように動作します. そのため, hook_foo(before = FALSE)
はチャンクの後に呼び出されます.
引数の options
は現在のチャンクに設定されたオプション (1章) のリストです. 例えば options$label
は現在のチャンクのラベルです.
引数の envir
はコードチャンクが評価させる環境です.
引数の name
は knit_hooks
のフックが関連付けられた名前です.
全ての引数はオプションです. 例えば, function(before)
も function(options, ...)
フック関数として有効なシグネチャです. フック関数に対応する引数が存在するならば, リスト list(before, options, envir, name)
の値はそれぞれの引数に与えられます.
たとえば, small_mar
というオプションに対してフックを設定したいなら, 以下のようにします.
後の2つの引数はチャンクフックのオプション引数です. 例えば, 次のように small_mar
オプションにフックを設定します.
訳注: マージン調整だけでは違いがわかりづらい, というかこのドキュメントを生成している R Markdown はデフォルトでマージン調整するので, 違いをわかりやすくするため, 背景色を変更する処理も追加しています.
knitr::knit_hooks$set(small_mar = function(before, ...) {
if (before)
par(mar = c(4, 4, 0.1, 0.1)) # 上と右のマージンを狭める
par(bg = "blue") # 背景色を青にする
})
そしてフックに設定した関数はこのように呼び出されます. small_mar
オプションの値に必ず TRUE
を設定する必要はありません. NULL
以外の任意の値さえ与えられていれば動作します.
```{r, myplot, small_mar=TRUE}
hist(rnorm(100), main = '')
```
knitr のフックは出力にテキストを挿入することにも使えます. そのため, このタイプのフック関数は文字列を返す必要があります. この機能はフックの能力を大いに広げます. rgl
パッケージを例に取りましょう. rgl によって生成された 3D グラフを Markdown または HTML 文書に挿入したい時, このタイプのフック関数を考える事になるでしょう. なお, この例よりも洗練された hook_rgl()
関数が rgl
パッケージにあるので参考にしてください.
knitr::knit_hooks$set(rgl = function(before, options, envir) {
if (!before) {
# チャンクコードが評価された後の処理
if (rgl.cur() == 0)
return() # アクティブなデバイスがないかどうか
name = paste0(options$fig.path, options$label, ".png")
rgl.snapshot(name, fmt = "png")
return(paste0("![rgl plot](", name, ")\n"))
}
})
そしてコードチャンクはこのようになります.
```{r, fancy-rgl, rgl=TRUE}
library(rgl) # 用例は ?plot3d から
open3d()
x = sort(rnorm(1000)); y = rnorm(1000); z = rnorm(1000) + atan2(x,y)
plot3d(x, y, z, col = rainbow(1000))
```
Markdown の場合 ![rgl plot](fancy-rgl.png)
と出力されているでしょう.
ここまでの話を要約します.
- フックは
knit_hooks
で,knit_hooks$set(foo = FUN)
という構文で設定されます. - あるチャンクで
foo
というチャンクオプションがNULL
以外の値をとる場合, このフック関数FUN
が実行されます. - フックはチャンクの直前と直後に実行できます.
- フックによって返される文字列は修正が加えられることなく出力ブロックに書き出されます.
さらなる用例は 045-chunk-hook.md (source) を参照してください.
2.2 出力フック
出力フックはチャンクからの生の出力をカスタマイズし洗練するために使います. 様々な種類の出力に対処するための 8つの出力フック関数が存在します.
source
: ソースコードoutput
: 通常の R の出力で, 警告文, メッセージ文, エラー文を除くもの (つまり, 通常の R ターミナルで出力されていたものです)warning
:warning()
による警告文message
:message()
によるメッセージ文error
:stop()
によるエラー文 (コードチャンクとインライン R コードの両方に適用されます)plot
: 出力されるグラフinline
: インライン R コードの出力chunk
: チャンクの全ての出力 (つまりその前のフックにも生み出されたもの)document
: 文書全体の出力 (デフォルトではbase::identity
が適用されます)
これらのフックは全て function(x, options)
という形式をとります (例外として, inline
と document
のフックのみ引数は x
の1つです), x
が出力の文字列で, options
が現在のチャンクのオプションのリストです. 出力フックに関する情報と用例をさらに詳しく知りたい場合はR Markdown クックブックの出力フックの章を参考にしてください.
以下は error
フックの用例になります. これは R Markdown 上で, エラー文に対して追加の整形処理を実行するフックです.
knitr::knit_hooks$set(error = function(x, options) {
paste(c("\n\n:::{style=\"color:Crimson; background-color: SeaShell;\"}",
gsub("^## Error", "**Error**", x), ":::"), collapse = "\n")
})
このようなチャンクでフックの動作をテストします
```{r, error=TRUE}
1 + "a"
```
Error in 1 + “a”: 二項演算子の引数が数値ではありません
デフォルトではチャンクフックは空ですが, 出力フックはデフォルト設定があり, 以下のようにしてリセットできます.
本パッケージは出力を異なる部品にわけてそれぞれにデフォルトのフックを設定し, さらに LaTeX, HTML, Jekyll といった異なる出力フォーマットごとに用意しています.
render_*()
という一連の関数群は, 例えば render_latex()
, redner_html()
, など出力フォーマットごとにそれぞれ異なる, 組み込みの出力フックを提供するためにあります.
出力フックはドキュメント内で設定すべきですが, knitr::knit()
が文書を処理する前にフックを設定したなら render_*()
, たとえば render_markdown()
, render_html()
を最初に呼び出さなければなりません.
hooks_markdown()
などの hooks_*()
関数で, 設定を変えることなくこれらの出力フックにアクセスすることができます.
訳注
R Markdown の場合, 基本的な出力フォーマットにもデフォルトでフックが定義されており, 処理内容によっては予期せぬ結果になることがあるため, 単純な上書きや $restore()
は意図しない動作につながることがあります. 詳細は “R Markdown Cookboox” Ch. 1217 を確認ください.
本パッケージは出力を異なる部品にわけてそれぞれにデフォルトのフックを設定し, さらに LaTeX, HTML, Jekyll といった異なる出力フォーマットごとに用意しています. render_*()
という一連の関数群は, 例えば render_latex()
, redner_html()
, など出力フォーマットごとにそれぞれ異なる, 組み込みの出力フックを提供するためにあります. 出力フックはドキュメント内で設定すべきですが, knitr::knit()
が文書を処理する前にフックを設定したなら render_*()
を先に呼び出す必要があります. この *
には出力フォーマットの名前あてはまります. 例えば render_markdown()
, render_html()
があります. hooks_markdown()
などの hooks_*()
関数で, 設定を変えることなくこれらの出力フックにアクセスすることができます.
以降は各フォーマットでの詳細を記します.
2.2.1 LaTeX: render_latex()
出力ファイルタイプが LaTeX の場合, デフォルトのフックはほとんどのチャンク出力を verbatim
環境で囲んで出力し, inline
出力における数値を指数表記で出力します.
詳細は チャンク出力の制御のデモを参照してください).
plot
, chunk
フックはより複雑な処理をしています.
- デフォルトでは
plot
フックは出力の信頼性を維持するため多くの要因に影響されます.- たとえばグラフィックデバイスが
tikz
ならば,\input{}
コマンドが使われますし18, それ以外では通常は\includegraphics{}
コマンドが使われます. out.width
,out.height
オプションに依存して, フックはグラフのサイズを設定し直します. 例えば\includegraphics[width=.8\textwidth]{file}
のように変更されます. 1つのチャンクに複数のグラフがある場合,fig.show='hold'
を設定するとともに, 複数の画像を適切なサイズで横に並べて表示できるように設定できます (たとえば.45\textwidth
19 とすれば横に2つのグラフを並べられます).- tikz のグラフは
\input{}
で挿入するため, このやり方は正しくありませんが, チャンクオプションresize.width
とresize.height
は複数の tikz グラフを横に並べられます (\resizebox{resize.width}{resize.height}{file.tikz}
という書き方によって. もしいずれかのオプションがNULL
なら!
で置き換えられます. 詳細は LaTeX パッケージのgraphicx
のドキュメントを参照してください). このフック関数によってユーザーは自動レポート生成の全能力を使いこなせます. 単一チャンクの複数グラフとグラフのサイズの設定が可能になるだけでなく, base R のグラフィックスや grid 系のグラフィックス (例: ggplot2), あるいはグリッド系のグラフを並べて表示することもできるためです. この機能がなかったら, R で1つのウィンドウにこういった複数のグラフを1つにまとめるのがどんなに難しいことか考えても見てください20. - グラフのアラインメントを決めるために
fig.align
には (default
,left
,right
,center
) の4つの値が用意され,fig.align='center'
を指定すれば簡単に画像を中央揃えにできます.
- たとえばグラフィックデバイスが
- デフォルトの
chunk
フックは主にチャンクの装飾に使われています.- LaTeX の
framed
パッケージがユーザーの TeX ソフトウェアパッケージ (TeXLive か MikTeX か他の何か21) にインストールされているなら,chunk
フックはカスタマイズした背景色 (デフォルトでは薄灰色) にしたkframe
環境に全ての出力を挿入することで, チャンクの視認性を向上させます (他の地の文よりも強調されますが, とても目立つというほどでもないはずです). - 最後に, 全ての出力が
knitrout
環境で囲まれます. この環境はユーザーが LaTeX で再定義できます.
- LaTeX の
2.2.2 Sweave: render_sweave()
ソースコードを Sinput
環境に挿入し, その出力を Sioutput
環境に挿入し, そしてチャンク全体を Schunk
環境に挿入します. このテーマの使用にはスタイルファイル Sweave.sty
か, 少なくともこれら3つの環境を定義することが必要です.
2.2.3 Listings: render_listings()
Sweave 同様に, Sweavel.sty
が使用されます.
2.2.4 HTML: render_html()
HTML ファイルに書き出すにあたって, フックは出力を自動で調整します. 基本的にチャンクによる出力はクラス付きの div
レイヤーに挿入されます. たとえば, ソースコードは <div class="knitr source"></div>
, チャンク全体は <pre></pre>
に, インラインの出力は <code class="knitr inline"></code>
に書き出されます22.
2.2.5 Markdown: render_markdown()
ソースコードとその出力はスペース4つでインデントされます. GitHub Flavored Markdown のため, ソースコードは ```r
と ```
の間に挿入され, 出力部分は ```
と ```
の間に挿入されます.
2.2.6 Jekyll: render_jekyll()
このサイト23を構築するために, Jykell 用に特別にいくつかのフックを用意する必要がありました. これらは実際かなり単純なものです. R ソースコードはハイライト環境に挿入し言語を r
に設定する, 残りの出力部分は text
言語に設定したハイライト環境 (ほとんど何もハイライトしない) に挿入するだけです. 現在, グラフは Markdown の構文に従って書き出されます.
2.3 オプションフック
他のチャンクオプションの値に応じて別のチャンクオプションの値を変えたいとき, opts_hooks
をつかってそれを実行することがあるかもしれません. オプションフックは対応するチャンクオプションの値が NULL
以外であるときに実行されます. たとえば fig.width
を常に fig.height
以上の値に調整できます.
knitr::opts_hooks$set(fig.width = function(options) {
if (options$fig.width < options$fig.height) {
options$fig.width = options$fig.height
}
options
})
fig.width
は NULL
になることがないため, このフック関数は常にチャンクの直前の, チャンクオプションが確認される前に実行されます. 以下のコードチャンクは上記のフックを設定することで, fig.width
の実際の値は初期値の 5
の代わりに 6
が適用されます.
訳注: knit_hooks
同様に, opts_hooks
にも restore()
メソッドが用意されています.
翻訳版: https://gedevan-aleksizde.github.io/rmarkdown-cookbook/output-hooks.html↩︎
訳注: tikz の画像は LaTeX ソースコードで記述されるため,
\input{}
でテキストファイルとして読み込む必要があります.↩︎訳注: 本文幅の45%↩︎
訳注: 現在は patchwork や cowplot パッケージなどの登場により, そこまで難しいことではなくなりつつあります.↩︎
訳注: あるいは Yihui 氏による TinyTeX が使いやすいでしょう.↩︎
訳注: R Markdown では最終的に出力されるHTMLはさらに pandoc などの処理を経由しているため, これとは異なります↩︎
訳注: オリジナルが掲載されている Yihui 氏のサイトのこと↩︎