I'm teaching students to visually approximate effect sizes from typical plot types (e.g. Pearson's r from scatter and parallel coordinates plots, Kendall's τ from alluvial plots, Cramer's V from mosaic plots, ...). Dynamic generation of such tasks with the {exams}
package works like a charm.
However, as I use the questions generated with r-exams in moodle, and moodle allows to give partial credit for the numeric question type, I was wondering if there is a way to implement this via {exams}
.
A possible function converting answers to partial credit could be
I don't think it is possible to use a continuous function for assigning partial credits in Moodle. But for NUMERICAL
answers in Moodle cloze questions you can set discrete steps of partial credits, see the accompanying Moodle docs.
R/exams currently has no convenience functionality for generating such partial credits for cloze
questions with num
elements. However, it is possible to put verbatim
elements into the cloze where the exsolution
contains the Moodle NUMERICAL
syntax. See the confint3 exercise template for a worked example.
For doing something similar to what you describe, I have written a small convenience function num_to_moodle()
which approximates the continuous function by a discrete step function. The syntax is num_to_moodle(x, tol = 0, breaks = 1, range = NULL)
where:
x
is the correct solution,tol
is the tolerance for 100% of the points,breaks
is the number of additional steps towards 0% of the points, which are awarded beyond the maximum tolerance of tol * breaks
,range
is an optional range in which to consider the partial credits.As an illustration I include a short exercise that displays a scatterplot and student have to "guesstimate" the correlation coefficient r
(sampled in -0.9, -0.6, ..., 0.9). The numerical solution is set up as: num_to_moodle(r, tol = 0.06, breaks = 5, range = c(-1, 1))
. That means r
plus/minus 0.06 is accepted for 100% of the points, the next 0.06 (in both directions) for 80%, the next 0.06 for 60%, etc. Everything beyond plus/minus 0.3 yields 0%. Partial credit solution beyond -1 or 1 are excluded. (To be precise: Only midpoints outside that interval are excluded. Due to the tolerances solutions slightly outside might still yield points.)
The source code for the num_to_moodle()
function is:
num_to_moodle <- function(x, tol = 0, breaks = 1, range = NULL, digits = 5) {
## round correct solution and tolerance
breaks <- round(breaks)
x <- round(x, digits = digits)
tol <- round(tol, digits = digits)
## only correct solution without tolerance
if(breaks <= 0L || (breaks == 1L && tol <= 0)) return(paste0(":NUMERICAL:=", x))
## only correct solution with tolerance
if(breaks == 1L) return(paste0(":NUMERICAL:=", x, ":", tol))
## multiple partial solutions with tolerances
if(!(breaks %in% c(2:6, 10))) stop("'breaks' must be 0, 1, ..., 6, or 10")
perc <- exams:::moodlePercent((breaks:1L)/breaks)
y <- seq(x + 1.5 * tol, by = tol, length.out = breaks - 1L)
## set up Moodle string: :NUMERICAL:=solution1~solution2~solution3 etc.
## where each solution has: %_percent_%_solution_:_tolerance_#_comment_
perc <- c("", paste0("%", rep.int(perc[-1], 2), "%"))
x <- round(c(x, y, x + (x - y)), digits = digits)
tol <- c(tol, rep.int(tol/2, 2 * (breaks - 1L)))
if(!is.null(range)) {
if(length(range) != 2L) stop("'range' must have length 2")
ok <- (x >= range[1L]) & (x <= range[2L])
if(!ok[1L]) stop("'x' is not within 'range'")
} else {
ok <- rep.int(TRUE, 2 * breaks - 1L)
}
num <- paste0(":NUMERICAL:=", paste0(
perc[ok], ## percents
x[ok], ## solutions
":", tol[ok], ## tolerances
"", ## comments (none for now)
collapse = "~")) ## collapse
return(num)
}
The screenshot above has been generated based on:
set.seed(0)
exams2moodle("correlation.Rmd")
where the correlation.Rmd
exercise contains the following text:
```{r, include = FALSE}
r <- sample(seq(-0.9, 0.9, by = 0.3), 1)
x <- rnorm(200)
y <- r * x + rnorm(200, sd = sqrt(1 - r^2))
r <- cor(x, y)
```
Question
========
Consider the following scatterplot:
\
```{r scatterplot, echo = FALSE, results = "hide", fig.height = 5, fig.width = 5, fig.path = "", fig.cap = ""}
plot(x, y)
```
Answerlist
----------
* Estimate the correlation of x and y:
Meta-information
================
exname: Correlation
extype: cloze
exclozetype: verbatim
exsolution: `r num_to_moodle(r, tol = 0.06, breaks = 5, range = c(-1, 1))`