web-dev-qa-db-ja.com

`source( 'myfile.r')`のようなR Markdownファイルをソースするには?

私は、メインのR Markdownファイルまたはknitr LaTeXファイルをよく持っており、そこでsource他のRファイル(たとえば、データ処理用)があります。ただし、場合によっては、これらのソースファイルを独自の再現可能なドキュメントにすることが有益だと考えていました(データ処理用のコマンドを含むだけでなく、データ処理を説明する再現可能なドキュメントを作成するR Markdownファイル決定)。

したがって、メインのR Markdownファイルにsource('myfile.rmd')のようなコマンドが必要です。 _myfile.rmd_のRコードチャンク内のすべてのRコードを抽出およびソースします。もちろん、これによりエラーが発生します。

次のコマンドが機能します。

_```{r message=FALSE, results='hide'}
knit('myfile.rmd', tangle=TRUE)
source('myfile.R')
```
_

出力が必要な場合は_results='hide'_を省略できます。つまり、knitrはRコードを_myfile.rmd_から_myfile.R_に出力します。

ただし、完璧ではないようです。

  • 余分なファイルが作成されます
  • 表示の制御が必要な場合は、独自のコードチャンクに表示する必要があります。
  • 単純なsource(...)ほどエレガントではありません。

したがって、私の質問:R MarkdownファイルのRコードを調達するよりエレガントな方法はありますか?

71
Jeromy Anglim

ワンライナーを探しているようです。これを_.Rprofile_に入れてみませんか?

_ksource <- function(x, ...) {
  library(knitr)
  source(purl(x, output = tempfile()), ...)
}
_

ただし、Rmdファイル自体のコードをsource()したい理由がわかりません。つまり、knit()はこのドキュメントのすべてのコードを実行し、コードを抽出してチャンクで実行すると、このドキュメントをknit()するときにすべてのコードが2回実行されます(あなた自身の中に自分自身を実行します)。 2つのタスクは別々にする必要があります。

すべてのコードを本当に実行したい場合、RStudioはこれをかなり簡単にしました:_Ctrl + Shift + R_。基本的に、purl()source()を背後で呼び出します。

29
Yihui Xie

共通コードを個別のRファイルに分解し、そのRファイルを必要な各Rmdファイルに取り込みます。

たとえば、インフルエンザアウトブレイクと銃対バター分析という2つのレポートを作成する必要があるとします。当然、2つのRmdドキュメントを作成し、それで完了します。

今度は、ボスがやって来て、インフルエンザの発生とバターの価格の変動(9mmの弾薬を制御する)を見たいとします。

  • コードをコピーして貼り付けてレポートを分析し、新しいレポートを作成することは、コードの再利用などには不適切です.
  • 見た目がいいです。

私の解決策は、プロジェクトをこれらのファイルに含めることでした。

  • Flu.Rmd
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • guns_data_import.R
    • butter_data_import.R

各Rmdファイル内には次のようなものがあります。

```{r include=FALSE}
source('flu_data_import.R')
```

ここでの問題は、再現性が失われることです。私の解決策は、各Rmdファイルに含める共通の子ドキュメントを作成することです。したがって、私が作成するすべてのRmdファイルの最後に、これを追加します。

```{r autodoc, child='autodoc.Rmd', eval=TRUE}
``` 

そして、もちろん、autodoc.Rmd:

Source Data & Code
----------------------------
<div id="accordion-start"></div>

