library(irt)

Basic Objects

irt package contains many useful functions commonly used in psychometrics.

Item parameters are defined within three main objects types:

Item

In order to create an Item object, the psychometric model and item parameter values is sufficient. Specifying an item_id field is required if Item will be used within an Itempool or Testlet.

3PL Model

A three parameter logistic model item (3PL) requires a, b and c parameters to be specified:

item1 <- item(a = 1.2, b = -.8, c = .33, model = "3PL")
item1
#> A '3PL' item.
#> Model:   3PL (Three-Parameter Logistic Model)
#> Model Parameters:
#>   a = 1.2
#>   b = -0.8
#>   c = 0.33
#>   D = 1
#> 
#> --------------------------

a is the item discrimination, b is the item difficulty and c is the pseudo-guessing parameter.

By default, the value of scaling constant D is specified as 1. But it can be overridden:

item1 <- item(a = 1.2, b = -.8, c = .33, D = 1.7, model = "3PL")
item1
#> A '3PL' item.
#> Model:   3PL (Three-Parameter Logistic Model)
#> Model Parameters:
#>   a = 1.2
#>   b = -0.8
#>   c = 0.33
#>   D = 1.7
#> 
#> --------------------------

item_id and content field can be specified as well:

item1 <- item(a = 1.2, b = -.8, c = .33, D = 1.7, model = "3PL", 
              item_id = "ITM384", content = "Quadratic Equations")
item1
#> A '3PL' item.
#> Item ID:      ITM384
#> Model:   3PL (Three-Parameter Logistic Model)
#> Content: Quadratic Equations
#> Model Parameters:
#>   a = 1.2
#>   b = -0.8
#>   c = 0.33
#>   D = 1.7
#> 
#> --------------------------

Additional fields can be added through misc field:

item1 <- item(a = 1.2, b = -.8, c = .33, D = 1.7, model = "3PL", 
              item_id = "ITM384", content = "Quadratic Equations", 
              misc = list(key = "A", 
                          enemies = c("ITM664", "ITM964"), 
                          seed_year = 2020, 
                          target_grade = "11")
              )
item1
#> A '3PL' item.
#> Item ID:      ITM384
#> Model:   3PL (Three-Parameter Logistic Model)
#> Content: Quadratic Equations
#> Model Parameters:
#>   a = 1.2
#>   b = -0.8
#>   c = 0.33
#>   D = 1.7
#> 
#> Misc: 
#>   key: "A"
#>   enemies: "ITM664", "ITM964"
#>   seed_year: 2020
#>   target_grade: "11"
#> --------------------------

An item characteristic curve can be plotted using plot function:

plot(item1)

plot of chunk unnamed-chunk-6

Rasch Model

Rasch model item requires b parameter to be specified:

item2 <- item(b = -.8, model = "Rasch")
item2
#> A 'Rasch' item.
#> Model:   Rasch (Rasch Model)
#> Model Parameters:
#>   b = -0.8
#> 
#> --------------------------

For Rasch model, D parameter cannot be specified.

1PL Model

A one-parameter model item requires b parameter to be specified:

item3 <- item(b = -.8, D = 1.7, model = "1PL")
item3
#> A '1PL' item.
#> Model:   1PL (One-Parameter Logistic Model)
#> Model Parameters:
#>   b = -0.8
#>   D = 1.7
#> 
#> --------------------------

2PL Model

A two-parameter model item requires a and b parameters to be specified:

item4 <- item(a = 1.2, b = -.8, D = 1.702, model = "2PL")
item4
#> A '2PL' item.
#> Model:   2PL (Two-Parameter Logistic Model)
#> Model Parameters:
#>   a = 1.2
#>   b = -0.8
#>   D = 1.702
#> 
#> --------------------------

4PL Model

A four-parameter model item requires a, b, c and d parameters to be specified:

item5 <- item(a = 1.06, b = 1.76, c = .13, d = .98, model = "4PL", 
              item_id = "itm-5")
item5
#> A '4PL' item.
#> Item ID:      itm-5
#> Model:   4PL (Four-Parameter Logistic Model)
#> Model Parameters:
#>   a = 1.06
#>   b = 1.76
#>   c = 0.13
#>   d = 0.98
#>   D = 1
#> 
#> --------------------------

d is the upper-asymptote parameter.

Graded Response Model (GRM)

