Parameterised Rmarkdown

R
intermediate
Published

July 15, 2024

Welcome

  • this is an 🌶🌶 intermediate-level practical session designed for those with prior R experience, but who are new to Shiny
  • it’s definitely meant to be a taster session, rather than a comprehensive introduction
  • you’ need R of some sort to follow along
    • you’ll also need Quarto for the Quarto-specific part

Session outline

  • ways of knitting
  • interesting things to try
    • show/hide code
    • choose params interactively
    • knit from a function
    • use purrr (and possibly targets) to do this at scale
    • try this with Quarto

Resources

  • you’ll need R 4.0+ and Rstudio/posit.cloud/Posit workbench/VSCode and packages
  • you’ll also need Quarto installed, which can be a battle in the server-based flavours of Posit

Ways of knitting

We’ll need to start with a hello-world Rmarkdown document. That should be knit-able from the knit button knit button. But you should also be able to knit using rmarkdown::render() to produce some output. Together, those three elements look like this:

---
title: "Plain Rmarkdown"
output: html_document
---

```{r, results='asis'}
cat("Hello world")
```
rmarkdown::render("01_rmd.Rmd")

This gets more interesting when you start specifying options inside render. A basic example of this would be to change the output filename by adding output_file:

rmarkdown::render("Rmarkdown.Rmd", 
                  output_file = "Rmarkdown_plain.html")

We could also change our output format. By default, rmarkdown docs get rendered to whatever is specified in the header. That’s HTML in our case, but we could fiddle that to give us .pdf output:

rmarkdown::render("Rmarkdown.Rmd", 
                  output_file = "Rmarkdown_plain.pdf",
                  output_format = pdf_document)

Note that we’re still producing the same output each time, albeit with different filenames and in different formats. The next part is to add params, which will enable us to change the content. We need to make two changes to our Rmd first:

We add params in the header, and some code (params$name) to access their values in the body of our Rmarkdown document:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
---

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```
rmarkdown::render("02_rmd.Rmd")

We can now add some params in our render code to produce a different output. Note that, as we set defaults in the Rmarkdown header, these params are optional. But if we do include params in our render call, they will over-ride the defaults:

Same Rmarkdown:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
---

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```
rmarkdown::render("02_rmd.Rmd",
                  params = list(name = "Steve")) # new param

Interesting things to do with params: show/hide code

Add an extra param to the Rmarkdown, and link it to the chunk options:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
  showcode: TRUE
---

```{r, include=FALSE}
knitr::opts_chunk$set(echo = params$showcode) # setting all chunk options to the value of showcode
```

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```
rmarkdown::render("03_rmd.Rmd",
                  params = list(showcode = FALSE)) # new param

Interesting things to do with params: choose params interactively

With the same Rmd, we can run params interactively via a mini-Shiny app:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
  showcode: TRUE
---

```{r, include=FALSE}
knitr::opts_chunk$set(echo = params$showcode) # setting all chunk options to the value of showcode
```

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```

You can also access this via the knit menu:

rmarkdown::render("03_rmd.Rmd", params = "ask") # interactive parameter choice

Interactive parameter chooser:

Interesting things to do with params: knit from a function

We’ll use the same Rmd for this:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
  showcode: TRUE
---

```{r, include=FALSE}
knitr::opts_chunk$set(echo = params$showcode) # setting all chunk options to the value of showcode
```

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```

We build a function to call rmarkdown::render() with the right options:

make_mark <- function(input_name, show_code = TRUE, format = "html"){

  rmarkdown::render("03_rmd.Rmd",
                    output_file = paste0("03_rmd_", input_name, ".", format),
                    params = list(name = input_name, 
                                  showcode = show_code))
}

make_mark(input_name = "Nat")
make_mark(input_name = "Mel", format = "pdf")
make_mark(input_name = "Sue", show_code = FALSE, format = "html")

and

and

Interesting things to do with params: purrr from that function

Same Rmd again:

---
title: "Rmarkdown with params"
output: html_document
params:
  name: "Bruce" # added a param with a default value
  showcode: TRUE
---

```{r, include=FALSE}
knitr::opts_chunk$set(echo = params$showcode) # setting all chunk options to the value of showcode
```

```{r, results='asis'}
cat(paste("Hello", params$name)) # use that param in the body of the document
```

We’ll now call our make_mark function using purrr. dplyr::expand_grid() helps set all the correct combinations up for us:

input_name = c("Angela", "Gloria", "Audre", "bell")
format = c("pdf", "html")

tidyr::expand_grid(input_name, format) |>
  purrr::pwalk(make_mark2) 

Angela HTML

and

Angela .pdf

and

Audr

and

…and so on and so on.

Using functional programming can lead to massive and complicated results quickly. You should probably investigate the targets package if you’re looking to apply this at scale.

Interesting things to do with params: use in Quarto

This parameterised approach is also applicable to Quarto.

Basically the same as our Rmd with a different render function:

---
title: "Quarto with params"
output: html
params:
  name: "Bruce" # added a param with a default value
---

```{r}
#| results: asis
cat(paste("Hello", params$name)) # use that param in the body of the document
```
quarto::quarto_render("01_qmd.qmd") # different render function


quarto::quarto_render("01_qmd.qmd", 
                      execute_params = list(name = "Emma")) # same way of setting params

and