【问题标题】:Externalise config file and functions in R markdown在 R markdown 中外部化配置文件和函数
【发布时间】:2018-06-29 14:09:14
【问题描述】:

我在理解 R 笔记本中外部化代码的不同方法之间的(实际)差异时遇到了问题。参考了之前的questionsdocumentation,目前还不清楚采购外部.R 文件或read_chunk() 它们的区别。出于实际目的,让我们考虑以下内容:

  1. 我想用外部config.R 文件加载库:据我所知,最直观的方法似乎是创建config.R

    library(first_package)
    library(second_package)
    ...
    

    并且,在一般的 R 笔记本中(比如,main.Rmd)这样称呼它

    ```{r}
    source('config.R')
    ```
    
    ```{r}
    # use the libraries included above
    ```
    

    但是,这无法识别包含的软件包,因此似乎采购外部配置文件是无用的。同样使用 read_chunk() 代替。因此问题是:如何在顶部包含库,以便在主要的 Markdown 脚本中识别它们?

  2. 假设我想在外部定义全局函数,然后将它们包含在主笔记本中:与上述相同,将它们包含在外部 foo.R 文件中,并将它们包含在主文件中。

同样,在这种情况下,read_chunk() 似乎不能完成这项工作,而 source('foo.R') 可以;文档指出,前者“只评估代码,但不执行它”:什么时候曾经只想评估代码而不执行它?换种方式:出于实际目的,为什么要使用read_chunk() 而不是source

【问题讨论】:

    标签: r r-markdown rnotebook


    【解决方案1】:
    1. 这无法识别包含的包

      在您的示例中,first_packagesecond_package 在第二个代码块的工作环境中可用。

      尝试将library(nycflights13) 放在R 文件中,将head(airlines) 放在Rmd 文件的第二个块中。如果nycflights13 包没有成功加载source,则调用knit("main.Rmd") 将失败。

    2. read_chunk 实际上确实做到了这一点(与source 一起),但是它们的处理方式不同。使用source,您将在source 之后直接使用全局函数(如您所见)。但是,对于read_chunk,正如您所指出的,因为它只评估代码,但不执行它,您需要显式执行该块,然后该函数才可用。 (请参见下面的third_config_chunk 示例。在报告中包含third_config_chunk 的空块允许在后续块中调用全局some_function。)

    关于“只评估代码,但不执行它”,这是 R 编程的一个完整属性,称为 lazy evaluation。这个想法是您可能想要创建许多函数或模板代码,这些函数或模板代码会读入您的 R 环境但不会在现场执行,从而允许您在评估之前修改环境/参数。这也允许您多次执行相同的代码块,而 source 只会使用已经提供的代码运行一次。

    假设您有一个外部 R 脚本,其中包含大量报告中不需要的设置代码。可以将此文件格式化为许多“块”,这些“块”将通过read_chunk 加载到工作环境中,但在明确告知之前不会被评估。

    为了使用read_chunk() 将您的config.R 外部化,您可以将R 脚本编写为:

    config.R

    # ---- config_preamble
    ## setup code that is required for config.R
    ## to run but not for main.Rmd
    
    # ---- first_config_chunk
    library(nycflights13)
    library(MASS)
    
    # ---- second_config_chunk
    y <- 1
    
    # ---- third_config_chunk
    some_function <- function(x) {
      x + y
    }
    
    # ---- fourth_config_chunk
    some_function(10)
    
    # ---- config_output
    ## code that is output during `source`
    ## and not wanted in main.Rmd
    print(some_function(10))
    

    要将此脚本与外部化方法一起使用,您需要按如下方式设置 ma​​in.Rmd

    ma​​in.Rmd

    ```{r, include=FALSE}
    knitr::read_chunk('config.R')
    ```
    
    ```{r first_config_chunk}
    ```
    
    The packages are now loaded.
    
    ```{r third_config_chunk}
    ```
    
    `some_function` is now available.
    
    ```{r new_chunk}
    y <- 20
    ```
    
    ```{r fourth_config_chunk}
    ```
    ## [1] 30
    
    ```{r new_chunk_two}
    y <- 100
    lapply(seq(3), some_function)
    ```
    ## [[1]]
    ## [1] 101
    ## 
    ## [[2]]
    ## [1] 102
    ## 
    ## [[3]]
    ## [1] 103
    
    ```{r source_file_instead}
    source("config.R")
    ```
    ## [1] 11
    

    如您所见,如果您要source 此文件,则无法在执行之前修改对some_function 的调用,并且该调用将打印输出“11”。现在这些块在环境中可用,它们可以被重新调用任意次数(例如,在更改 y 的值之后)或在当前环境中以任何其他方式使用(例如,new_chunk_twosource 是不可能的如果你不想执行 R 脚本的其余部分

    【讨论】:

    • 谢谢你的彻底回答,现在更清楚了。然而,似乎我的错误是从我用来实际“加载”该块的同一块中调用该函数:显然需要一个块来调用外部代码,然后才能使用该代码,但在取而代之的是其他新的块(这对我来说似乎非常违反直觉)。明确地说,如果我做了{r read_first_chunk} #use loaded libraries ,那么你的例子也不起作用。
    • 是的,执行块需要是空的,否则任何写在它的位置的东西都会被执行。或者,您可以在 read_chunk 之后立即使用 eval(parse(text=knitr:::knit_code$get("third_config_chunk"))) 或您喜欢的任何块名称将块执行到环境中。但请注意,knitr 不会导出此函数。
    • 我明白了:但是,读取外部 .R 文件而不是 .Rmd 有什么区别吗?从技术上讲,块本身可以采用后一种格式(或者有什么理由更喜欢前者)?
    • 功能上,我不这么认为。这将归结为个人喜好和用例。如果我希望这些块包含一个更大的脚本,我会使用外部 R 文件,该脚本可以作为独立源文件分发给不熟悉 R markdown 或通过另一个工作流调用的其他人(即Rscript)。 Rmd 显然不是纯 R 源,所以这行不通。
    猜你喜欢
    • 2015-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2016-04-17
    • 1970-01-01
    • 2019-12-31
    • 2014-08-23
    相关资源
    最近更新 更多