如何像源代码('myfile.r')那样获取R Markdown文件?

时间:2012-06-10 03:06:24

标签: r markdown knitr

我经常有一个主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将myfile.rmd的R代码输出到myfile.R

然而,它似乎并不完美:

  • 导致创建额外文件
  • 如果需要控制显示,它需要出现在它自己的代码块中。
  • 它不如简单source(...)那么优雅。

因此我的问题: 是否有更优雅的方式来获取R Markdown文件的R代码?

11 个答案:

答案 0 :(得分:28)

看来你正在寻找单线。把它放在.Rprofile中怎么样?

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

但是,我不明白你为什么想要source() Rmd文件本身的代码。我的意思是knit()将运行本文档中的所有代码,如果您提取代码并在一个块中运行它,那么当您knit()此文档时,所有代码都将运行两次(您自己运行你自己)。这两项任务应该是分开的。

如果您真的想要运行所有代码,那么RStudio已经非常容易了:Ctrl + Shift + R。它基本上会在场景后面调用purl()source()

答案 1 :(得分:16)

将公共代码分解为单独的R文件,然后将该R文件导入到您希望的每个Rmd文件中。

所以举个例子,假设我需要做两份报告,流感爆发和枪支与黄油分析。当然,我会创建两个Rmd文档并完成它。

现在假设老板出现并希望看到流感爆发与黄油价格的变化(控制9毫米弹药)。

  • 复制和粘贴代码以将报告分析到新报告中对于代码重用等是一个坏主意。
  • 我希望它看起来不错。

我的解决方案是将项目纳入这些文件:

  • 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设计的 - &gt; HTML工作流程。如果你选择乳胶或其他任何东西,这将是一个丑陋的混乱。此Rmd文档在全局环境中查找所有source()'ed文件,并在文档末尾包含其源。它包括jquery ui,tablesorter,并设置文档以使用手风琴样式来显示/隐藏源文件。这是一项正在进行的工作,但可以根据自己的用途进行调整。

我知道,不是单行。希望它至少能给你一些想法:)

答案 2 :(得分:3)

可能人们应该开始思考不同。我的问题如下: 将通常在.R文件中的.Rmd块中的每个代码写入.R文件中。 对于你用来编织的Rmd文件,即html,你只剩下了

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

通过这种方式,您可能会创建一堆.R文件,并且您将失去处理所有代码&#34; chunk after chunk&#34;使用ctrl + alt + n(或+ c,但通常这不起作用)。 但是,我读了Gandrud先生关于可重复研究的书,并意识到,他肯定只使用knitr和.Rmd文件来创建html文件。 Main Analysis本身是一个.R文件。 我认为。如果你开始在里面进行整个分析,.Rmd文档会迅速增长。

答案 3 :(得分:2)

如果您只是在代码之后,我认为这些内容应该有效:

  1. 使用readLines
  2. 阅读markdown / R文件
  3. 使用grep查找代码块,搜索以<<<开头的行,例如
  4. 获取包含原始行的对象的子集以仅获取代码
  5. 使用writeLines
  6. 将其转储到临时文件中
  7. 将此文件导入您的R会话
  8. 将其包含在函数中应该可以满足您的需求。

答案 4 :(得分:2)

以下黑客对我很好:

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)
}

答案 5 :(得分:2)

尝试使用knitr的purl功能:

source(knitr::purl("myfile.rmd", quiet=TRUE))

答案 6 :(得分:1)

我建议将主分析和计算代码保存在.R文件中,并根据需要在.Rmd文件中导入块。我已经解释了流程here

答案 7 :(得分:1)

我使用以下自定义功能

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

source_rmd("munge_script.Rmd")

答案 8 :(得分:1)

sys.source(“ ./ your_script_file_name.R”,envir = knitr :: knit_global())

在调用your_script_file_name.R中包含的功能之前,请输入此命令。

在your_script_file_name.R之前添加的“ ./”,以显示指向文件的方向(如果您已经创建了Project)。

您可以查看此链接以获取更多详细信息:https://bookdown.org/yihui/rmarkdown-cookbook/source-script.html

答案 9 :(得分:0)

这对我有用

source("myfile.r", echo = TRUE, keep.source = TRUE)

答案 10 :(得分:0)

我使用这种单线:

```{r optional_chunklabel_for_yourfile_rmd, child = 'yourfile.Rmd'}
```

请参阅: My .Rmd file becomes very lengthy. Is that possible split it and source() it's smaller portions from main .Rmd?