Author: Subhabrata
Date: 2020-05-19

# QAIG: Automatic Item Generator for Quantitative Multiple-Choice Items

#### Subhabrata

#### 2020-05-19

Automatic item generation (AIG) is an old concept which was first introduced by John R. Bormuth (1969). This process has not been advanced that much in past few decades. Due to recent demand of test items and high costs in item development, this process has become in light of many educators and psychometricians again.

QAIG is an effort to provide tool by means of R function for generating group of dissimilar sibling items from Quantitative Multiple-Choice type parent item model. When we think about generating sibling items from a parent item, main focus becomes to have:

- generated items that look dissimilar to each other as much as possible.
- effective distractors in each of the items.

QAIG package enables users to generate large number of items from a parent item:

- by applying possible dissimilarities within the sibling items.
- by taking care of effectiveness of the distractors for each sibling item.

## Usage of `itemgen()` function and its arguments.

QAIG package includes a function called `itemgen`:

`itemgen(stem_text = stem_text, formulae = formulae, N = N, C, ans_key, options_affix, save.csv)`

The usage of this function is illustrated here.

### 1: Formation of the parent item.

For `stem_text`: User of this package should construct a parent item model i.e. a stem text along with desired number of response choices. All the changeable elements (variables) in the stem of an item should be replaced by specific notations enclosed by `[]`. Character variable should be written as `[C1], [C2], [C3] … etc.`. Number variables should be written as `[N1], [N2], [N3] … etc.`.

For `N` and `C`: For each of the number variables (`[N1], [N2], [N3], …`) in the stem text, user has to decide a set of numbers that should be declared as vectors `n1, n2, n3, …` respectively. Vectors of characters `c1, c2, c3, …` for each character variable should also be declared similar way. All the input number vectors and input character vectors should be wrapped within `list` separately as the inputs for `N` and `C` in `itemgen()` function.

For `formulae`: A generic formula for each of the response choices should be provided using the names (`n1, n2, n3, …`) of input number vectors. All the formulae need to be written in new line separately and should be wrapped as a raw text as an input for `formulae` in `itemgen()` function. The correct response choice should be marked by “~” and distractors should be marked by “?” in the formulae.

Note: User must have stem text, formulae and the `list` of input number vector(s) to work with `itemgen()` function, as `stem_text`, `formulae` and `N` are the three default arguments in this function.

### Example I

    library(QAIG)

    stem_text <- "What is the sum of first [N1] [C1] ?"

    n1 <- c(5, 8, 11, 14, 17)
    c1 <- c("natural numbers", "non-zero positive integers")

    N <- list(n1 = n1)
    C <- list(c1 = c1)

    formulae <- "Option_A ? 2*n1-1
    Option_B ? 3*n1-2
    Option_C ~ n1*(n1+1)/2
    Option_D ? n1*(n1-1)/2
    "

    ## itemgen() function can be used as:
    newitems <- QAIG::itemgen(stem_text = stem_text, formulae = formulae, N = N, C = C)

    newitems
    #>            [Q1]
    #> Stem       "What is the sum of first 5 natural numbers ?"
    #> Option_A   " 9"
    #> Option_B   "13"
    #> Option_C   " 15"
    #> Option_D   " 10"
    #> Answer_Key "Option_C"
    #>            [Q2]
    #> Stem       "What is the sum of first 8 non-zero positive integers ?"
    #> Option_A   "15"
    #> Option_B   "22"
    #> Option_C   " 36"
    #> Option_D   " 28"
    #> Answer_Key "Option_C"
    #>            [Q3]
    #> Stem       "What is the sum of first 11 natural numbers ?"
    #> Option_A   "21"
    #> Option_B   "31"
    #> Option_C   " 66"
    #> Option_D   " 55"
    #> Answer_Key "Option_C"
    #>            [Q4]
    #> Stem       "What is the sum of first 14 non-zero positive integers ?"
    #> Option_A   "27"
    #> Option_B   "40"
    #> Option_C   "105"
    #> Option_D   " 91"
    #> Answer_Key "Option_C"
    #>            [Q5]
    #> Stem       "What is the sum of first 17 natural numbers ?"
    #> Option_A   "33"
    #> Option_B   "49"
    #> Option_C   "153"
    #> Option_D   "136"
    #> Answer_Key "Option_C"

### 2: Using `options_affix` argument.

Suffix or prefix or both can be attached with the numeric values in the response choices by using `options_affix` argument of `itemgen()` function. Response choice names in `formulae` and in `options_affix` must be same. If user wants to declare difficulty level of the item, that also can be added within `options_affix`.

