如何获取R Markdown文件,例如`source('myfile.r')`?


89

我经常有一个主R Markdown文件或knitr LaTeX文件,其中source还有一些其他R文件(例如,用于数据处理)。但是,我认为在某些情况下,使这些源文件成为其自己的可复制文档(例如,R Markdown文件不仅包含用于数据处理的命令,而且还会生成可解释文档以解释数据处理决定的文档)将是有益的。 )。

因此,我想source('myfile.rmd')在主R Markdown文件中有一个命令。这将提取并获取的R代码块内的所有R代码myfile.rmd。当然,这会引起错误。

以下命令有效:

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

results='hide'如果需要输出,可以省略。即,从knitr输出的R代码myfile.rmdmyfile.R

但是,这似乎并不完美:

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

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


我实际上很难理解您的问题(我读了几次)。您可以将其他R脚本轻松地来源到Rmd文件中。但是您还想将其他markdown文件中的源文件转换成要编织的文件吗?
Maiasaura 2012年

4
我想在R Markdown文件(即* .rmd)的R代码块中获取R代码吗?我对问题进行了一些编辑,以使事情变得更清楚。
Jeromy Anglim 2012年

include乳胶里的东西。如果markdown支持包含其他markdown文档,则创建此类功能应该相对容易。
Paul Hiemstra 2012年

@PaulHiemstra我想源文本和R代码块的功能也将很有用。我特别考虑仅在R Markdown文档中采购代码。
Jeromy Anglim 2012年

Answers:


35

看来您正在寻找单线。把这个放进你的.Rprofile呢?

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

但是,我不明白为什么要source()Rmd文件本身中的代码。我的意思是knit()将运行该文档中的所有代码,并且如果您提取代码并将其分批运行,则在您knit()编写本文档时,所有代码将运行两次(在您自己内部运行)。这两个任务应该分开。

如果您真的想运行所有代码,则RStudio使此操作相当简单:Ctrl + Shift + R。它基本上调用purl()source()幕后。


8
@Yihui,您好,我认为这很有用,因为有时您的分析可能以小的脚本形式进行组织,但是在您的报告中,您希望拥有整个管道的代码。
lucacerone

9
因此,这里的用例是您要编写所有代码,并对其进行大量记录和解释,但是该代码由其他脚本运行。
垃圾平衡

4
@BrashEquilibrium这是使用source()knitr::knit()运行代码的问题。我知道人们对后者不太熟悉,但purl()并不可靠。您已被警告:github.com/yihui/knitr/pull/812#issuecomment-53088636
Xie

5
@Yihui您认为'source(purl(x,...))'的替代方案是什么?一个源可以如何多个* .Rmd-Files,而不会出现关于重复的块标签的错误?我不想回到原始文档并编写它。我将* .Rmd用于许多文件,而我可能必须将其导出并与他人讨论,因此,能够为分析的所有步骤提供多个Rmd-Files将是很棒的。
stats-hb

当knitr呈现.rmd文件时,会发出错误“错误:缺少必需的软件包”。我必须在.rmd文件中执行代码,以查找包含丢失软件包名称的真实错误消息。svmcaret需要一种情况kernlab
Charles

19

将通用代码分解到一个单独的R文件中,然后将该R文件提供给您想要放入的每个Rmd文件。

例如,假设我有两个报告需要填写,分别是《流感暴发》和《枪支与黄油分析》。当然,我会创建两个Rmd文档并完成它。

现在,假设老板来了,并希望了解流感爆发与黄油价格的差异(控制9毫米弹药)。

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

我的解决方案是将项目分解为以下文件:

  • 流感病毒
    • 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>

注意,这是为Rmd-> html工作流设计的。如果您使用乳胶或其他任何东西,这将是一个丑陋的混乱。该Rmd文档在全局环境中查找所有source()编辑的文件,并在文档末尾包含其源代码。它包括jquery ui,tablesorter,并设置文档以使用手风琴样式显示/隐藏源文件。这是一项正在进行中的工作,但是可以根据自己的用途进行调整。

我知道不是一线客。希望它至少能给您一些想法:)


4

也许人们应该开始思考不同的事物。我的问题如下:在.R文件的.Rmd块中编写您通常应该拥有的所有代码。对于用于编织的Rmd文档(即html),您只剩下

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

这样,您可能会创建一堆.R文件,并且您失去了使用ctrl + alt + n(或+ c,但通常不起作用)处理所有“块后块”代码的优势。但是,我阅读了Gandrud先生有关可重复研究的书,并意识到,他绝对使用knitr和.Rmd文件仅用于创建html文件。Main Analysis本身是一个.R文件。我认为,如果您开始在内部进行整个分析,.Rmd文档将迅速变得太大。


3

如果您只是在编写代码之后,我认为应该遵循以下几点:

  1. 使用读取markdown / R文件 readLines
  2. 使用grep查找代码块,寻找这样开始的行<<<,例如
  3. 取包含原始行的对象子集,仅获取代码
  4. 使用以下命令将其转储到临时文件中 writeLines
  5. 将此文件来源到您的R会话中

将其包装在函数中应该会为您提供所需的内容。


1
谢谢,我想那行得通。但是,前四个点听起来像Stangle已经以一种可靠的方式完成了Sweave的功能,以及knit('myfile.rmd', tangle=TRUE)编织物中的功能。我想我正在寻找一种既能缠结又能打源并且理想情况下不创建文件的衬垫。
Jeromy Anglim 2012年

一旦将其包装到函数中,它将成为一个单行;)。您可以做的是textConnection模仿文件,并从中获取源文件。这样可以避免创建文件。
Paul Hiemstra,2012年

是。textConnection可能是看的地方。
Jeromy Anglim 2012年

2

以下hack对我来说很好:

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

我使用以下自定义函数

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

source_rmd("munge_script.Rmd")


1

我建议将主要分析和计算代码保留在.R文件中,并根据需要在.Rmd文件中导入块。我在这里解释了这个过程。


1

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

在调用your_script_file_name.R中包含的函数之前放置此命令。

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

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


0

这对我有用

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.