```{css alert, echo = FALSE, eval = !identical(Sys.getenv("IN_PKGDOWN", "false"), "true")} / alerts for standard vignettes (not pkgdown with BS5) / .alert { padding: 0.5em 1em; margin: 1em 2em; border: 1px #eee solid; border-radius: 5px; } .alert.alert-success { background-color: #e4f2dc; border-color: #ddedcf; } .alert.alert-success, .alert.alert-success code, .alert.alert-success code a:not(.close):not(.btn) { color: #5c9653; } .alert.alert-danger { background-color: #f2e2e2; border-color: #eed9dc; } .alert.alert-danger, .alert.alert-danger code, .alert.alert-danger code a:not(.close):not(.btn) { color: #ad3532; } .alert code { background-color: #ffffff55; }
```{css echo=FALSE, eval = identical(Sys.getenv("IN_PKGDOWN", "false"), "true")} /* tweak alert code colors for pkgdown */ .alert code { background-blend-mode: lighten; background-color: rgba(255, 255, 255, 0.5); background-image: none; color: inherit; }
```{css echo=FALSE} / tweak alerts for both pkgdown and standard vignettes / .alert > :last-child { margin-bottom: 0; } / tab button colors / [id^="correct"] svg { fill: var(--bs-success, #5c9653); } [id^="wrong"] svg { fill: var(--bs-danger, #ad3532); } button[role="tab"] > svg { margin-right: 3px; }
```{css callout, echo=FALSE} .callout { padding: 1.5rem 1.5rem 1.5rem 80px; margin: 1em 0; background-size: 40px; background-repeat: no-repeat; background-position: 20px 1.5em; min-height: 80px; position: relative; border: 2px solid; } .callout::before { content: "!"; display: flex; position: absolute; top: 0; left: 0; background-repeat: no-repeat; background-size: contain; width: 40px; height: 40px; transform: translate(20px, 20px) scale(1); transition: transform 0.15s linear, background-color 0.2s linear; font-weight: bold; font-size: 2em; background-color: #486181; color: white; align-items: center; justify-content: center; border-radius: 50%; } .callout:first-child { margin-top: 0; } .callout p:last-child { margin-bottom: 0; } .callout { border-color: #b5c1d2; } .callout p, .callout ol, .callout ul { color: #486181; } .callout h3, .callout h4 { color: #294263; } .callout p code, .callout ol code, .callout ul code, .callout h3 code, .callout h4 code { background-color: #e9f2fd; color: #294263; } .callout blockquote { border-color: #b5c1d1; } @media only screen and (max-width: 400px) { .callout::before { transform: translate(-10px, -15px) scale(0.9); } .callout { padding-left: 1.5rem; padding-top: 1.5rem; margin-top: 1.5rem; } }
library(gradethis) library(tblcheck) gradethis_setup( pass.praise = TRUE, fail.hint = TRUE, fail.encourage = TRUE, maybe_code_feedback.before = "\n\n" ) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) set.seed(424341) options(max.print = 20, width = 70) source(system.file("internal", "fake-grading.R", package = "tblcheck"), local = TRUE) fake_grade_example <- function(example) { ex_prefix <- strsplit(example, "-")[[1]][1] fake_grade_chunks( user = example, setup = paste0(ex_prefix, "-setup"), solution = paste0(ex_prefix, "-solution"), check = paste0(ex_prefix, "-check") ) } icons <- list( code = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V1.75a.25.25 0 00-.25-.25H1.75zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm9.22 3.72a.75.75 0 000 1.06L10.69 8 9.22 9.47a.75.75 0 101.06 1.06l2-2a.75.75 0 000-1.06l-2-2a.75.75 0 00-1.06 0zM6.78 6.53a.75.75 0 00-1.06-1.06l-2 2a.75.75 0 000 1.06l2 2a.75.75 0 101.06-1.06L5.31 8l1.47-1.47z"></path></svg>', correct = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 16A8 8 0 108 0a8 8 0 000 16zm3.78-9.72a.75.75 0 00-1.06-1.06L6.75 9.19 5.28 7.72a.75.75 0 00-1.06 1.06l2 2a.75.75 0 001.06 0l4.5-4.5z"></path></svg>', wrong = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>' )
To help you get a feel for the kind of advice tblcheck provides,
we've collected a few example exercises
that use grade_this_table()
or grade_this_vector()
from tblcheck
to grade a learnr exercise.
In the examples below,
we'll show the specific R chunks you'd need to prepare the exercise.
Globally,
we assume learnr and gradethis are already loaded,
and we've made a few adjustments to the default gradethis options using gradethis::gradethis_setup()
:
```r`r ''` library(learnr) library(gradethis) library(tblcheck) gradethis_setup( pass.praise = TRUE, fail.hint = TRUE, fail.encourage = TRUE, maybe_code_feedback.before = "\n\n" ) ```
Note that in all examples below we're using either grade_this_table()
or grade_this_vector()
without any additional customizations.
We've also made sure that each exercise contains a -solution
chunk.
(Read more about how to set up an exercise for use with tblcheck.)
The first example repeats the example presented in the Get started article.
The goal is for the learner to use the tibble::tibble()
function to create a table,
but it takes them a few tries.
Prompt
Create atibble
with three columns:food
,vegetable
, andcolor
.
- In
food
, store "lettuce" and "tomato".- In
vegetable
, store whether the food is a vegetable.- In
color
, store the color of the food.
r icons$code
Exercise Code```r library(tibble) ``` ```r ``` ```r tibble( food = c("lettuce", "tomato"), vegetable = c(TRUE, FALSE), color = c("green", "red") ) ``` ```r grade_this_table() ```
r icons$correct
Correct Answerfake_grade_example("tomato-solution")
r icons$wrong
Wrong Classlist( food = "lettuce", fruit = "TRUE", color = "green" )
fake_grade_example("tomato-wrong-class")
r icons$wrong
Wrong Namestibble( food = "lettuce", fruit = "TRUE", color = "green" )
fake_grade_example("tomato-wrong-names")
r icons$wrong
Wrong Lengthtibble( food = "lettuce", vegetable = "TRUE", color = "green" )
fake_grade_example("tomato-wrong-length")
r icons$wrong
Wrong Column Classtibble( food = c("lettuce", "tomato"), vegetable = c("TRUE", "TRUE"), color = c("green", "red") )
fake_grade_example("tomato-wrong-column-class")
r icons$wrong
Wrong Column Valuestibble( food = c("lettuce", "tomato"), vegetable = c(TRUE, TRUE), color = c("green", "red") )
fake_grade_example("tomato-wrong-column-values")
transmute()
{.tabset}The first example shows all of the problems that tblcheck will discover. This example, however, is more realistic.
In this problem students are asked to use the dplyr::transmute()
function
to transform the starwars
dataset.
Prompt
Consider thestarwars
dataset. Usetransmute()
to turn convertheight
from centimeters into inches andmass
from kilgrams into pounds, keeping only those columns.
r icons$code
Exercise Code```r library(dplyr) ``` ```r ``` ```r starwars %>% transmute( height = height / 2.54, mass = mass * 2.205 ) ``` ```r grade_this_table(check_column_order = TRUE) ```
r icons$correct
Correct AnswerThe correct answer uses dplyr::transmute()
to divide the height
column by 2.54
(to convert height from cm to in)
and to multiply the mass
column by 2.205
(to convert kg to lb).
Note that transmute()
keeps only the columns named in its calculations,
so our final table has only two columns.
r icons$wrong
Wrong FunctionIn this example, the student uses dplyr::mutate()
,
which is certainly a reasonable choice but not what was requested.
As a result, their submission contains more columns than expected.
starwars %>% mutate( height = height / 2.54, mass = mass * 2.205 )
fake_grade_example("starwars-wrong-mutate")
r icons$wrong
Wrong Column OrderHere the student has done more or less the right thing, but they specified the columns in the opposite order.
starwars %>% transmute( mass = mass * 2.205, height = height / 2.54 )
fake_grade_example("starwars-wrong-columns")
If we decide we don't mind if the columns aren't in the right order,
we could set check_column_order = FALSE
in the check code for this exercise.
```r grade_this_table(check_column_order = FALSE) ```
fake_grade_chunks("starwars-wrong-columns", check = "starwars-check-no-order", setup = "starwars-setup", solution = "starwars-solution")
r icons$wrong
Wrong Column ClassesIf the student were to somehow create columns with an incorrect class, e.g. integer values where doubles were expected, tblcheck will alert them to their mistake.
starwars %>% transmute( mass = as.integer(mass * 2.205), height = as.integer(height / 2.54) )
fake_grade_example("starwars-wrong-class")
r icons$wrong
Wrong CalculationIf the values are completely wrong —
here the student is multiplying height
by 2.54
rather than dividing —
then tblcheck points out which column is incorrect
and gives a hint about the expected values.
starwars %>% transmute( height = height * 2.54, mass = mass * 2.205 )
fake_grade_example("starwars-wrong-values")
This example comes from a lesson on factors
in R.
Students have just learned about functions from the forcats package,
and are now asked to practice using forcats::fct_reorder()
.
Prompt
Consider themarital_age
dataset. Usefct_reorder()
to turn themarital
column into a factor with levels ordered based onavg_age
.
r icons$code
Exercise CodeWe created a new table specifically for this exercise in the factor-setup
chunk,
drawing from the data provided in forcats::gss_cat
.
```r library(dplyr) library(forcats) marital_age <- gss_cat %>% mutate(marital = as.character(marital)) %>% group_by(marital) %>% summarize(avg_age = mean(age, na.rm = TRUE)) %>% ungroup() ``` ```r marital_age ``` ```r marital_age %>% mutate(marital = fct_reorder(marital, avg_age)) ``` ```r grade_this_table() ```
Since students aren't familiar with the marital_age
table,
we've included the starter code so they can see the initial state of the table
when they start the exercise.
r icons$correct
Correct AnswerIn the expected solution,
the student will use the forecats::fct_reorder()
function
to transform the marital
column,
reordering its factor levels by increasing avg_age
.
fake_grade_chunks("factor-solution", solution = "factor-solution", check = "factor-check", setup = "factor-setup")
r icons$wrong
Wrong FunctionHere the student forgets about the forcats::fct_reorder()
function
and tries to use order()
to set the order of the factor levels in the marital
column.
marital_age %>% mutate(marital = order(marital, avg_age))
fake_grade_chunks("factor-wrong-class", solution = "factor-solution", check = "factor-check", setup = "factor-setup")
r icons$wrong
Wrong ColumnIn this case the student remembers to use forecats::fct_reorder()
but incorrectly assigns the result to an unexpected column.
marital_age %>% mutate(marital_fct = fct_reorder(marital, avg_age))
fake_grade_chunks("factor-wrong-column", solution = "factor-solution", check = "factor-check", setup = "factor-setup")
r icons$wrong
Wrong DirectionIn another case, the student uses forecats::fct_reorder()
but sets .desc = TRUE
,
resulting in an unexpected ordering of the factor levels.
marital_age %>% mutate(marital = fct_reorder(marital, avg_age, .desc = TRUE))
fake_grade_chunks("factor-wrong-subset", solution = "factor-solution", check = "factor-check", setup = "factor-setup")
In addition to table-checking functions, tblcheck includes functions for checking vectors — after all, columns in a table are vectors!
Here's an example exercise from a tutorial on string transformations with the stringr package. Students are asked to practice concepts they just discovered in the tutorial:
Prompt
Consider thefruit
data. Use a function from stringr to create a vector containing only the fruits with more than one word in their name.Hint: a fruit named with more than one word also has at least one space in its name.
r icons$code
Exercise CodeIn the exercise markdown,
we call grade_this_vector()
in the string-check
chunk
to automatically compare the result of the student's submission
with our string-solution
.
```r library(dplyr) library(stringr) ``` ```r ``` ```r str_subset(fruit, " ") ``` ```r grade_this_vector() ```
Note that fruit
is from stringr::fruit
,
a vector containing
r length(stringr::fruit)
fruit names.
head(stringr::fruit, 20)
r icons$correct
Correct Answerr length(stringr::str_subset(stringr::fruit, " "))
of the
r length(stringr::fruit)
fruits have more than one word.
What's your favorite two-worded fruit name?
(Mine is the purple mangosteen.)
fake_grade_example("string-solution")
r icons$wrong
Wrong ClassThis student submission makes a potentially common mistake
of hoping that stringr::str_which()
will return the fruit
which have pattern
.
Instead, str_which()
follows the base R function which()
,
and returns an integer
index indicating the items in fruit
where the pattern
is matched.
str_which(fruit, " ")
fake_grade_example("string-wrong-which")
r icons$wrong
Wrong LengthIn another mistake example,
this student probably expected stringr::str_extract()
to extract elements from the vector.
However, str_extract()
instead extracts the pattern from each item,
so it returns a vector with the same length as the input.
str_extract(fruit, " ")
fake_grade_example("string-wrong-extract")
r icons$wrong
Wrong SubsetFinally, a student might have chosen the right function, but they missed our hint and came up with a pattern that's too clever. Here the student probably looked at the first 20 fruit names and took a guess at the answer.
str_subset(fruit, " (pepper|orange|melon|fruit)")
fake_grade_example("string-wrong-subset")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.