【问题标题】:R knitr: Possible to programmatically modify chunk labels?R knitr:可以以编程方式修改块标签吗?
【发布时间】:2012-08-19 04:11:24
【问题描述】:

我正在尝试使用 knitr 生成一份报告,该报告对数据集的不同子集执行相同的分析集。该项目包含两个 Rmd 文件:第一个文件是设置工作区和文档的主文档,第二个文件仅包含执行分析和生成相关图形的块。

我想做的是编织主文件,然后它将为每个数据子集调用第二个文件并将结果包含在单个文档中。下面是一个简单的例子。

主文档:

# My report

```{r}
library(iterators)
data(mtcars)
```

```{r create-iterator}
cyl.i <- iter(unique(mtcars$cyl))
```

## Generate report for each level of cylinder variable
```{r cyl4-report, child='analysis-template.Rmd'}
```

```{r cyl6-report, child='analysis-template.Rmd'}
```

```{r cyl8-report, child='analysis-template.Rmd'}
```

分析模板.Rmd:

```{r, results='asis'}
cur.cyl <- nextElem(cyl.i)
cat("###", cur.cyl)
```

```{r mpg-histogram}
hist(mtcars$mpg[mtcars$cyl == cur.cyl], main = paste(cur.cyl, "cylinders"))
```

```{r weight-histogam}
hist(mtcars$wt[mtcars$cyl == cur.cyl], main = paste(cur.cyl, "cylinders"))
```

问题是 knitr 不允许使用非唯一的块标签,因此第二次调用 analysis-template.Rmd 时编织失败。这个问题可以通过不命名块来避免,因为随后会自动生成唯一的标签。然而,这并不理想,因为我想使用块标签为导出的图创建信息文件名。


一个潜在的解决方案是使用一个简单的函数,将当前柱面附加到块标签:

```r{paste('cur-label', cyl, sep = "-")}
```

但似乎 knitr 不会评估块标签位置中的表达式。


我还尝试使用修改当前块标签的自定义chunk hook

knit_hooks$set(cyl.suffix = function(before, options, envir) {
    if (before) options$label <- "new-label"
})

但是更改块标签不会影响生成图的文件名,所以我认为 knitr 没有使用新标签。


关于如何更改块标签以便可以多次调用同一个子文档的任何想法?或者也许是实现这一目标的替代策略?

【问题讨论】:

    标签: r knitr


    【解决方案1】:

    here 提出了一个类似的问题,我能够使用 knit_expand(text=) 和 @ 基于任意输入图列表以编程方式创建 r 块并编织输出以用于 flexdashboard(非常有用) 987654322@ 方法。

    【讨论】:

      【解决方案2】:

      对于看到这篇文章的其他人,我想指出@Yihui 在 knitr 1.0 中为这个问题提供了formal solution,并引入了knit_expand() 功能。它工作得很好,确实简化了我的工作流程。

      例如,以下将为mtcars$cyl 的每个级别处理下面的模板脚本,每次将{{ncyl}}(在模板中)的所有实例替换为其当前值:

      # My report
      
      ```{r}
      data(mtcars)
      cyl.levels <- unique(mtcars$cyl)
      ```
      
      ## Generate report for each level of cylinder variable
      ```{r, include=FALSE}
      src <- lapply(cyl.levels, function(ncyl) knit_expand(file = "template.Rmd"))
      ```
      
      `r knit(text = unlist(src))`
      

      模板:

      ```{r, results='asis'}
      cat("### {{ncyl}} cylinders")
      ```
      
      ```{r mpg-histogram-{{ncyl}}cyl}
      hist(mtcars$mpg[mtcars$cyl == {{ncyl}}], 
        main = paste({{ncyl}}, "cylinders"))
      ```
      
      ```{r weight-histogam-{{ncyl}}cyl}
      hist(mtcars$wt[mtcars$cyl == {{ncyl}}], 
        main = paste({{ncyl}}, "cylinders"))
      ```
      

      【讨论】:

      • 我使用过这种方法,但注意到在模板中使用echo=FALSE 会导致代码未被处理。您是否注意到相同的行为?
      • Edit:我已经将这种方法与 bookdown 包一起使用,并注意到在模板中使用 results='asis'echo=FALSE 会导致未处理的代码。解决方案是将每个输出放在单独的代码块中。
      【解决方案3】:

      如果你把你的**中的所有块都设为无名,即```{r},它就可以工作。当然,这不是很优雅,但是有两个问题阻止您更改当前块的标签:

      1. 在执行代码块之前解析文件。在执行任何代码或调用自定义挂钩之前,解析器已经检测到重复标签。
      2. 在调用钩子之前处理块选项(包括标签)(逻辑:这是触发钩子的选项),因此钩子不能再更改标签。

      未命名块起作用的事实是它们在内部获得标签unnamed-chunk-+块编号。

      块不能有重复的名称,因为内部 knitr 通过标签引用它们。一个修复方法是让 knitr 将块编号添加到具有重复名称的所有块中。或者通过块编号而不是标签来引用它们,但这在我看来是一个更大的变化。

      【讨论】:

      • 您的理解是绝对正确的,这是一个令人信服的案例,表明 knitr 需要进行一些更改。我现在正在查看您的拉取请求。谢谢!
      • @Rolo,您对 knitr 内部工作原理的解释非常有帮助。我非常感谢您抽出宝贵时间写the code implementing your solution。 @Yihui,你认为你会包括这个变化吗?它将解决我试图完成的 90% 的任务,并且使我无需维护除了修改的块标签之外相同的 Rmd 文件的副本。理想的解决方案将允许类似for(i in unique(mtcars$cyl)) knit_child("analysis-template.Rmd", label.suffix = i) 的东西,如果它是合理的。
      • 是的,我想我倾向于接受拉取请求;再给我几分钟,因为我还有几个替代解决方案。解决这个问题很容易,但很难决定使用哪种解决方案。
      猜你喜欢
      • 1970-01-01
      • 2015-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      相关资源
      最近更新 更多