Search code examples
rr-exams

exams num_to_schoice to include wrong alternatives


I am using num_to_choice to generate 5 options for a numeric exercise using the r exams package.

I want my sc$questions to include the correct solution (res) plus two calculated wrong solutions (err1 and err2). I am using this code:

sc <- num_to_schoice(res, digits=3, wrong=c(err1, err2))

I do get the five options generated, one with the correct solution (res), but none of the other four options include err1 and err2. Reading the num_to_choice documentation I find:

"The wrong solutions can be provided or are generated randomly. If wrong is provided only up to 2 elements of it are used in order to assure some random solutions."

However, in my case none of the err1 or err2 options are included in the sc$questions. What am I missing here? How can I force the err options to be included in the sc$questions?

Here is an reproducible example:

v <- c(1225,4000,1372,974,956,940,1532,969,1070,685,220,407,903,239,283,368,483,352,77,699)
res <- sd(v)
err1 <- IQR(v)
err2 <- sqrt(var(v)*(length(v-1)/length(v)))
set.seed(1)
sc <- num_to_schoice(res, digits=3, wrong=c(err1, err2), range=c(0,max(v)))

and here is the solution generated by sc$questions:

The result is "specified 'wrong' is too small for 'delta'"


Solution

  • In your example

    In your case err2 and res are identical:

    identical(res, err2)
    ## [1] TRUE
    

    That is why num_to_schoice() returns NULL along with a warning that should inform you that the difference from res to the wrong argument is too small for the given delta. Hence, it is not possible to include res as the correct solution and err2 as a wrong solution.

    The source of the problem is that you compute

    length(v - 1)
    

    which is the same as length(v) rather than the intended

    length(v) - 1
    

    If you do

    err2 <- sqrt(var(v) * (length(v) - 1)/length(v))
    

    Then you get the desired

    set.seed(1)
    num_to_schoice(res, digits = 3, wrong = c(err1, err2))
    ## $solutions
    ## [1] FALSE FALSE FALSE  TRUE FALSE
    ## 
    ## $questions
    ## [1] "$819.235$"  "$634.000$"  "$996.905$"  "$840.517$"  "$1222.200$"
    

    More generally

    By design num_to_schoice() is rather conservative: If it thinks it cannot find a set of items with the desired properties it returns NULL rather than trying to find an approximate solution. This is to avoid randomly getting exercises that might be simpler than intended.

    Another typical example is that the range is too small and/or the delta too large to produce a list of five answer options that conform with all arguments. For example, let's assume we have the following correct solution and two distractors:

    res <- 1.234
    err1 <- 2.345
    err2 <- 3.456
    

    Then, the command you posted above will not work:

    num_to_schoice(res, digits = 3, wrong = c(err1, err2))
    ## NULL
    ## Warning message:
    ## In num_to_schoice(res, digits = 3, wrong = c(err1, err2)) :
    ##   specified 'range' is too small for 'delta'
    

    The reason is that the default delta is 1 and the default range is c(0.5, 1.5) * res in this case. And this is not large enough to accomodate five answers with a minimum delta of 1.

    But if you increase the range (or alternatively decrease the delta) you get:

    set.seed(1)
    num_to_schoice(res, digits = 3, wrong = c(err1, err2), range = c(0, 10))
    ## $solutions
    ## [1] FALSE FALSE FALSE  TRUE FALSE
    ## 
    ## $questions
    ## [1] "$3.456$" "$2.345$" "$4.496$" "$1.234$" "$9.195$"
    

    where res ends up in position 4 while err1 and err2 are in positions 2 and 1, respectively.