简体   繁体   English

RMarkdown渲染到包含子块的笔记本

[英]RMarkdown render to Notebook with child chunks included

I'm after a way to render an Rmd document (that contains references to various "child" files) to a self-contained R Notebook without these dependencies. 我正在寻找一种将Rmd文档(其中包含对各种“子”文件的引用)呈现给不包含这些依赖项的自包含 R Notebook的一种方法。

At the moment, the .Rmd code chunks are located throughout a number of .R, .py and .sql files and are referenced in the report using 目前,.Rmd代码块位于整个.R,.py和.sql文件中,并在报告中使用

```{r extraction, include=FALSE, cache=FALSE}
knitr::read_chunk("myscript.R")
```

followed by 其次是

```{r chunk_from_myscript}
```

as documented here . 这里所记载。

I've done this to avoid code duplication and to allow for running the source files separately however these code chunks are only executable in the report via a call to knit or render (when read_chunk is run and the code chunk is available). 我这样做是为了避免代码重复,并允许单独运行源文件,但是这些代码块只能通过调用knitrender (在运行read_chunk并且代码块可用时)才能在read_chunk中执行。

Is there a way to spin-off an Rmd (prior to knitting) with just these chunks populated? 是否有一种方法可以在仅填充这些块的情况下剥离Rmd(在编织之前)?

This function 该功能

rmarkdown::render("report.Rmd", clean = FALSE)

almost gets there as it leaves the markdown files behind whilst removing extraction and populating chunk_from_myscript however as these files are straight markdown, the chunks are no longer executable and the chunk options are missing. 几乎到达那里了,因为它留下了markdown文件,同时删除了extraction并填充chunk_from_myscript但是由于这些文件是直接markdown的,因此这些块不再可执行,并且缺少块选项。 It obviously also doesn't include chunks where eval=TRUE, echo=FALSE which would be needed to run the resulting notebook. 显然,它也不包括运行结果笔记本需要的eval=TRUE, echo=FALSE块。

I've also looked at knitr::spin however this would mean disseminating the contents of the report to every source file and isn't terribly ideal. 我也看过knitr::spin但这意味着将报告的内容分发到每个源文件中,并且不是很理想。

Reprex Reprex

report.Rmd report.Rmd

---
title: 'Report'
---

```{r read_chunks, include=FALSE, cache=FALSE}
knitr::read_chunk("myscript.R")
```

Some documentation

```{r chunk_from_myscript}
```

Some more documentation

```{r chunk_two_from_myscript, eval=TRUE, echo=FALSE}
```

myscript.R myscript.R

#' # MyScript
#' 
#' This is a valid R source file which is formatted
#' using the `knitr::spin` style comments and code
#' chunks.
#' The file's code can be used in large .Rmd reports by
#' extracting the various chunks using `knitr::read_chunk` or
#' it can be spun into its own small commented .Rmd report
#' using `knitr::spin`

# ---- chunk_from_myscript
sessionInfo()

#' This is the second chunk

# ---- chunk_two_from_myscript
1 + 1

Desired Output 期望的输出

notebook.Rmd notebook.Rmd

---
title: 'Report'
---

Some documentation

```{r chunk_from_myscript}
sessionInfo()
```

Some more documentation

```{r chunk_two_from_myscript, eval=TRUE, echo=FALSE}
1 + 1
```

Working through your reprex I now better understand the issue you are trying to solve. 现在,通过您的reprex我可以更好地了解您要解决的问题。 You can knit into an output.Rmd to merge your report and scripts into a single markdown file. 您可以knitoutput.Rmd中,以将报告和脚本合并到单个markdown文件中。

Instead of using knitr::read_chunk , I've read in with knitr::spin to cat the asis output into another .Rmd file. 而不是使用的knitr::read_chunk ,我已经阅读knitr::spincatasis输出到另一个.Rmd文件。 Also note the params$final flag to allow rendering the final document when set as TRUE or allowing the knit to an intermediate .Rmd as FALSE by default. 还要注意params$final标志,当设置为TRUE时默认呈现最终文档,或者默认情况下允许将knit到中间.RmdFALSE

