I have a .Rmd chunk that loads a list of data frames from an analysis function in another .R file. In a separate .R file, I have code to knit the .Rmd file with certain parameters.
The objective is to save an .html file with the output from the function for each value in the range of 1 to 17.
Currently, my approach causes the .Rmd file to iterate over the entire range and load all 17 outputs, but it only exports the last one. On the other hand, the .R file knits the .Rmd to .html 17 times, saving the export over the same file each time.
Here is the approach that I am using.
.Rmd code
{r report1, echo=FALSE, message=FALSE, warning=FALSE}
#Source file to load function
source('function_file.R')
# Iterate over 1 to 17
for (i in 1:17) {
# load function from file and save output in a list for each obs.
output_list <- list_function(i)
}
# Next comes printing from output_list
.R code
# Write function to render .Rmd file
render_report <- function(index) {
# Parameters
params <- list(index = index)
# Render .Rmd file into HTML
rendered_report <- rmarkdown::render("rmd_file.Rmd", params = params)
# output name for .HTML file
html_output <- paste0("rep_", indice, ".html")
# save as .HTML
file.copy(rendered_report, html_output)
# delete temp generated by render()
file.remove(rendered_report)
}
# vector with desired indexes
indexes <- 1:17
# Iterate over indexes and render .Rmd for each one
for (index in indexes) {
render_report(index)
}
I need that each time the file is rendered, one of the function's index is changed, so that the next output in the range from 1 to 17 is rendered.
I would appreciate any help in advance.
With parametrized reports, all that functionality is already there. Here's an example of a single Rmd notebook including all the code for generating a series of HTML reports.
Parameters can be defined in YAML block at the start of the document together with default values, that parameter (here: report_index
) can later referenced in R as params$report_index
; it also works for inline R expressions and in YAML (check how it's used for title
in YAML).
Report generation loop just calls rmarkdown::render()
with each desired report_index
parameter value. There's no need to copy or move rendered reports, instead, we can set output_file
argument and let rmarkdown::render()
handle this.
The last code block from the following example is for rendering; it's excluded from knitr
output and evaluation by setting eval=FALSE, echo=FALSE
, so it has to be executed manually. Or it can be kept in a separate R file.
---
output: html_document
params:
report_index : 1
title: "Report `r params$report_index`"
---
```{r setup, echo=FALSE, message=FALSE, warning=FALSE}
# something to simulate a function in function_file.R
# returns a list of 3 vectors
list_function <- function(i){
list(".5" = .5, "1" = 1, "2" = 2) |>
lapply(\(sd_) rnorm(100, i, sd_))
}
```
```{r report, echo=FALSE, message=FALSE, warning=FALSE}
# for parametrized report, use params$report_index to refer to report index:
data_list <- list_function(params$report_index)
df <- stack(data_list)
hist(df$values, main = paste("Report index", params$report_index))
boxplot(values ~ ind, data = df, main = "By ind")
```
```{r render, eval=FALSE, echo=FALSE}
# rendering block, only for manual execution, not evaluated during knitting
# generate 5 reports:
for (i in 1:5){
rmarkdown::render("multi-report.Rmd",
output_file = sprintf("rep_%.2d", i),
params = list(report_index = i),
quiet = TRUE)
}
# list resulting files:
fs::dir_info(glob = "*rep*")[1:3]
# screenshots of first three:
webshot2::webshot(list.files(pattern = "html$")[1:3], zoom = .5) |>
lapply(png::readPNG) |>
lapply(grid::rasterGrob) |> gridExtra::marrangeGrob(nrow = 1, ncol = 3, top = NA)
```
Result of the last block and screenshots from the first three reports:
# A tibble: 6 × 3
path type size
<fs::path> <fct> <fs::bytes>
1 multi-report.Rmd file 1.35K
2 rep_01.html file 639.74K
3 rep_02.html file 639.95K
4 rep_03.html file 639.55K
5 rep_04.html file 639.53K
6 rep_05.html file 640.62K