### Example II

    library(QAIG)

    stem_text <- "[C1] has $ [N1] and [C2] has $ [N2] . If [C2] takes $ [N3] from [C1] later, then how much more amount than [C1] does [C3] have now?"

    c1 <- c('Sam', 'Sean')
    c2 <- c('Max', 'Martha', 'Mandy')
    c3 <- c('he', 'she', 'she')

    n1 <- c(4, 5, 6, 7)
    n2 <- c(8, 9, 10)
    n3 <- c(2, 3)

    C <- list(c1 = c1, c2 = c2, c3 = c3)
    N <- list(n1 = n1, n2 = n2, n3 = n3)

    formulae <- "Option_A ? (n1 + n2)
    Option_B ~ (n2 + 2*n3 - n1)
    Option_C ? (n1 + n2 + 1)
    Option_D ? (n1 + n2 - 2)
    Option_E ? (n2 + n3 - n1)
    "
    options_affix <- list(Option_A = c('$ ', ''), Option_B = c('$ ', ''), Option_C = c('$ ', ''), Option_D = c('$ ', ''), Option_E = c('$ ', ''), Difficulty = 'EASY')

    ## itemgen() function can be used as:
    newitems <- QAIG::itemgen(stem_text = stem_text, formulae = formulae, N = N, C = C, options_affix = options_affix)

    newitems[, c(1, 4, 21, 24)]
    #>            [Q1]
    #> Stem       "Sam has $ 4 and Max has $ 8 . If Max takes $ 2 from Sam later, then how much more amount than Sam does he have now?"
    #> Option_A   "$ 12"
    #> Option_B   "$ 8"
    #> Option_C   "$ 13"
    #> Option_D   "$ 10"
    #> Option_E   "$ 6"
    #> Difficulty "EASY"
    #> Answer_Key "Option_B"
    #>            [Q4]
    #> Stem       "Sean has $ 7 and Max has $ 8 . If Max takes $ 2 from Sean later, then how much more amount than Sean does he have now?"
    #> Option_A   "$ 15"
    #> Option_B   "$ 5"
    #> Option_C   "$ 16"
    #> Option_D   "$ 13"
    #> Option_E   "$ 3"
    #> Difficulty "EASY"
    #> Answer_Key "Option_B"
    #>            [Q21]
    #> Stem       "Sam has $ 4 and Mandy has $ 10 . If Mandy takes $ 3 from Sam later, then how much more amount than Sam does she have now?"
    #> Option_A   "$ 14"
    #> Option_B   "$ 12"
    #> Option_C   "$ 15"
    #> Option_D   "$ 12"
    #> Option_E   "$ 9"
    #> Difficulty "EASY"
    #> Answer_Key "Option_B"
    #>            [Q24]
    #> Stem       "Sean has $ 7 and Mandy has $ 10 . If Mandy takes $ 3 from Sean later, then how much more amount than Sean does she have now?"
    #> Option_A   "$ 17"
    #> Option_B   "$ 9"
    #> Option_C   "$ 18"
    #> Option_D   "$ 15"
    #> Option_E   "$ 6"
    #> Difficulty "EASY"
    #> Answer_Key "Option_B"

### User can include a response choice with only text using `options_affix`.

### Example III

    library(QAIG)

    stem_text <- "[C1] bought a [C2] at $ [N1] . [C3] spent $ [N2] for repairing it and then sold it at $ [N3] . What was [C4] percentage of profit or loss?"

    c1 <- c('Samuel', 'April')
    c2 <- c('motorcycle', 'moped')
    c3 <- c('He', 'She')
    c4 <- c('his', 'her')

    n1 <- c(925, 862, 784)
    n2 <- c(92, 102)
    n3 <- 1030

    C <- list(c1 = c1, c2 = c2, c3 = c3, c4 = c4)
    N <- list(n1 = n1, n2 = n2, n3 = n3)

    formulae <- "Option_A ? round((n2/n1)*100, 2)
    Option_B ? round(((n3-n2-n1)/n3)*100, 1)
    Option_C ? round(((n3-n2-n1+0)/n3)*100, 1)
    Option_D ~ round((((n3-n2-n1)/(n1+n2))*100), 2)
    "
    options_affix <- list(Option_A = c('', '% loss'), Option_B = c('', '% profit'), Option_C = c('', '% loss'), Option_D = c('', '% profit'), Option_E = 'No profit no loss')

    ## itemgen() function can be used as:
    newitems <- QAIG::itemgen(stem_text = stem_text, formulae = formulae, C = C, N = N, options_affix = options_affix)

    newitems[, c(1, 2, 3, 6)]
    #>            [Q1]
    #> Stem       "Samuel bought a motorcycle at $ 925 . He spent $ 92 for repairing it and then sold it at $ 1030 . What was his percentage of profit or loss?"
    #> Option_A   "9.95% loss"
    #> Option_B   "1.3% profit"
    #> Option_C   "1.3% loss"
    #> Option_D   "1.28% profit"
    #> Option_E   "No profit no loss"
    #> Answer_Key "Option_D"
    #>            [Q2]
    #> Stem       "April bought a moped at $ 862 . She spent $ 92 for repairing it and then sold it at $ 1030 . What was her percentage of profit or loss?"
    #> Option_A   "10.67% loss"
    #> Option_B   "7.4% profit"
    #> Option_C   "7.4% loss"
    #> Option_D   "7.97% profit"
    #> Option_E   "No profit no loss"
    #> Answer_Key "Option_D"
    #>            [Q3]
    #> Stem       "Samuel bought a motorcycle at $ 784 . He spent $ 92 for repairing it and then sold it at $ 1030 . What was his percentage of profit or loss?"
    #> Option_A   "11.73% loss"
    #> Option_B   "15% profit"
    #> Option_C   "15% loss"
    #> Option_D   "17.58% profit"
    #> Option_E   "No profit no loss"
    #> Answer_Key "Option_D"
    #>            [Q6]
    #> Stem       "April bought a moped at $ 784 . She spent $ 102 for repairing it and then sold it at $ 1030 . What was her percentage of profit or loss?"
    #> Option_A   "13.01% loss"
    #> Option_B   "14% profit"
    #> Option_C   "14% loss"
    #> Option_D   "16.25% profit"
    #> Option_E   "No profit no loss"
    #> Answer_Key "Option_D"