report.Rmd report.Rmd

---
title: "Report"
params:
  final: false
---

```{r load_chunk, include=FALSE}
chunk <- knitr::spin(text = readLines("myscript.R"), report = FALSE, knit = params$final)
```


Some documentation

```{r print_chunk, results='asis', echo=FALSE}
cat(chunk, sep = "\n")
```

to produce the intermediate file: 产生中间文件:

rmarkdown::render("report.Rmd", "output.Rmd")

output.Rmd output.Rmd

---
title: "Report"
---

Some documentation

```{r chunk_from_myscript, echo=TRUE}
sessionInfo() 
```

With the secondary output.Rmd , you could continue with my original response below to render to html_notebook so that the document may be shared without needing to regenerate but still containing the source R markdown file. 使用secondary output.Rmd ,您可以继续下面的原始响应以呈现到html_notebook以便可以共享文档而无需重新生成,但仍包含源R markdown文件。

To render the final document from report.Rmd you can use: 要从report.Rmd渲染最终文档,可以使用:

rmarkdown::render("report.Rmd", params = list(final = TRUE))

Original response 原始回应

You need to include additional arguments to your render statement. 您需要在render语句中包含其他参数。

rmarkdown::render(
  input = "output.Rmd",
  output_format = "html_notebook",
  output_file = "output.nb.html"
)

When you open the .nb.html file in RStudio the embedded .Rmd will be viewable in the editing pane. 当您在RStudio中打开.nb.html文件时,嵌入式.Rmd将在编辑窗格中可见。

Since neither knitr::knit nor rmarkdown::render seem suited to rendering to R markdown, I've managed to somewhat work around this by dynamically inserting the chunk text into each empty chunk and writing that to a new file: 由于knitr::knitrmarkdown::render都不适合渲染 R markdown,因此我设法通过将块文本动态插入每个空块并将其写入新文件中来解决此问题:

library(magrittr)
library(stringr)

# Find the line numbers of every empty code chunk
get_empty_chunk_line_nums <- function(file_text){
  # Create an Nx2 matrix where the rows correspond
  # to code chunks and the columns are start/end line nums
  mat <- file_text %>%
    grep(pattern = "^```") %>%
    matrix(ncol = 2, byrow = TRUE)
  # Return the chunk line numbers where the end line number
  # immediately follows the starting line (ie. chunk is empty)
  empty_chunks <- mat[,1] + 1 == mat[,2]
  mat[empty_chunks, 1]
}

# Substitute each empty code chunk with the code from `read_chunk`
replace_chunk_code <- function(this_chunk_num) {
  this_chunk <- file_text[this_chunk_num]
  # Extract the chunk alias
  chunk_name <- stringr::str_match(this_chunk, "^```\\{\\w+ (\\w+)")[2]
  # Replace the closing "```" with "<chunk code>\n```"
  chunk_code <- paste0(knitr:::knit_code$get(chunk_name), collapse = "\n")
  file_text[this_chunk_num + 1] %<>% {paste(chunk_code, ., sep = "\n")}
  file_text
}

render_to_rmd <- function(input_file, output_file, source_files) {
  lapply(source_files, knitr::read_chunk)
  file_text <- readLines(input_file)
  empty_chunks <- get_empty_chunk_line_nums(file_text)
  for (chunk_num in empty_chunks){
    file_text <- replace_chunk_code(file_text, chunk_num)
  }
  writeLines(file_text, output_file)
}

source_files <- c("myscript.R")
render_to_rmd("report.Rmd", "output.Rmd", source_files)

This has the added benefits of preserving chunk options and working with Python and SQL chunks too since there is no requirement to evaluate any chunks in this step. 这具有保留块选项以及使用Python和SQL块的额外好处,因为在此步骤中无需评估任何块。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM