Site icon R-bloggers

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

[This article was first published on R on Nicola Rennie, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

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:

Here’s what I don’t want:

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

What is Quarto? < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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 < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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 < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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 < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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 < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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
---

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:

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

Rendering multiple documents using R < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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

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 < svg class="anchor-symbol" aria-hidden="true" height="26" width="26" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> < path d="M0 0h24v24H0z" fill="currentColor"> < path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76.0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71.0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71.0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76.0 5-2.24 5-5s-2.24-5-5-5z">

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:

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!

To leave a comment for the author, please follow the link and comment on their blog: R on Nicola Rennie.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Exit mobile version