A Graded Response model item requires a and b parameters to be specified. b parameters is ascending vector of threshold parameters:

item6 <- item(a = 1.22, b = c(-1.9, -0.37, 0.82, 1.68), model = "GRM", 
              item_id = "itm-6")
item6
#> A 'GRM' item.
#> Item ID:      itm-6
#> Model:   GRM (Graded Response Model)
#> Model Parameters:
#>   a = 1.22
#>   b = -1.9;  -0.37;  0.82;  1.68
#>   D = 1
#> 
#> --------------------------
plot(item6)

plot of chunk unnamed-chunk-11

D parameter can also be specified.

Generalized Partial Credit Model (GPCM)

A Generalized Partial Credit model item requires a and b parameters to be specified. b parameters is ascending vector of threshold parameters:

item7 <- item(a = 1.22, b = c(-1.9, -0.37, 0.82, 1.68), D = 1.7, model = "GPCM", 
              item_id = "itm-7")
item7
#> A 'GPCM' item.
#> Item ID:      itm-7
#> Model:   GPCM (Generalized Partial Credit Model)
#> Model Parameters:
#>   a = 1.22
#>   b = -1.9;  -0.37;  0.82;  1.68
#>   D = 1.7
#> 
#> --------------------------

Partial Credit Model (PCM)

A Partial Credit model item requires b parameters to be specified. b parameters is ascending vector of threshold parameters:

item8 <- item(b = c(-1.9, -0.37, 0.82, 1.68), model = "PCM")
item8
#> A 'PCM' item.
#> Model:   PCM (Partial Credit Model)
#> Model Parameters:
#>   b = -1.9;  -0.37;  0.82;  1.68
#> 
#> --------------------------

Generating Random Item Parameters

An item with random item parameters can be generated using generate_item function:

generate_item("3PL")
#> A '3PL' item.
#> Model:   3PL (Three-Parameter Logistic Model)
#> Model Parameters:
#>   a = 1.343
#>   b = -1.4301
#>   c = 0.023
#>   D = 1
#> 
#> Misc: 
#>   key: "D"
#>   possible_options: "A", "B", "C", "D"
#> --------------------------
generate_item("2PL")
#> A '2PL' item.
#> Model:   2PL (Two-Parameter Logistic Model)
#> Model Parameters:
#>   a = 0.9555
#>   b = 2.1742
#>   D = 1
#> 
#> Misc: 
#>   key: "A"
#>   possible_options: "A", "B", "C", "D"
#> --------------------------
generate_item("Rasch")
#> A 'Rasch' item.
#> Model:   Rasch (Rasch Model)
#> Model Parameters:
#>   b = -0.3221
#> 
#> Misc: 
#>   key: "B"
#>   possible_options: "A", "B", "C", "D"
#> --------------------------
generate_item("GRM")
#> A 'GRM' item.
#> Model:   GRM (Graded Response Model)
#> Model Parameters:
#>   a = 0.9448
#>   b = -0.9244;  0.0779;  0.8268
#>   D = 1
#> 
#> --------------------------
# The number of categories of polytomous items can be specified:
generate_item("GPCM", n_categories = 5)
#> A 'GPCM' item.
#> Model:   GPCM (Generalized Partial Credit Model)
#> Model Parameters:
#>   a = 0.6691
#>   b = -0.4325;  -0.0823;  0.6461;  1.4024
#>   D = 1
#> 
#> --------------------------

Testlet

A testlet is simply a collection of Item objects:

item1 <- item(a = 1.2, b = -.8, c = .33, D = 1.7, model = "3PL", 
              item_id = "ITM384", content = "Quadratic Equations")
item2 <- item(a = 0.75, b = 1.8, c = .21, D = 1.7, model = "3PL", 
              item_id = "ITM722", content = "Quadratic Equations")
item3 <- item(a = 1.06, b = 1.76, c = .13, d = .98, model = "4PL", 
              item_id = "itm-5")
t1 <- testlet(c(item1, item2, item3))
t1
#> An object of class 'Testlet'.
#> Model:   BTM
#> 
#> Item List:
#> 
#>   item_id model     a     b     c     d     D content            
#>   <chr>   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>              
#> 1 ITM384  3PL    1.2  -0.8   0.33 NA      1.7 Quadratic Equations
#> 2 ITM722  3PL    0.75  1.8   0.21 NA      1.7 Quadratic Equations
#> 3 itm-5   4PL    1.06  1.76  0.13  0.98   1   <NA>

