【问题标题】:How to make code chunks depend on all previous chunks in knitr/rmarkdown?如何使代码块依赖于 knitr/rmarkdown 中的所有先前块?
【发布时间】:2015-10-21 21:48:42
【问题描述】:

目标

我想通过使块依赖于所有先前的块来创建可重现的数据分析。因此,如果有 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 个块中的任何一个,第三个块在 knitting 之后会给出正确的输出。但是,如果我将第一个块的代码放在源文件data1.R 中,将第二个块的代码放在文件data2.R 中,保持全局块选项与以前相同,如果我对源文件进行任何更改,它们不会反映在第三块正确。例如,将 x 更改为 x &lt;- 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 

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

【问题讨论】:

  • 当您使用source 方法时,您将注释掉x &lt;- data.frame() 行,对吗?
  • Knitr 不太适合实现这一点所需的声明式工作流程。我推荐 make & makefiles,或者如果你想完全留在 R 中,优秀的 remake package
  • @rawr 是的,我只保留source 命令并注释掉所有其他命令。
  • @Ben 我会调查remake。但是我的目标在knitr 中是不可能的吗?

标签: r knitr


【解决方案1】:

当您使用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 是从第一个块创建的,并在第二个块中使用,因此第二个块必须依赖于第一个块。

【讨论】:

  • 非常感谢亿辉。一般来说,您对处理数据分析项目有什么建议?是否推荐使用source 来收集/创建数据?
  • example 113之后,第三种方法可以使用read_chunk('data1.R')吗?
  • @umairdurrani 我在这里没有一般性建议,但是如果您想将一些 R 代码放在单独的 R 脚本中,您可以使用我提到的 code 选项,或者将 read_chunk() 用作 @ Ben 提到(您必须标记代码块)。 source() 总体上很好,我想不出其他可能有问题的情况。
  • 在亿辉的第一个建议的基础上,您可以引入cache.extra = tools::md5sum("data1.R"),它会检查文件中是否有任何更改(尽管如果可能,第二个建议似乎更好)。
【解决方案2】:

我发现这可行(knitr 1.17):

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

【讨论】:

  • 这正是我想要的。使用 R Markdown,我必须使用的确切语法是 {r chunk-name, dependson=knitr::all_labels()}
【解决方案3】:

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

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

【讨论】:

  • cache=FALSE 对于这个例子来说没问题,但对于计算密集型块来说,这是一个非常糟糕的主意。如果您想在多个文档中使用相同的块,使用 source 很有用。 knitr 中的代码外部化是可能的,但它需要使用所有相互依赖的块。这就是为什么source 是更好的选择。也许数据读取步骤应该在第一个块中明确定义。
  • 我猜你的另一个选择是使用不同的缓存机制。我喜欢 R.cache。我知道感觉 knitr 应该能够做你想做的事,但是,我自己也经历过这些,我发现最好让 knitr doc 保持非常简单,并将逻辑转化为实际的编程语言,例如 R!跨度>
猜你喜欢
  • 2018-12-10
  • 2019-04-13
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
  • 2018-05-24
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多