```{r sourcedata, echo=FALSE, results='asis', warnings=FALSE}

if(!exists(autodoc.skip.df)) {
  autodoc.skip.df <- list()
}

#Generate the following table:
for (i in ls(.GlobalEnv)) {
  if(!i %in% autodoc.skip.df) {
    itm <- tryCatch(get(i), error=function(e) NA )
    if(typeof(itm)=="list") {
      if(is.data.frame(itm)) {
        cat(sprintf("### %s\n", i))
        print(xtable(itm), type="html", include.rownames=FALSE, html.table.attributes=sprintf("class='exportable' id='%s'", i))
      }
    }
  }
}
```
### Source Code
```{r allsource, echo=FALSE, results='asis', warning=FALSE, cache=FALSE}
fns <- unique(c(compact(llply(.data=llply(.data=ls(all.names=TRUE), .fun=function(x) {a<-get(x); c(normalizePath(getSrcDirectory(a)),getSrcFilename(a))}), .fun=function(x) { if(length(x)>0) { x } } )), llply(names(sourced), function(x) c(normalizePath(dirname(x)), basename(x)))))

for (itm in fns) {
  cat(sprintf("#### %s\n", itm[2]))
  cat("\n```{r eval=FALSE}\n")
  cat(paste(tryCatch(readLines(file.path(itm[1], itm[2])), error=function(e) sprintf("Could not read source file named %s", file.path(itm[1], itm[2]))), sep="\n", collapse="\n"))
  cat("\n```\n")
}
```
<div id="accordion-stop"></div>
<script type="text/javascript">
```{r jqueryinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://code.jquery.com/jquery-1.9.1.min.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r tablesorterinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://tablesorter.com/__jquery.tablesorter.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r jqueryuiinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://code.jquery.com/ui/1.10.2/jquery-ui.min.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r table2csvinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(file.path(jspath, "table2csv.js")), sep="\n")
```
</script>
<script type="text/javascript">
  $(document).ready(function() {
  $('tr').has('th').wrap('<thead></thead>');
  $('table').each(function() { $('thead', this).prependTo(this); } );
  $('table').addClass('tablesorter');$('table').tablesorter();});
  //need to put this before the accordion stuff because the panels being hidden makes table2csv return null data
  $('table.exportable').each(function() {$(this).after('<a download="' + $(this).attr('id') + '.csv" href="data:application/csv;charset=utf-8,'+encodeURIComponent($(this).table2CSV({delivery:'value'}))+'">Download '+$(this).attr('id')+'</a>')});
  $('#accordion-start').nextUntil('#accordion-stop').wrapAll("<div id='accordion'></div>");
  $('#accordion > h3').each(function() { $(this).nextUntil('h3').wrapAll("<div>"); });
  $( '#accordion' ).accordion({ heightStyle: "content", collapsible: true, active: false });
</script>

N.B.、これはRmd-> htmlワークフロー用に設計されています。ラテックスなどを使用すると、これはい混乱になります。このRmdドキュメントは、すべてのsource()されたファイルのグローバル環境を調べ、ドキュメントの最後にそれらのソースを含めます。 jquery ui、tablesorterが含まれ、ソースファイルの表示/非表示にアコーディオンスタイルを使用するようにドキュメントを設定します。進行中の作業ですが、ご自身の用途に合わせて自由に調整してください。

ワンライナーではありません。少なくともいくつかのアイデアが得られることを願っています:)

17
Keith Twombley

おそらく、違う考えを始めるべきです。私の問題は次のとおりです。通常、.Rファイルの.Rmdチャンクにあるはずのすべてのコードを記述します。そして、htmlを編むために使用するRmdドキュメントについては、

```{R Chunkname, Chunkoptions}  
source(file.R)  
```

この方法では、おそらく多数の.Rファイルを作成し、ctrl + alt + n(または+ c、ただし通常は機能しません)を使用してすべてのコードを「チャンク後チャンク」で処理する利点を失います。しかし、私はGandrud氏による再現性のある研究に関する本を読んで、htmlファイルを作成するためだけにknitrファイルと.Rmdファイルを使用していることに気付きました。メイン分析自体は.Rファイルです。内部ですべての分析を開始すると、.Rmdドキュメントは急速に大きくなりすぎると思います。

3
Pharcyde

あなたがコードの直後なら、これらの行に沿って何かが機能するはずです:

  1. readLinesを使用してmarkdown/Rファイルを読み取ります
  2. grepを使用してコードチャンクを見つけ、たとえば<<<で始まる行を検索します
  3. 元の行を含むオブジェクトのサブセットを取得して、コードのみを取得します
  4. writeLinesを使用してこれを一時ファイルにダンプします
  5. このファイルをRセッションに取り込みます

これを関数でラップすると、必要なものが得られます。

2
Paul Hiemstra

次のハックは私のためにうまくいきました:

library(readr)
library(stringr)
source_rmd <- function(file_path) {
  stopifnot(is.character(file_path) && length(file_path) == 1)
  .tmpfile <- tempfile(fileext = ".R")
  .con <- file(.tmpfile) 
  on.exit(close(.con))
  full_rmd <- read_file(file_path)
  codes <- str_match_all(string = full_rmd, pattern = "```(?s)\\{r[^{}]*\\}\\s*\\n(.*?)```")
  stopifnot(length(codes) == 1 && ncol(codes[[1]]) == 2)
  codes <- paste(codes[[1]][, 2], collapse = "\n")
  writeLines(codes, .con)
  flush(.con)
  cat(sprintf("R code extracted to tempfile: %s\nSourcing tempfile...", .tmpfile))
  source(.tmpfile)
}
2
qed

メインの分析および計算コードを.Rファイルに保持し、必要に応じて.Rmdファイルにチャンクをインポートすることをお勧めします。プロセスを説明しました こちら

1
pbahr

次のカスタム関数を使用します

source_rmd <- function(rmd_file){
  knitr::knit(rmd_file, output = tempfile())
}

source_rmd("munge_script.Rmd")
0
Joe