如何使代码块依赖于knitr / rmarkdown中以前的所有块?

时间:2015-10-21 21:48:43

标签: r knitr

目标

我想通过使块依赖于所有先前的块来创建可重现的数据分析。因此,如果有3个块并且我在第一个块中更改某些内容,则后续的2个块应该重新运行,以便它们反映输出中所做的更改。我想在文档顶部的全局块选项中添加此条件,这样我就不必多次使用dependson

问题

如果未修改块cache=TRUE,则块的输出不会更改。对于包含代码的块,我可以使用文档顶部的以下内容使它们可靠地依赖于所有先前的代码:

```{r setup, echo=FALSE}
# set global chunk options: 
library(knitr)
opts_chunk$set(cache=TRUE, autodep = TRUE)
dep_auto()
```

如果更改了上述任何一个块,则重新运行所有后续块。但是,如果我在块中使用source()来读取R脚本,则这不起作用。以下是一个示例文档:

---
title: "Untitled"
output: html_document
---
```{r setup, echo=FALSE}
# set global chunk options: 
library(knitr)
opts_chunk$set(cache=TRUE, autodep = TRUE)
dep_auto()
```


# Create Data
```{r}
#source("data1.R")
x <- data.frame(col1 = 4:10, col2 = 6:12)
x
```

# Summaries
```{r}
#source("data2.R")

median1.of.x <- sapply(x, function(x) median(x)-1)

sd.of.x <- sapply(x, sd)

plus.of.x <- sapply(x, function(x) mean(x)+1)

jj <- rbind(plus.of.x, sd.of.x, median1.of.x)

```

```{r}
jj
```

现在,如果我更改了前2个块中的任何一个,则第三个块在knit之后提供正确的输出。但是如果相反我将第一个块的代码放在源文件data1.R中,将第二个块放在文件data2.R中,保持全局块选项与以前相同,如果我对源文件进行任何更改,则不会反映在第三个块的输出正确。例如,将x更改为x <- data.frame(col1 = 5:11, col2 = 6:12)应该会产生:

 > jj
                 col1      col2
plus.of.x    9.000000 10.000000
sd.of.x      2.160247  2.160247
median1.of.x 8.000000  9.000000 

但如上所述使用source()knitr文档会报告:

 jj
##                col1      col2
## mean.of.x  5.000000  9.000000
## sd.of.x    2.160247  2.160247
## minus.of.x 6.000000 10.000000 

我需要更改哪些设置才能正确使用source文档中的knitr

3 个答案:

答案 0 :(得分:13)

当您使用source()时, knitr 无法分析从中创建的可能对象; knitr 必须能够看到完整的源代码来分析代码块之间的依赖关系。有两种方法可以解决您的问题:

  1. 通过添加使用x值的任意块选项,例如,告诉第二个块它取决于x的值。 ```{r cache.extra = x};然后,只要x发生更改,此代码块的缓存就会自动失效(more info);
  2. knitr 查看完整的源代码;您可以通过块选项code将源代码传递给代码块,例如```{r code = readLines('data1.R')}data2.R相同);然后dep_auto()应该能够找出从第一个块创建的x,并在第二个块中使用,所以第二个块必须依赖于第一个块。

答案 1 :(得分:2)

我发现这有效(knitr 1.17):

<<..., dependson=all_labels()>>=
...
@

答案 2 :(得分:0)

我认为,默认情况下,块确实取决于以前的块,并且作者竭尽全力尝试使每个块开始使用与最后一个块相同的环境(尽管有很多方法可以解决这个问题,比如开启缓存的源文件......)我无法回想起语法,但你可以在外部文档中包含knitr块。还有一个技巧可以通过重用标签以类似函数的方式在同一个doc中重用knitr块,并且您可以从中构建一些非线性依赖。但是,当你不想要缓存时,为什么不将缓存设置为FALSE?采购似乎是一个坏主意,但我无法理解为什么。我会使knitr工作流程变为线性并将逻辑放入函数中,如果相同的函数调用可以返回具有相同输入参数的不同内容,则关闭缓存。

另一个可能对您有用的技巧是最近添加的使用输入参数编织文档的功能。这可能会从你的knitr doc中提取一些逻辑,我认为这是你问题的可避免根源。