### 3: Using `ans_key` argument.

Answer key of the items can be declared using `ans_key` argument also. In case of any text response choice becomes the answer key, user should declare the answer key using `ans_key` argument. In this case all the response choices in `formulae` must be marked by “?” only.

### Example IV

    library(QAIG)

    stem_text <- "A [C1] was delayed somewhere for [N1] minutes but made up for the delay on a section of [N2] km travelling at a speed of [N3] km per hour higher than that which accorded the schedule. What was the speed of the [C1] that accorded the schedule?"

    c1 <- c('car', 'bus', 'truck', 'train')

    n1 <- c(16, 18, 20, 22, 24)
    n2 <- c(80, 90, 100, 110)
    n3 <- c(10, 12, 15, 18)

    C <- list(c1 = c1)
    N <- list(n1 = n1, n2 = n2, n3 = n3)

    formulae <- "p <- 1
    Option_A ? round((-n3 + sqrt(n3^2 - 4*p*(-60*n2*n3/n1)))/2,2)
    Option_B ? round((-n3 - sqrt(n3^2 - 4*p*(-60*n2*n3/n1)))*(-1)/2-20,2)
    Option_C ? round((-n3 - sqrt(n3^2 - 4*p*(-60*n2*n3/n1)))*(-1)/2,2)
    Option_D ? round((-n3 + sqrt(n3^2 - 4*p*(-60*n2*n3/n1)))/2+30,2)
    "
    options_affix <- list(Option_A = c('', ' km/hr'), Option_B = c('', ' km/hr'), Option_C = c('', ' km/hr'), Option_D = c('', ' km/hr'), Option_E = 'Cannot be determined', Difficulty = 'HARD')

    ## itemgen() function can be used as:
    newitems <- QAIG::itemgen(stem_text = stem_text, formulae = formulae, N = N, C = C, ans_key = 'Option_A', options_affix = options_affix)

    newitems[, c(1, 2, 79, 80)]
    #>            [Q1]
    #> Stem       "A car was delayed somewhere for 16 minutes but made up for the delay on a section of 80 km travelling at a speed of 10 km per hour higher than that which accorded the schedule. What was the speed of the car that accorded the schedule?"
    #> Option_A   "50 km/hr"
    #> Option_B   "40 km/hr"
    #> Option_C   "60 km/hr"
    #> Option_D   "80 km/hr"
    #> Option_E   "Cannot be determined"
    #> Difficulty "HARD"
    #> Answer_Key "Option_A"
    #>            [Q2]
    #> Stem       "A bus was delayed somewhere for 18 minutes but made up for the delay on a section of 80 km travelling at a speed of 10 km per hour higher than that which accorded the schedule. What was the speed of the bus that accorded the schedule?"
    #> Option_A   "46.88 km/hr"
    #> Option_B   "36.88 km/hr"
    #> Option_C   "56.88 km/hr"
    #> Option_D   "76.88 km/hr"
    #> Option_E   "Cannot be determined"
    #> Difficulty "HARD"
    #> Answer_Key "Option_A"
    #>            [Q79]
    #> Stem       "A truck was delayed somewhere for 22 minutes but made up for the delay on a section of 110 km travelling at a speed of 18 km per hour higher than that which accorded the schedule. What was the speed of the truck that accorded the schedule?"
    #> Option_A   "65.03 km/hr"
    #> Option_B   "63.03 km/hr"
    #> Option_C   "83.03 km/hr"
    #> Option_D   "95.03 km/hr"
    #> Option_E   "Cannot be determined"
    #> Difficulty "HARD"
    #> Answer_Key "Option_A"
    #>            [Q80]
    #> Stem       "A train was delayed somewhere for 24 minutes but made up for the delay on a section of 110 km travelling at a speed of 18 km per hour higher than that which accorded the schedule. What was the speed of the train that accorded the schedule?"
    #> Option_A   "61.93 km/hr"
    #> Option_B   "59.93 km/hr"
    #> Option_C   "79.93 km/hr"
    #> Option_D   "91.93 km/hr"
    #> Option_E   "Cannot be determined"
    #> Difficulty "HARD"
    #> Answer_Key "Option_A"

