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:
- 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).
- An approach that will also work for printed documents (i.e. PDF output).
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? < 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
- conditional content
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 aJupyter
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 --- |
- 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:
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:
- 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 < 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:
-
Mandy Norrbo’s Generate multiple presentations with Quarto parameters blog post shows you how to create parameterised presentations using Quarto!
-
Mike Mahoney’s How to use Quarto for Parameterized Reporting blog post takes it one step further to explain how to include parameterised SQL code.
-
Jadey Ryan’s Parameterized Reporting with Quarto 2 hour workshop for R-Ladies will leave you with your own template and examples to modify for your own projects. There’s also a recording of the session, so you can follow along!
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!
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.