13.3 チャンクの実行時間をレポートする

knitr はデフォルトでは knit 処理中にテキストベースの進捗バーを提供します. より正確なチャンクの時間の情報がほしいなら, カスタムチャンクフックを登録して各チャンクの時間を記録することもできます. これはそのようなフックの例です.

knitr::knit_hooks$set(time_it = local({
  now <- NULL
  function(before, options) {
    if (before) {
      # 各チャンクの直前の時刻を記録する
      now <<- Sys.time()
    } else {
      # チャンク直後の時刻との差を計算する
      res <- difftime(Sys.time(), now)
      # 時間を表示するための文字列を返す
      paste("Time for this code chunk to run:", res)
    }
  }
}))

するとこれ以降のチャンクでは, チャンクオプション time_it を使って時間を測定できます. これが例です.

```{r, time_it = TRUE}
Sys.sleep(2)
```

全てのコードチャンクで時間を表示したいなら, もちろん knitr::opts_chunk$set(time_it = TRUE) でグローバルに設定することができます.

上記のフック関数では, さらに詳細な情報をチャンクオプションから出力することもできます. つまりフック関数の options 引数を使います. 例えば, 返り値のチャンクラベルを表示することもできます.

paste("Time for the chunk", options$label, "to run:", res)

あるいはフック関数で時間を表示させずに記録するだけという手もあります.

all_times <- list()  # 全てのチャンクの時間を保存する
knitr::knit_hooks$set(time_it = local({
  now <- NULL
  function(before, options) {
    if (before) {
      now <<- Sys.time()
    } else {
      res <- difftime(Sys.time(), now)
      all_times[[options$label]] <<- res
    }
  }
}))

こうすると all_times オブジェクトで全ての実行時間情報にアクセスすることができます. このオブジェクトはチャンクラベルを名前にもつ名前つきリストで, 各要素の値はそれぞれのチャンクの実行時間です.

最後に技術的な注意事項として, 先ほどのフックで使われた local() 関数に詳しくない人もいるかもしれませんので, これについて説明したいとおもいます. この関数でコードを「ローカルな」環境で実行することができます. その主な恩恵は, コード内で作られた変数はこの環境内のローカルなものになるので, 外部の環境, たいていの場合はグローバル環境を汚染することがないということです. 例えばここでは local() 内で now 変数を作成し, これを time_it 内で使用しています. フック関数内では通常の代入演算子 <- の代わりに二重アロー演算子 <<-now の値を更新しています. <<- は 親環境(ここではあくまでも, local() 環境の内部にある)の変数に代入し, <- は単に現在の環境にのみ値を代入するからというのが理由です. 各コードチャンクが評価される直前に, ローカル変数 now は現在の時刻を記録します. 各コードチャンクが評価されたら現在時刻と now との差を計算します. local() はコード内に渡された最後の値を返しますが, ここではそれがフック関数であることに注意してください. 簡潔に言うなら, local() は, ローカルだけで使われグローバル環境で使われない変数を露出しないことで, ワークスペースをきれいに保つということです. グローバル環境に変数 now が作られても構わなければ, local() を使わないという選択もできます.