### 4: Using supporting values and function(s) within `formulae`.

User may want to provide supporting values or a self-defined function to use during formulation of the response choice models. Those can be supplied within `formulae` by writing in separate lines.

Note: Formulation of each response choice model (formula) in `formulae` must produce a single numeric value.

### Example V

    library(QAIG)

    stem_text <- "Sum of present ages of [C1] and [C2] [C3] is [N1] . After [N2] years [C2] [C3] will be thrice as old as [C1] . The present age of [C2] [C3] is"

    n1 <- c(74, 80, 72, 68)
    n2 <- c(8, 10)

    c1 <- c('Sophia', 'Viktor', 'Julia', 'Andy')
    c2 <- c('her', 'his')
    c3 <- c('father', 'mother')

    N <- list(n1 = n1, n2 = n2)
    C <- list(c1 = c1, c2 = c2, c3 = c3)

    formulae <- "a <- 5
    sol <- function(x, y){
      A <- matrix(c(1, 1, 1, -3), nrow=2)
      B <- matrix(c(x, 2*y), nrow=2)
      return((as.matrix(solve(A)%*%B)))
    }

    Option_A ? round(sol(n1, n2)[1,]+a, 2)
    Option_B ? round(sol(n1, n2)[2,]+a, 2)
    Option_C ~ round(sol(n1, n2)[1,], 2)
    Option_D ? round(sol(n1, n2)[2,], 2)
    Option_E ? round(sol(n1, n2)[1,]-a, 2)
    "
    options_affix <- list(Option_A = c('', ' years'), Option_B = c('', ' years'), Option_C = c('', ' years'), Option_D = c('', ' years'), Option_E = c('', ' years'), Difficulty = 'MEDIUM')

    ## itemgen() function can be used as:
    newitems <- QAIG::itemgen(stem_text = stem_text, formulae = formulae, N = N, C = C, options_affix = options_affix)
    newitems[, c(1, 2, 7, 8)]
    #>            [Q1]
    #> Stem       "Sum of present ages of Sophia and her father is 74 . After 8 years her father will be thrice as old as Sophia . The present age of her father is"
    #> Option_A   "64.5 years"
    #> Option_B   "19.5 years"
    #> Option_C   "59.5 years"
    #> Option_D   "14.5 years"
    #> Option_E   "54.5 years"
    #> Difficulty "MEDIUM"
    #> Answer_Key "Option_C"
    #>            [Q2]
    #> Stem       "Sum of present ages of Viktor and his mother is 80 . After 8 years his mother will be thrice as old as Viktor . The present age of his mother is"
    #> Option_A   "69 years"
    #> Option_B   "21 years"
    #> Option_C   "64 years"
    #> Option_D   "16 years"
    #> Option_E   "59 years"
    #> Difficulty "MEDIUM"
    #> Answer_Key "Option_C"
    #>            [Q7]
    #> Stem       "Sum of present ages of Julia and her father is 72 . After 10 years her father will be thrice as old as Julia . The present age of her father is"
    #> Option_A   "64 years"
    #> Option_B   "18 years"
    #> Option_C   "59 years"
    #> Option_D   "13 years"
    #> Option_E   "54 years"
    #> Difficulty "MEDIUM"
    #> Answer_Key "Option_C"
    #>            [Q8]
    #> Stem       "Sum of present ages of Andy and his mother is 68 . After 10 years his mother will be thrice as old as Andy . The present age of his mother is"
    #> Option_A   "61 years"
    #> Option_B   "17 years"
    #> Option_C   "56 years"
    #> Option_D   "12 years"
    #> Option_E   "51 years"
    #> Difficulty "MEDIUM"
    #> Answer_Key "Option_C"

### 5: Using `save.csv` argument

Generated items can be saved in a `.csv` file by using the `save.csv` argument. If a name of the file is passed to this argument, all the generated items will be saved automatically in `.csv` format in the working directory.

As example:

`itemgen(stem_text, formulae, N, C = C, options_affix = options_affix, save.csv = 'New Items')`

Reference: AIG for Developing Mathematics Achievement Items