Search code examples
rr-exams

Partial credit for numeric exercises (e.g. for guessing tasks)?


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

formula


Solution

  • 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.)

    correlation exercise using partial credits via num_to_moodle()

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