An testlet_id field is required if testlet will be used in an item pool.

t1 <- testlet(item1, item2, item3, testlet_id = "T1")
t1
#> An object of class 'Testlet'.
#> Testlet ID:      T1
#> Model:   BTM
#> 
#> Item List:
#> 
#>   item_id model     a     b     c     d     D content            
#>   <chr>   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>              
#> 1 ITM384  3PL    1.2  -0.8   0.33 NA      1.7 Quadratic Equations
#> 2 ITM722  3PL    0.75  1.8   0.21 NA      1.7 Quadratic Equations
#> 3 itm-5   4PL    1.06  1.76  0.13  0.98   1   <NA>

Itempool

An Itempool object is the most frequently used object type in irt package. It is a collection of Item and Testlet objects.

item1 <- generate_item("3PL", item_id = "I1") 
item2 <- generate_item("3PL", item_id = "I2") 
item3 <- generate_item("3PL", item_id = "I3") 
ip1 <- itempool(item1, item2, item3)

Item pools can be composed of items from different psychometric models and testlets:

item4 <- generate_item("GRM", item_id = "I4") 
item5 <- generate_item("3PL", item_id = "T1-I1") 
item6 <- generate_item("3PL", item_id = "T1-I2") 
t1 <- testlet(item5, item6, item_id = "T1")
ip2 <- itempool(item1, item2, item3, item4, t1)

Most of the time item pools are generated using data frames:

n_item <- 6 # Number of items
ipdf <- data.frame(a = rlnorm(n_item), b = rnorm(n_item), 
                   c = runif(n_item, 0, .3))
ip3 <- itempool(ipdf)
ip3
#> An object of class 'Itempool'.
#> Model of items: 3PL
#> D = 1
#> 
#>   item_id     a       b      c
#>   <chr>   <dbl>   <dbl>  <dbl>
#> 1 Item_1  0.790  0.444  0.131 
#> 2 Item_2  1.37   0.333  0.159 
#> 3 Item_3  1.05  -0.0779 0.258 
#> 4 Item_4  0.577 -0.542  0.0744
#> 5 Item_5  0.410  0.441  0.0337
#> 6 Item_6  3.32   0.805  0.0630

# Scaling constant can be specified
ip4 <- itempool(ipdf, D = 1.7)
ip4
#> An object of class 'Itempool'.
#> Model of items: 3PL
#> D = 1.7
#> 
#>   item_id     a       b      c
#>   <chr>   <dbl>   <dbl>  <dbl>
#> 1 Item_1  0.790  0.444  0.131 
#> 2 Item_2  1.37   0.333  0.159 
#> 3 Item_3  1.05  -0.0779 0.258 
#> 4 Item_4  0.577 -0.542  0.0744
#> 5 Item_5  0.410  0.441  0.0337
#> 6 Item_6  3.32   0.805  0.0630
ipdf <- data.frame(
  item_id = c("Item_1", "Item_2", "Item_3", "Item_4", "Item_5", "Item_6"), 
  model = c("3PL", "3PL", "3PL", "GPCM", "GPCM", "GPCM"), 
  a = c(1.0253, 1.3609, 1.6617, 1.096, 0.9654, 1.3995), 
  b1 = c(NA, NA, NA, -1.112, -0.1709, -1.1324), 
  b2 = c(NA, NA, NA, -0.4972, 0.2778, -0.5242), 
  b3 = c(NA, NA, NA, -0.0077, 0.9684, NA), 
  D = c(1.7, 1.7, 1.7, 1.7, 1.7, 1.7), 
  b = c(0.7183, -0.4107, -1.5452, NA, NA, NA), 
  c = c(0.0871, 0.0751, 0.0589, NA, NA, NA), 
  content = c("Geometry", "Algebra", "Algebra", "Geometry", "Algebra", 
              "Algebra") 
)

ip5 <- itempool(ipdf)

Itempool objects can also be converted to a data frame:

as.data.frame(ip2)
#>   item_id testlet_id model      a       b      c      b1      b2     b3 D  key
#> 1      I1       <NA>   3PL 0.9469 -2.4883 0.0049      NA      NA     NA 1    A
#> 2      I2       <NA>   3PL 0.9293 -0.5001 0.2241      NA      NA     NA 1    A
#> 3      I3       <NA>   3PL 1.3965 -0.4750 0.0977      NA      NA     NA 1    A
#> 4      I4       <NA>   GRM 0.9274      NA     NA -0.9556 -0.1848 1.4966 1 <NA>
#> 5   T1-I1  Testlet_1   3PL 0.8522  0.4584 0.2960      NA      NA     NA 1    A
#> 6   T1-I2  Testlet_1   3PL 1.4833  1.9175 0.1574      NA      NA     NA 1    D
#>   possible_options
#> 1       A, B, C, D
#> 2       A, B, C, D
#> 3       A, B, C, D
#> 4               NA
#> 5       A, B, C, D
#> 6       A, B, C, D

Basic IRT Functions

Probability

Probability of correct response (for dichotomous items) and probability of each category (for polytomous items) can be calculated using prob function:

item1 <- generate_item("3PL")
theta <- 0.84
# The probability of correct and incorrect response for `item1` at theta = 0.84
prob(item1, theta)
#>              0         1
#> [1,] 0.1900671 0.8099329

# Multiple theta values
prob(item1, theta = c(-1, 1))
#>              0         1
#> [1,] 0.3927963 0.6072037
#> [2,] 0.1767949 0.8232051

# Polytomous items:
item2 <- generate_item(model = "GPCM")
prob(item2, theta = 1)
#>               0         1         2         3
#> [1,] 0.02920451 0.1466817 0.3918041 0.4323097
prob(item2, theta = c(-1, 0, 1))
#>               0         1         2          3
#> [1,] 0.46467898 0.3608595 0.1490357 0.02542584
#> [2,] 0.16808811 0.3319652 0.3486704 0.15127623
#> [3,] 0.02920451 0.1466817 0.3918041 0.43230971

Probability of correct response (or category) for each item in an item pool can be calculated as:

ip <- generate_ip(model = "3PL", n = 7)
ip
#> An object of class 'Itempool'.
#> Model of items: 3PL
#> D = 1
#> possible_options = c("A", "B", "C", "D")
#> 
#>   item_id     a      b      c key  
#>   <chr>   <dbl>  <dbl>  <dbl> <chr>
#> 1 Item_1  0.963  0.486 0.296  D    
#> 2 Item_2  0.713 -0.202 0.296  C    
#> 3 Item_3  1.09   1.09  0.024  D    
#> 4 Item_4  1.51  -1.69  0.256  A    
#> 5 Item_5  1.23   1.19  0.185  C    
#> 6 Item_6  0.633 -1.41  0.0205 B    
#> 7 Item_7  0.936 -0.104 0.0359 B
prob(ip, theta = 0)
#>                 0         1
#> Item_1 0.43269150 0.5673085
#> Item_2 0.32684516 0.6731548
#> Item_3 0.74808307 0.2519169
#> Item_4 0.05407779 0.9459222
#> Item_5 0.66249017 0.3375098
#> Item_6 0.28469776 0.7153022
#> Item_7 0.45870367 0.5412963
# When there are multiple theta values, a list where each element corresponds
# to a theta value returned. 
prob(ip, theta = c(-2, 0, 1))
#> [[1]]
#>                0          1
#> Item_1 0.6448742 0.35512580
#> Item_2 0.5514417 0.44855831
#> Item_3 0.9434049 0.05659513
#> Item_4 0.4578878 0.54211222
#> Item_5 0.7995129 0.20048707
#> Item_6 0.5801860 0.41981395
#> Item_7 0.8243167 0.17568329
#> 
#> [[2]]
#>                 0         1
#> Item_1 0.43269150 0.5673085
#> Item_2 0.32684516 0.6731548
#> Item_3 0.74808307 0.2519169
#> Item_4 0.05407779 0.9459222
#> Item_5 0.66249017 0.3375098
#> Item_6 0.28469776 0.7153022
#> Item_7 0.45870367 0.5412963
#> 
#> [[3]]
#>                 0         1
#> Item_1 0.26643234 0.7335677
#> Item_2 0.20980963 0.7901904
#> Item_3 0.51241201 0.4875880
#> Item_4 0.01268471 0.9873153
#> Item_5 0.45589477 0.5441052
#> Item_6 0.17504828 0.8249517
#> Item_7 0.25314653 0.7468535

Item characteristic curves (ICC) can be plotted:

# Plot ICC of each item in the item pool
plot(ip)

