6

I have to run markdown reports for 2 subjects (math and reading) on a regular basis. I currently have to set the subject variable in the title of the markdown, in the output filename (.html file), and again within the R code chunks for data processing. I would like to set this variable one time and have the title, output file, and analyses all adjust. Is there a way to do this?

I know the title can be made dynamic through the params YAML header but that doesn't help with the output filename or within the R code chunks.

See a portion of my current code below. Notice the reading variable is specified 3 times (title, output file name, and within the R code chunk under "Presets". I would like to specify "reading" one time (preferably at the top of the script).

---
title: Reading Investigation"
author: "xxx"
date: "`r format(Sys.time(), '%B %d, %Y')`"
output:
  html_document:
    code_folding: hide
    depth: 3
    fig_height: 10
    fig_width: 12
    highlight: tango
    number_sections: no
    theme: cerulean
    toc: yes
    toc_float: yes
    
knit: (function(inputFile, encoding) 
{ rmarkdown::render(inputFile, encoding = encoding, 
output_file = paste0('folder/reading_output_', Sys.Date(), '.html') )}) 
---

```{r, message=FALSE, echo=FALSE, warning=FALSE, results='hide', comment="", fig.height=10, fig.width=12}

# Libraries ------------------------------------------------------------------

library(RODBC)
library(tidyverse)
library(ggplot2)
library(kableExtra)

# Set up ------------------------------------------------------------------
options(scipen=999)

#### Presets
subject = "reading" # "math", "reading"
```
shafee
  • 15,566
  • 3
  • 19
  • 47
Kate N
  • 423
  • 3
  • 14

1 Answers1

2

To set the output file name dynamically, we have to rely on params and params can be used to set document title dynamically and define variable in r-code chunk too.

Using Helper Render function


dynamic_metadata.Rmd

---
author: "xxx"
date: "`r format(Sys.time(), '%B %d, %Y')`"
output:
  html_document:
    code_folding: hide
    depth: 3
    fig_height: 10
    fig_width: 12
    highlight: tango
    number_sections: no
    theme: cerulean
    toc: yes
    toc_float: yes
params: 
    set_title: "reading"
title: "`r paste(tools::toTitleCase(params$set_title), 'Investigation')`"
---

```{r}

#### Presets
subject = params$set_title # "math", "reading"

subject
```

Then we have to define this function in r-console or in a separate R file (i.e. R script) and then call it from r-console or from that R file with sub as either reading or math.

render_subject <- function(sub) {
  rmarkdown::render(
    input = "dynamic_metadata.Rmd",
    params = list(set_title = sub),
    output_file = paste0('folder/', sub, '_output_', Sys.Date(), '.html')
  )
}


render_subject("reading")
render_subject("math")

The rendered output files look like this,

renders files (side by side)


And the filenames in the folder directory,

+---folder
|       math_output_2022-08-18.html
|       reading_output_2022-08-18.html

Using Custom function in knit


Another way of doing this could be based on this answer on dynamically naming output file,

---
author: "xxx"
date: "`r format(Sys.time(), '%B %d, %Y')`"
output:
  html_document:
    code_folding: hide
    depth: 3
    fig_height: 10
    fig_width: 12
    highlight: tango
    number_sections: no
    theme: cerulean
    toc: yes
    toc_float: yes
params: 
    set_title: "math" # reading

title: "`r paste(tools::toTitleCase(params$set_title), 'Investigation')`"
knit: >
  (function(input_file, encoding) {
    # Render, keeping intermediate files for extracting front matter
    md_dir <- tempdir()
    output_file_temp <- rmarkdown::render(
      input = input_file,
      output_file = tempfile(),
      intermediates_dir = md_dir,
      clean = FALSE
    )
    
    # Get the rendered front matter from the intermediate Markdown file
    md_file <- fs::path_ext_set(fs::path_file(input_file), ".knit.md")
    metadata <- rmarkdown::yaml_front_matter(fs::path(md_dir, md_file))
    
    # Extract the subject name from title of rendered file
    output_name <- with(metadata, {
      sub = sub(" .*$", "", tolower(title))
      paste0(fs::path_home_r(), '/folders/', sub, '_output_', Sys.Date(), '.html')
    })

    # Add the file extension and move to R's definition of the home directory
    output_ext <- fs::path_ext(output_file_temp)
    output_file <- fs::path_ext_set(output_name, output_ext)
    fs::file_move(output_file_temp, output_file)

    message("Output moved to: ", output_file)
  })
---

```{r}

#### Presets
subject = params$set_title # "math", "reading"

subject
```

The rendered output files look like the 1st option. One thing to note is that we would need the {fs} package installed before we proceed with this second approach.

shafee
  • 15,566
  • 3
  • 19
  • 47
  • This looks like the title and code chunks can be dealt with purely with the YAML, but to also get the output file name requires a helper function to set a parameter in `rmarkdown::render`. I am still interested if there is a way to do it all without a helper render function. – Brian Diggs Aug 18 '22 at 23:12
  • 1
    @BrianDiggs, see the updated answer. The second method also relies on `params` but without using a helper render function. And AFAIK, though it's possible to set the title dynamically using a global variable, its not possible to define the output file name with that global variable. So ultimately we have to rely on the params – shafee Aug 19 '22 at 04:35