如何使用source('myfile.r')来源化R Markdown文件?
我经常有一个主要的R Markdown文件或者编辑LaTeX文件,我从其他R文件(例如,用于数据处理)中获取source
文件。 但是,我认为在某些情况下,将这些源文件作为自己的可再生文档(例如,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
。
但是,这似乎并不完美:
- 它会导致创build一个额外的文件
- 如果需要控制显示,它需要出现在它自己的代码块中。
- 它不像简单的
source(...)
那样优雅。
因此,我的问题: 是否有一个更优雅的方式来源R R Markdown文件的R代码?
看来你正在寻找一个单线。 把这个放在你的.Rprofile
怎么.Rprofile
?
ksource <- function(x, ...) { library(knitr) source(purl(x, output = tempfile()), ...) }
不过,我不明白你为什么要在Rmd文件中input代码。 我的意思是knit()
将会运行这个文档中的所有代码,并且如果你提取代码并且在一个块中运行它,那么当你编译knit()
这个文档(你自己在里面运行knit()
时候,所有代码都会运行两次。 这两项任务应该是分开的。
如果你真的想运行所有的代码,RStudio已经很简单了: Ctrl + Shift + R
它基本上在场景后面调用purl()
和source()
。
将通用代码分解到单独的R文件中,然后将该R文件input到您需要的每个Rmd文件中。
所以举例来说,我有两个报告,我需要做,stream感爆发和枪支与黄油分析。 当然,我会创build两个Rmd文件,并完成它。
现在假设老板来了,想看看stream感爆发与黄油价格(控制9毫米弹药)的变化。
- 复制和粘贴代码以将报告分析到新报告中对于代码重用等是一个糟糕的主意。
- 我希望它看起来不错。
我的解决scheme是将这个项目分解到这些文件中:
- 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') ```
这里的问题是我们失去了重现性。 我的解决scheme是创build一个通用的子文档,以包含到每个Rmd文件。 所以在我创build的每个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("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("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工作stream而devise的。 这将是一个丑陋的混乱,如果你去与乳胶或其他任何东西。 这个Rmd文档通过全局环境查看所有source()的ed文件,并在文档的末尾包含它们的源代码。 它包括jquery ui,tablesorter,并将文档设置为使用手风琴风格来显示/隐藏源文件。 这是一项正在进行的工作,但随时可以根据自己的用途进行调整。
我知道,不是一句话。 希望至less给你一些想法:)
如果你只是在代码之后,我认为沿着这些线应该工作:
- 用
readLines
读取markdown / R文件 - 使用
grep
来查找代码块,search以<<<
开始的行 - 以包含原始行的对象的子集来获取只有代码
- 使用
writeLines
将其转储到临时文件 - 将此文件input到您的R会话中
用一个函数包装这个应该给你你需要的东西。
也许应该开始思考不同。 我的问题如下:将.Rmd块中的每个代码写入.R文件中。 而对于你用来编织的Rmd文件,即HTML,你只剩下了
```{R Chunkname, Chunkoptions} source(file.R) ```
这样你可能会创build一堆.R文件,并失去使用ctrl + alt + n(或+ c,但通常不起作用)处理所有代码块“块后”的优势。 但是,我读了Gandrud先生关于可重复研究的书,并认识到,他绝对使用knitr和.Rmd文件来创buildhtml文件。 主分析本身是一个.R文件。 我认为。如果你开始在里面进行整个分析,那么Rmd文件就会迅速增长太多。
下面的黑客为我工作得很好:
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) }
我build议在.R文件中保留主分析和计算代码,并根据需要在.Rmd文件中导入块。 我已经在这里解释了这个过程。