plot of chunk unnamed-chunk-24


# Plot test characteristic curve
plot(ip, type = "tcc")

plot of chunk unnamed-chunk-24

Information

Information value of an item at a given \(\theta\) value can also be calculated:

item1 <- generate_item("3PL")
info(item1, theta = -2)
#> [1] 0.01945498

# Multiple theta values
info(item1, theta = c(-1, 1))
#> [1] 0.1416700 0.2371639

# Polytomous items:
item2 <- generate_item(model = "GPCM")
info(item2, theta = 1)
#> [1] 0.77995
info(item2, theta = c(-1, 0, 1))
#> [1] 0.7425615 1.3028559 0.7799500

Information values for each item in an item pool can be calculated as:

ip <- generate_ip(model = "3PL", n = 7)
ip
#> An object of class 'Itempool'.
#> Model of items: 3PL
#> D = 1
#> possible_options = c("A", "B", "C", "D")
#> 
#>   item_id     a       b      c key  
#>   <chr>   <dbl>   <dbl>  <dbl> <chr>
#> 1 Item_1  0.921  1.21   0.210  B    
#> 2 Item_2  0.973 -0.582  0.243  D    
#> 3 Item_3  1.39  -0.607  0.0999 A    
#> 4 Item_4  1.66  -0.756  0.052  B    
#> 5 Item_5  1.48   0.749  0.112  D    
#> 6 Item_6  1.21  -0.0773 0.19   B    
#> 7 Item_7  0.827  0.625  0.119  A
info(ip, theta = 0)
#>           [,1]      [,2]      [,3]      [,4]      [,5]      [,6]      [,7]
#> [1,] 0.0760404 0.1456097 0.3495729 0.4459048 0.2721894 0.2535784 0.1175325
info(ip, theta = c(-2, 0, 1))
#>             [,1]       [,2]      [,3]      [,4]        [,5]       [,6]
#> [1,] 0.006254617 0.05862131 0.1132600 0.1848433 0.004203867 0.03249523
#> [2,] 0.076040404 0.14560965 0.3495729 0.4459048 0.272189350 0.25357842
#> [3,] 0.132319327 0.09912434 0.1503077 0.1269273 0.439045100 0.19009188
#>            [,7]
#> [1,] 0.02708505
#> [2,] 0.11753251
#> [3,] 0.13524622

Information functions can be plotted:

# Plot information function of each item
plot_info(ip)

plot of chunk unnamed-chunk-27

# Plot test information function
plot_info(ip, tif = TRUE)

plot of chunk unnamed-chunk-27

Ability Estimation

For a given set of item parameters and item responses, the ability (\(\theta\)) estimates can be calculated using est_ability function.

# Generate an item pool 
ip <- generate_ip(model = "2PL", n = 10)
true_theta <- rnorm(5)
resp <- sim_resp(ip = ip, theta = true_theta, output = "matrix")

# Calculate raw scores
est_ability(resp = resp, ip = ip, method = "sum_score")
#> $est
#> S1 S2 S3 S4 S5 
#>  5  1  3  3  5 
#> 
#> $se
#> [1] NA NA NA NA NA
# Estimate ability using maximum likelihood estimation:
est_ability(resp = resp, ip = ip, method = "ml")
#> $est
#>        S1        S2        S3        S4        S5 
#> -0.112270 -2.095595 -0.672741 -0.571194 -0.167281 
#> 
#> $se
#>       S1       S2       S3       S4       S5 
#> 0.628564 1.046504 0.657468 0.648198 0.629233
# Estimate ability using EAP estimation:
est_ability(resp = resp, ip = ip, method = "eap")
#> $est
#>        S1        S2        S3        S4        S5 
#> -0.084040 -1.267195 -0.495875 -0.423100 -0.125095 
#> 
#> $se
#>       S1       S2       S3       S4       S5 
#> 0.543051 0.613218 0.554039 0.551057 0.543524
# Estimate ability using EAP estimation with a different prior 
# (prior mean = 0, prior standard deviation = 2):
est_ability(resp = resp, ip = ip, method = "eap", prior_pars = c(0, 2))
#> $est
#>        S1        S2        S3        S4        S5 
#> -0.110500 -1.911991 -0.660615 -0.560790 -0.164206 
#> 
#> $se
#>       S1       S2       S3       S4       S5 
#> 0.620760 0.863989 0.651555 0.642898 0.622029