Lagrange (i.e., score) test to test whether parameters should be freed from a more constrained baseline model.
Arguments
- mod
an estimated model
- parnum
a vector, or list of vectors, containing one or more parameter locations/sets of locations to be tested. See objects returned from
mod2valuesfor the locations- SE.type
type of information matrix estimator to use. See
mirtfor further details- type
type of numerical algorithm passed to
numerical_derivto obtain the gradient terms- ...
additional arguments to pass to
mirt
References
Chalmers, R. P. (2012). mirt: A Multidimensional Item Response Theory Package for the R Environment. Journal of Statistical Software, 48(6), 1-29. doi:10.18637/jss.v048.i06
Author
Phil Chalmers rphilip.chalmers@gmail.com
Examples
# \donttest{
dat <- expand.table(LSAT7)
mod <- mirt(dat, 1, 'Rasch')
(values <- mod2values(mod))
#> group item class name parnum value lbound ubound est const nconst
#> 1 all Item.1 dich a1 1 1.000 -Inf Inf FALSE none none
#> 2 all Item.1 dich d 2 1.868 -Inf Inf TRUE none none
#> 3 all Item.1 dich g 3 0.000 0 1 FALSE none none
#> 4 all Item.1 dich u 4 1.000 0 1 FALSE none none
#> 5 all Item.2 dich a1 5 1.000 -Inf Inf FALSE none none
#> 6 all Item.2 dich d 6 0.791 -Inf Inf TRUE none none
#> 7 all Item.2 dich g 7 0.000 0 1 FALSE none none
#> 8 all Item.2 dich u 8 1.000 0 1 FALSE none none
#> 9 all Item.3 dich a1 9 1.000 -Inf Inf FALSE none none
#> 10 all Item.3 dich d 10 1.461 -Inf Inf TRUE none none
#> 11 all Item.3 dich g 11 0.000 0 1 FALSE none none
#> 12 all Item.3 dich u 12 1.000 0 1 FALSE none none
#> 13 all Item.4 dich a1 13 1.000 -Inf Inf FALSE none none
#> 14 all Item.4 dich d 14 0.521 -Inf Inf TRUE none none
#> 15 all Item.4 dich g 15 0.000 0 1 FALSE none none
#> 16 all Item.4 dich u 16 1.000 0 1 FALSE none none
#> 17 all Item.5 dich a1 17 1.000 -Inf Inf FALSE none none
#> 18 all Item.5 dich d 18 1.993 -Inf Inf TRUE none none
#> 19 all Item.5 dich g 19 0.000 0 1 FALSE none none
#> 20 all Item.5 dich u 20 1.000 0 1 FALSE none none
#> 21 all GROUP GroupPars MEAN_1 21 0.000 -Inf Inf FALSE none none
#> 22 all GROUP GroupPars COV_11 22 1.022 0 Inf TRUE none none
#> prior.type prior_1 prior_2
#> 1 none NaN NaN
#> 2 none NaN NaN
#> 3 none NaN NaN
#> 4 none NaN NaN
#> 5 none NaN NaN
#> 6 none NaN NaN
#> 7 none NaN NaN
#> 8 none NaN NaN
#> 9 none NaN NaN
#> 10 none NaN NaN
#> 11 none NaN NaN
#> 12 none NaN NaN
#> 13 none NaN NaN
#> 14 none NaN NaN
#> 15 none NaN NaN
#> 16 none NaN NaN
#> 17 none NaN NaN
#> 18 none NaN NaN
#> 19 none NaN NaN
#> 20 none NaN NaN
#> 21 none NaN NaN
#> 22 none NaN NaN
# test all fixed slopes individually
parnum <- values$parnum[values$name == 'a1']
lagrange(mod, parnum)
#> X2 df p
#> 1 0.367 1 0.545
#> 5 0.048 1 0.827
#> 9 4.697 1 0.03
#> 13 1.45 1 0.229
#> 17 2.554 1 0.11
# compare to LR test for first two slopes
mod2 <- mirt(dat, 'F = 1-5
FREE = (1, a1)', 'Rasch')
coef(mod2, simplify=TRUE)$items
#> a1 d g u
#> Item.1 1.157956 1.9393358 0 1
#> Item.2 1.000000 0.7850452 0 1
#> Item.3 1.000000 1.4501952 0 1
#> Item.4 1.000000 0.5175858 0 1
#> Item.5 1.000000 1.9787464 0 1
anova(mod, mod2)
#> AIC SABIC HQ BIC logLik X2 df p
#> mod 5341.802 5352.192 5352.994 5371.248 -2664.901
#> mod2 5343.264 5355.386 5356.321 5377.618 -2664.632 0.538 1 0.463
mod2 <- mirt(dat, 'F = 1-5
FREE = (2, a1)', 'Rasch')
coef(mod2, simplify=TRUE)$items
#> a1 d g u
#> Item.1 1.0000000 1.8746626 0 1
#> Item.2 0.9464702 0.7810629 0 1
#> Item.3 1.0000000 1.4661198 0 1
#> Item.4 1.0000000 0.5234068 0 1
#> Item.5 1.0000000 1.9997347 0 1
anova(mod, mod2)
#> AIC SABIC HQ BIC logLik X2 df p
#> mod 5341.802 5352.192 5352.994 5371.248 -2664.901
#> mod2 5343.720 5355.842 5356.777 5378.075 -2664.860 0.081 1 0.775
mod2 <- mirt(dat, 'F = 1-5
FREE = (3, a1)', 'Rasch')
coef(mod2, simplify=TRUE)$items
#> a1 d g u
#> Item.1 1.000000 1.8101732 0 1
#> Item.2 1.000000 0.7649559 0 1
#> Item.3 1.848252 1.7774916 0 1
#> Item.4 1.000000 0.5042538 0 1
#> Item.5 1.000000 1.9316244 0 1
anova(mod, mod2)
#> AIC SABIC HQ BIC logLik X2 df p
#> mod 5341.802 5352.192 5352.994 5371.248 -2664.901
#> mod2 5335.130 5347.252 5348.187 5369.485 -2660.565 8.671 1 0.003
# test slopes first two slopes and last three slopes jointly
lagrange(mod, list(parnum[1:2], parnum[3:5]))
#> X2 df p
#> 1.5 0.459 2 0.795
#> 9.13.17 9.153 3 0.027
# test all 5 slopes and first + last jointly
lagrange(mod, list(parnum[1:5], parnum[c(1, 5)]))
#> X2 df p
#> 1.5.9.13.17 9.862 5 0.079
#> 1.17 2.898 2 0.235
######
## DIF using score (Lagrange) test example
n <- 10
N <- 500
# generate 2PL data with DIF on Item_1's intercept in the focal group
a <- matrix(rep(1, n), ncol = 1)
d1 <- matrix(rnorm(n), ncol = 1)
d2 <- d1
d2[1, 1] <- d1[1, 1] + 1 # shift Item_1's difficulty for the focal group
dat1 <- simdata(a, d1, N, itemtype = '2PL')
dat2 <- simdata(a, d2, N, itemtype = '2PL')
dat <- rbind(dat1, dat2)
colnames(dat) <- paste0('Item_', 1:n)
group <- c(rep('Reference', N), rep('Focal', N))
# fully equality-constrained (no-DIF) baseline: all a1/d equated across
# groups, with the latent mean/variance freed in the focal group for
# identification (standard mirt DIF baseline)
mod_baseline <- multipleGroup(dat, model = 1, itemtype = '2PL', group = group,
invariance = c(colnames(dat), 'free_means', 'free_var'),
SE = TRUE)
values <- mod2values(mod_baseline)
(i1 <- values[values$item == 'Item_1', ]) # inspect parnum for this item
#> group item class name parnum value lbound ubound est const nconst
#> 1 Focal Item_1 dich a1 1 0.845 -Inf Inf TRUE 1 none
#> 2 Focal Item_1 dich d 2 0.920 -Inf Inf TRUE 2 none
#> 3 Focal Item_1 dich g 3 0.000 0 1 FALSE none none
#> 4 Focal Item_1 dich u 4 1.000 0 1 FALSE none none
#> 43 Reference Item_1 dich a1 43 0.845 -Inf Inf TRUE 1 none
#> 44 Reference Item_1 dich d 44 0.920 -Inf Inf TRUE 2 none
#> 45 Reference Item_1 dich g 45 0.000 0 1 FALSE none none
#> 46 Reference Item_1 dich u 46 1.000 0 1 FALSE none none
#> prior.type prior_1 prior_2
#> 1 none NaN NaN
#> 2 none NaN NaN
#> 3 none NaN NaN
#> 4 none NaN NaN
#> 43 none NaN NaN
#> 44 none NaN NaN
#> 45 none NaN NaN
#> 46 none NaN NaN
parnum_a1 <- i1$parnum[i1$name == 'a1' & i1$group == 'Focal']
parnum_d <- i1$parnum[i1$name == 'd'& i1$group == 'Focal']
# joint 2-df score test: does freeing both a1 and d for Item_1 in the
# focal group improve fit relative to the constrained baseline?
lagrange(mod_baseline, list(c(parnum_a1, parnum_d)))
#> X2 df p
#> 1.2 23.161 2 0
# compare to LR and Wald tests
mod_nest <- multipleGroup(dat, model = 1, itemtype = '2PL', group = group,
invariance = c(colnames(dat)[-1], 'free_means', 'free_var'),
SE = TRUE)
anova(mod_baseline, mod_nest)
#> AIC SABIC HQ BIC logLik X2 df p
#> mod_baseline 12017.54 12055.64 12058.58 12125.51 -5986.771
#> mod_nest 11978.24 12019.80 12023.01 12096.03 -5965.120 43.302 2 0
wald(mod_nest)
#> a1.1 d.2 a1.5.47 d.6.48 a1.9.51 d.10.52 a1.13.55 d.14.56
#> 0.914 1.446 1.007 0.430 1.176 0.716 1.208 1.811
#> a1.17.59 d.18.60 a1.21.63 d.22.64 a1.25.67 d.26.68 a1.29.71 d.30.72
#> 1.238 -0.756 1.147 -0.313 0.850 -0.301 1.033 -1.009
#> a1.33.75 d.34.76 a1.37.79 d.38.80 a1.43 d.44 MEAN_1.83 COV_11.84
#> 1.062 0.400 1.126 1.163 1.003 0.431 0.103 0.782
wald(mod_nest, c('a1.1 = a1.43', 'd.2 = d.44'))
#> W df p
#> 1 39.398 2 0
# separate 1-df tests: slope-only DIF vs. intercept-only DIF
lagrange(mod_baseline, list(parnum_a1, parnum_d))
#> X2 df p
#> 1 2.606 1 0.106
#> 2 23.144 1 0
# }