Creating R tutorial worksheets (with and without solutions) using Quarto

Combining parameterised reporting with conditional content in Quarto, makes it easy to create multiple versions of R tutorial worksheets - a questions worksheet for students, and a solutions worksheet for students to take home.

May 31, 2024

When I’m teaching R, I often have exercise questions for students to work through during our workshop sessions. So I need to create a tutorial worksheet. Although we often go through solutions to the exercises in the workshop, I also want to create a solutions sheet that students can take home - containing the answers to the questions and some explanation.

Here’s what I want:

  • To create a document containing questions that require the use of R to answer.
  • To create a second document containing questions and solutions to the questions.
  • An approach that works for tests as well as workshops (not interactive), and so will also work for printed documents.
  • A way to name the output files and place them into specific (sometimes nested) directories.

Here’s what I don’t want:

  • To maintain two copies of the document.
  • To manually copy and paste questions or answers.

So what’s one solution to this problem? Quarto.

What is Quarto?

Quarto is an open-source scientific and technical publishing system that lets you combine code with narrative text to create reproducible documents, and automate the reporting process. Documents can be rendered in a variety of formats including HTML documents, PDF reports, Word documents, or presentations. See the Quarto documentation for more information on getting started.

One possible solution to the problem of hiding solutions in one version of the document, is to use the echo option for code blocks. Setting echo: true shows the code block in the output, whereas echo: false hides the code block in the output. However, I often don’t just want to include code in the output - I often want to include some text as an explanation. There might also be some questions that don’t require code to answer and where a text answer is necessary. So unfortunately, it’s not quite as straightforward as echo.

To understand how to use Quarto to solve the show/hide answers problem, we need to make two diversions:

  • parameterised documents
  • conditional content

Parameterised documents

Parameterised documents allow you to create different variations of the same output document. For example, a report that is created every year to reflect company performance. You can add parameters using the params option in the document YAML, and set a default option for the parameter if you wish. For example, we could create a year parameter that has 2023 as the default value:

1
2
3
4
5
---
title: "Annual report"
params: 
  year: 2023
---

This blog post assumes you are using the knitr engine to render your Quarto documents (the default if using R code blocks) If you’re using a Jupyter engine, have a look at quarto.org/docs/computations/parameters.html to see the differences.

We could then use this parameter inside a code block to filter the data. We access the parameter using the params$ notation:

1
2
3
4
```{r}
year_data <- all_data |> 
  dplyr::filter(Year == params$year)
```

To render the report for 2024 (instead of the default 2023), we can pass the parameter value to the quarto render command:

1
quarto render annual_report.qmd -P year:2024

We’re able to update our report to the next year, without having to touch the Quarto document at all!

Parameterised tutorial documents

To solve the problem of showing and hiding exercise answers, we can create a parameter called hide_answers which is true by default. When it’s true, we’ll hide the answers in the output document - the next section will explain how we’ll do this!

1
2
3
4
5
6
---
title: "R Workshop 1"
author: "Nicola Rennie"
params: 
  hide_answers: true
---

Note that in the YAML we write true in lowercase, rather TRUE than uppercase as we would in R.

See an example of a parameterised Quarto document to create a monthly report at github.com/nrennie/capital-bikeshare-report.

Conditional content

In Quarto, conditional content allows you to show or hide content using the .content-hidden or .content-visible classes. These are most commonly combined with conditions based on output format i.e. creating content that only displays for a given output format.

This can be especially useful if you’re creating documents in multiple output formats. For example, you might wish to include an interactive plot made with plotly in the HTML version, but in a PDF version include a static plot of the data instead.

As an example, to hide content in the PDF version of the document:

1
2
3
4
5
::: {.content-hidden when-format="pdf"}

This will be hidden in the PDF output.

:::

Read quarto.org/docs/authoring/conditional.html for more examples of the different built-in options for conditional content.

Combining parameters with conditional content

Combining parameters with conditional content can help us to create two version of a document, where the answers are hidden in one of them. Think back to the hide_answers parameter we created earlier:

1
2
3
4
5
6
---
title: "R Workshop 1"
author: "Nicola Rennie"
params: 
  hide_answers: true
---
  • When our hide_answers parameter is true, we want to wrap the answers inside the .content-hidden class.
  • When our hide_answers parameter is not true, we want to leave the content as it is (visible).

We’re going to use inline R code to do that.

Before the start of the exercise answer, if hide_answers is true, we want to return the string "::: {.content-hidden}". After the answer, we also need to end the section by returning the string ":::". As a simple example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Q1: What is the mean of `mpg` in the `mtcars` data?

`r if (params$hide_answers) "::: {.content-hidden}"`

**Answer**

Calculate the mean using the `mean()` function:

```{r}
mean(mtcars$mpg)
```

`r if (params$hide_answers) ":::"`

When rendering with hide_answers: true, we create the questions worksheet:

Quarto document with questions

When rendering with hide_answers: false, we create the solutions worksheet:

Quarto document with questions and answers

Rendering multiple documents using R

You can render the two versions of the document in multiple different ways:

  • Manually change the hide_answers parameter in the YAML and hit the Render button in RStudio.
  • Pass different parameter values in via command line options.
  • Use the {quarto} R package.

The first option is fine but it’s still a manual process. The second option is also fine but you have to remember what you’ve called the parameters and how to pass them in. The third option is my favourite. With the {quarto} R package, we can use two calls to the quarto_render() function to create the two versions. If we save this code in an R script, we only need to run the script to generate the two files. You can pass a list of any parameters into the execute_params argument.

There’s one further change we need to make - file names. If we create a quarto document called Workshop.qmd, then the output document will be called Workshop.html. If we render the document twice, the second version will overwrite the first version. In the quarto_render function, we can set the output_file name to make sure we get two files: one called questions and one called solutions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Questions
quarto::quarto_render(
  "Workshop-1.qmd",
  output_file = "Workshop-1-Questions.html",
  execute_params = list(hide_answers = "true")
)

# Answers
quarto::quarto_render(
  "Workshop-1.qmd",
  output_file = "Workshop-1-Solutions.html",
  execute_params = list(hide_answers = "false")
)

Additional resources

There are plenty of other use cases for showing/hiding content based on parameters when creating Quarto reports. For example, you might create a parameterised Quarto document to create monthly reports - with month as the parameter. If it’s the last month of the year, you might want to include some charts showing an annual summary. If it’s not, you want to hide the additional content.

There are plenty of excellent blog posts out there on creating parameterised reports with Quarto. Here are a few of my favourites:

Some other useful resources:

  • The psyTeachR team have also created lots of resources for creating teaching materials using R. Check out the {webexercises} package if you want to create interactive hide/show buttons - thanks to Peter Higgins for reminding me of this package!

  • If you want to use Quarto projects, rather than just a single document, you can also use project profiles which avoids you having to write the inline R code described in this blog. The assign Quarto extension also allows you to design homework assignments and solutions in a single document using a similar approach of Quarto projects, and places the different documents into separate directories.


For attribution, please cite this work as:

Creating R tutorial worksheets (with and without solutions) using Quarto.
Nicola Rennie. May 31, 2024.
nrennie.rbind.io/blog/r-tutorial-worksheets-quarto

Licence: creativecommons.org/licenses/by/4.0