\documentclass[11pt]{article} \usepackage{indentfirst} \usepackage{url} \usepackage[utf8]{inputenc} \RequirePackage{amsmath} % \VignetteIndexEntry{grade Package Tutorial} \begin{document} \title{grade Package Tutorial} \author{Leif Johnson} \maketitle \section{Purpose and Motivation} \subsection{Motivation} This package came about because of work I was doing with Moodle \url{http://www.moodle.org}. Specifically I made a Moodle question type plugin that allows {\it R} to be integrated directly into Moodle questions. In brief, the Moodle server sends the question to a remote server for processing. This server does stuff to the question, and can process chunks of it however it pleases. I have a server doing this with {\it R}. Each Moodle question has a collection of answers with associated feedback and grades. When a student submits an answer to a Moodle question, Moodle iterates through the answers to find a match. The student then gets the appropriate feedback and grade for that question. The Moodle question type can grade single value numerical answers, but it can't do anything more complicated than that. My solution was to build in a {\it remote grading} option into the question type. If {\it remote grading} is enabled, the Moodle server sends the student's answer back to the remote server for grading. This raises issues of text processing and 'answer' matching. This is what the {\it grade} package provides. \subsection{The Problem} We want to be able to grade questions from within the question type, with minimal amount of work from the question writer. A simple example of a question would be:\\ \noindent {\it Question:} Given that $$ follows a normal distribution with standard deviation $$, find a $95\%$ confidence interval for the mean. \noindent {\it answer:} $grade.interval( x.bar + s.dev * qnorm(0.975) * c(-1,1), studentans)$\\ So the remote server needs to evaluate each possible answer and return a binary true/false decision back to Moodle. This involves calculating the correct answer, converting the student's answer from {\it text} to the appropriate form, and deciding if they match. {\it grade} provides functions to do this. \section{Usage} Usage is intended to be very simple, and hopefully it is. Each grading function follows the same basic format: \[grade.function(correctans, studentans)\] \noindent The grading function returns {\it TRUE} if {\it studentans} is correct, and {\it FALSE} otherwise. \noindent {\it correctans} is the 'correct' answer. {\it studentans} is the student answer. Although you are not required to use them in that order, it is a good idea. Functions typically enforce stricter requirements on {\it correctans}. E.g. {\it grade.interval} requires that {\it correctans} have length 2 and errors if it does not. {\it grade.interval} does not error if {\it studentans} does not have length 2, though the answer will be incorrect. There are 5 options in common as well: \begin{itemize} \item {\it tolerance} Either a numeric or a string representing a numeric (NOT INF or NA). Usage varies by grading function, but is typically a component wise tolerance. \item {\it useeval} {\it TRUE/FALSE}. Defaults to {\it TRUE}. If {\it TRUE} text elements are evaluated using {\it eval}. If {\it FALSE} text elements are coerced with {\it as.numeric}. \item {\it usena} {\it TRUE/FALSE}. Defaults to {\it FALSE}. If {\it TRUE}, {\it NA} is an accepted value, if {\it FALSE} {\it NA} is not considered to be valid. \item {\it useinf} {\it TRUE/FALSE}. Defaults to {\it FALSE}. If {\it TRUE}, {\it Inf} and {\it -Inf} are accepted values. If {\it FALSE}, {\it Inf} and {\it -Inf} are not considered to be valid. \item {\it quiet} {\it TRUE/FALSE}. Defaults to {\it TRUE}. If {\it TRUE}, functions output as little extra information as possible. If {\it FALSE} there are more warning messages. Can be use full for debugging or tracing failures. \end{itemize} \subsection{Simple Examples} First some intervals. Note the usage of 'usena', and 'useinf'. <>= library(grade) x <- c(1,2) grade.interval(x, "[1,2]") grade.interval(x, "[1,2.02]") grade.interval(x, "[1,2.02]", tolerance=.03) grade.interval("[NA, 1]", c(NA, 1), usena=T) # usena=T not allowed in grade.interval grade.interval(c(1,Inf), c(1,Inf)) grade.interval(c(1,Inf), c(1,Inf), useinf=T) @ To grade sets there are {\it grade.set} and {\it grade.orderedset}, the only difference is that {\it grade.orderedset} requires the sets to be in the same order. <<>= set1 <- "[1,2,3,5]" set2 <- "[5,3,2,1]" grade.orderedset(set1, set2) grade.set(set1, set2) set3 <- c(NA, Inf, pi) grade.orderedset(set3, set3) grade.orderedset(set3, set3, usena=T, useinf=T) @ There is a function for discrete probability distributions as well. It can be used to verify that a student's answer could be a discrete probability dist. <<>= grade.discreteprobability(NULL, c(0, 1/2, 1/2, 0), checkcorrect=FALSE) grade.discreteprobability(NULL, c(-1, 1/2, 1/2, 1), checkcorrect=FALSE) @ And to check against a target distribution, order is enforced if {\it ordered=TRUE}, and not enforced if {\it ordered=FALSE}: <<>= correct <- c(0, 1/2, 1/4, 1/4) sans <- "[0, 1/4, 1/4, 1/2]" grade.discreteprobability(correct, sans) grade.discreteprobability(correct, sans, ordered=T) @ Lastly, we have a function to check if a number is negative, and one to compare a single value. <<>= grade.negative(NULL, -1) grade.negative(NULL, -Inf) grade.negative(NULL, -Inf, useinf=T) grade.number(1, 1) grade.number(3.141, "pi", tolerance=.002) @ \subsection{ Parsing Input} There are 4 functions involved in parsing input, {\it grade.isscalar}, {\it grade.parse}, {\it grade.parsechunk} and {\it grade.parseset}. Generally, you will just want to use {\it grade.parse}. These functions return a vector of values if all checks are passed, {\it NULL} otherwise. <<>= grade.parse("[1, 2, 3]") grade.parse("NA") grade.parse("NA", usena=T) grade.parse("[-Inf, Inf]") grade.parse("[-Inf, Inf]", useinf=T) @ Note that you can pass R objects in as well. These need to conform to whatever requirements you pass to the parse function: <<>= grade.parse(c(1,2,3)) grade.parse(NA) grade.parse(c(-Inf, Inf), useinf=T) @ Checks are made with the {\it grade.isscalar} function: <<>= grade.isscalar(c(1,2)) grade.isscalar(Inf, useinf=T) @ \subsection{eval vs as.numeric} Using {\it eval} to parse input that is coming from random sources can be a problem, but the grade.parse functions {\bf will not eval text containing parenthesis or assignment operators}. It is unlikely that anything malicious can slip through. <>= x <- NULL grade.parse("x <- 1") x grade.parse("rm(list=ls())") ls() eval(parse(text="rm(list=ls())")) ls() @ Using {\it eval} should generally be safe. It does have a significant advantage over {\it as.numeric}: <<>= grade.parse("1") grade.parse("1", useeval=F) grade.parse("pi") grade.parse("pi", useeval=F) grade.parse("1/2") grade.parse("1/2", useeval=F) @ I recommend that you use {\it eval}, which is why it's the default. \end{document}