1.) Understand how to perform the constrained baseline MIMIC model to test for DIF.
2.) From here, know how to identify an anchor item and then conduct a series of sequential free baseline models.
3.) Understand how lavaan computes these models, as well as the alternative MIMIC models that lavaan/semTools have that mayyyyy or may not be more useful…and quicker…and well, you know…more efficient……maybe.
1.) Perform constrained baseline approach. Which items are flagged as DIF items and how did you decide the DIF items?
2.) Perform the sequential free baseline approach. Which item you choose for an anchor item? How did you decide the anchor item? Which items are flagged as DIF items and how did you decide the DIF items?
Step 1: Loading necessary packages
library(lavaan)
library(semTools)
library(psych)
Step 2: Import the tab-delimited text data file “MIMICrep1.dat”
data<- read.table("MIMICrep1.dat", quote="\"", comment.char="")
To make it easy, let’s change item ‘V16’ to ‘Gender’ so we know what our grouping variable is in the data,
colnames(data)[colnames(data)=="V16"] <- "Gender" #changing variable 'V16' name to 'Gender'
names(data) #check to see if this worked correctly
## [1] "V1" "V2" "V3" "V4" "V5" "V6" "V7"
## [8] "V8" "V9" "V10" "V11" "V12" "V13" "V14"
## [15] "V15" "Gender"
Step 3: Create a proxy for the latent by group interaction term:
Although Lavaan does not have a direct way to create an interaction term for a latent variable, they do provide a way to create this interaction indirectly.
new.data <- indProd(var1 = paste0("V", 1:15), var2 = "Gender", match = FALSE,
data = data[ , c("Gender", paste0("V", 1:15))] )
Now that we have our latent interaction variables ready, we can conduct our DIF detection procedures one of two ways. The first will be the two-step procedures of the constrained baseline approach followed by the sequential free baseline approach using lavaan/semTools. The second approach will be using semTools variation of MIMICfor identifying items that have both non-uniform and uniform DIF.
If we wanted to manually specify MIMIC models that are slightly tedious and require testing each individual item, we can do so if we are so inclined…
The first step is fitting a series of ‘constrained baseline models’ to detect DIF. Since these models are prone to type I errors, the idea is to test each item in this context in order to identify the best non-DIF item that will be used in the next step as an ‘anchor’ item.
Specifying the baseline model
base.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender
'
fit.base <- cfa(model=base.mod, data = new.data, meanstructure=TRUE,
std.lv=T, estimator="MLR")
summary(fit.base, stand = TRUE)
## lavaan 0.6-3 ended normally after 162 iterations
##
## Optimization method NLMINB
## Number of free parameters 110
##
## Number of observations 400
##
## Estimator ML Robust
## Model Fit Test Statistic 703.225 577.267
## Degrees of freedom 417 417
## P-value (Chi-square) 0.000 0.000
## Scaling correction factor 1.218
## for the Yuan-Bentler correction (Mplus variant)
##
## Parameter Estimates:
##
## Information Observed
## Observed information based on Hessian
## Standard Errors Robust.huber.white
##
## Latent Variables:
## Estimate Std.Err z-value P(>|z|) Std.lv
## f1 =~
## V1 0.373 0.014 26.228 0.000 0.373
## V2 0.167 0.026 6.366 0.000 0.167
## V3 0.322 0.018 18.161 0.000 0.322
## V4 0.285 0.020 14.149 0.000 0.285
## V5 0.315 0.018 17.299 0.000 0.315
## V6 0.200 0.026 7.711 0.000 0.200
## V7 0.211 0.022 9.458 0.000 0.211
## V8 0.332 0.017 19.806 0.000 0.332
## V9 0.332 0.017 19.298 0.000 0.332
## V10 0.250 0.022 11.430 0.000 0.250
## V11 0.318 0.019 16.828 0.000 0.318
## V12 0.339 0.018 19.387 0.000 0.339
## V13 0.333 0.017 20.075 0.000 0.333
## V14 0.380 0.014 27.037 0.000 0.380
## V15 0.287 0.020 14.211 0.000 0.287
## Group =~
## Gender 0.500 0.000 19103682.960 0.000 0.500
## Gender.by.theta =~
## V1.Gender 0.187 0.007 26.171 0.000 0.187
## V2.Gender 0.084 0.013 6.434 0.000 0.084
## V3.Gender 0.160 0.009 17.835 0.000 0.160
## V4.Gender 0.144 0.010 14.594 0.000 0.144
## V5.Gender 0.157 0.009 17.141 0.000 0.157
## V6.Gender 0.099 0.013 7.548 0.000 0.099
## V7.Gender 0.105 0.011 9.348 0.000 0.105
## V8.Gender 0.165 0.008 19.534 0.000 0.165
## V9.Gender 0.165 0.009 18.930 0.000 0.165
## V10.Gender 0.124 0.011 11.177 0.000 0.124
## V11.Gender 0.160 0.009 17.154 0.000 0.160
## V12.Gender 0.169 0.009 19.331 0.000 0.169
## V13.Gender 0.166 0.008 19.919 0.000 0.166
## V14.Gender 0.190 0.007 27.185 0.000 0.190
## V15.Gender 0.143 0.010 14.106 0.000 0.143
## Std.all
##
## 0.765
## 0.334
## 0.677
## 0.569
## 0.659
## 0.406
## 0.454
## 0.665
## 0.691
## 0.513
## 0.643
## 0.695
## 0.667
## 0.758
## 0.581
##
## 1.000
##
## 0.765
## 0.337
## 0.675
## 0.578
## 0.658
## 0.403
## 0.451
## 0.663
## 0.689
## 0.511
## 0.646
## 0.695
## 0.666
## 0.758
## 0.580
##
## Covariances:
## Estimate Std.Err z-value P(>|z|) Std.lv
## .V1 ~~
## .V1.Gender 0.003 0.005 0.575 0.565 0.003
## .V2 ~~
## .V2.Gender 0.014 0.007 1.917 0.055 0.014
## .V3 ~~
## .V3.Gender 0.008 0.006 1.363 0.173 0.008
## .V4 ~~
## .V4.Gender 0.002 0.007 0.260 0.795 0.002
## .V5 ~~
## .V5.Gender 0.003 0.006 0.443 0.658 0.003
## .V6 ~~
## .V6.Gender 0.031 0.008 3.948 0.000 0.031
## .V7 ~~
## .V7.Gender 0.006 0.007 0.857 0.391 0.006
## .V8 ~~
## .V8.Gender 0.006 0.006 0.927 0.354 0.006
## .V9 ~~
## .V9.Gender 0.005 0.006 0.910 0.363 0.005
## .V10 ~~
## .V10.Gender 0.026 0.007 3.807 0.000 0.026
## .V11 ~~
## .V11.Gender -0.010 0.006 -1.541 0.123 -0.010
## .V12 ~~
## .V12.Gender -0.003 0.006 -0.565 0.572 -0.003
## .V13 ~~
## .V13.Gender 0.002 0.006 0.382 0.702 0.002
## .V14 ~~
## .V14.Gender -0.011 0.006 -1.897 0.058 -0.011
## .V15 ~~
## .V15.Gender 0.016 0.007 2.344 0.019 0.016
## f1 ~~
## Group -0.060 0.053 -1.135 0.256 -0.060
## Gender.by.thet -0.053 0.071 -0.751 0.453 -0.053
## Group ~~
## Gender.by.thet 0.005 0.053 0.087 0.930 0.005
## Std.all
##
## 0.061
##
## 0.126
##
## 0.126
##
## 0.021
##
## 0.041
##
## 0.301
##
## 0.065
##
## 0.083
##
## 0.088
##
## 0.299
##
## -0.139
##
## -0.055
##
## 0.034
##
## -0.198
##
## 0.196
##
## -0.060
## -0.053
##
## 0.005
##
## Intercepts:
## Estimate Std.Err z-value P(>|z|) Std.lv
## .V1 0.608 0.024 24.882 0.000 0.608
## .V2 0.562 0.025 22.678 0.000 0.562
## .V3 0.655 0.024 27.558 0.000 0.655
## .V4 0.512 0.025 20.506 0.000 0.512
## .V5 0.645 0.024 26.958 0.000 0.645
## .V6 0.647 0.024 27.106 0.000 0.647
## .V7 0.682 0.023 29.323 0.000 0.682
## .V8 0.522 0.025 20.921 0.000 0.522
## .V9 0.635 0.024 26.380 0.000 0.635
## .V10 0.657 0.024 27.711 0.000 0.657
## .V11 0.593 0.025 24.116 0.000 0.593
## .V12 0.613 0.024 25.145 0.000 0.613
## .V13 0.492 0.025 19.702 0.000 0.492
## .V14 0.562 0.025 22.678 0.000 0.562
## .V15 0.600 0.024 24.495 0.000 0.600
## .Gender 1.500 0.025 60.000 0.000 1.500
## .V1.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V2.Gender 0.000 0.012 0.000 1.000 0.000
## .V3.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V4.Gender 0.000 0.012 0.000 1.000 0.000
## .V5.Gender 0.000 0.012 0.000 1.000 0.000
## .V6.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V7.Gender 0.000 0.012 0.000 1.000 0.000
## .V8.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V9.Gender 0.000 0.012 0.000 1.000 0.000
## .V10.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V11.Gender 0.000 0.012 0.000 1.000 0.000
## .V12.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V13.Gender 0.000 0.012 0.000 1.000 0.000
## .V14.Gender -0.000 0.012 -0.000 1.000 -0.000
## .V15.Gender 0.000 0.012 0.000 1.000 0.000
## f1 0.000 0.000
## Group 0.000 0.000
## Gender.by.thet 0.000 0.000
## Std.all
## 1.245
## 1.126
## 1.379
## 1.025
## 1.349
## 1.313
## 1.466
## 1.045
## 1.320
## 1.353
## 1.198
## 1.255
## 0.985
## 1.121
## 1.213
## 3.000
## -0.000
## 0.000
## -0.000
## 0.000
## 0.000
## -0.000
## 0.000
## -0.000
## 0.000
## -0.000
## 0.000
## -0.000
## 0.000
## -0.000
## 0.000
## 0.000
## 0.000
## 0.000
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv
## .V1 0.099 0.010 9.984 0.000 0.099
## .V2 0.222 0.008 27.387 0.000 0.222
## .V3 0.122 0.010 11.805 0.000 0.122
## .V4 0.169 0.011 14.698 0.000 0.169
## .V5 0.129 0.011 11.977 0.000 0.129
## .V6 0.203 0.010 21.292 0.000 0.203
## .V7 0.172 0.010 16.579 0.000 0.172
## .V8 0.139 0.011 12.280 0.000 0.139
## .V9 0.121 0.011 11.264 0.000 0.121
## .V10 0.174 0.011 16.392 0.000 0.174
## .V11 0.143 0.011 12.483 0.000 0.143
## .V12 0.123 0.011 11.125 0.000 0.123
## .V13 0.139 0.011 12.586 0.000 0.139
## .V14 0.107 0.010 10.572 0.000 0.107
## .V15 0.162 0.011 14.137 0.000 0.162
## .Gender 0.000 0.000
## .V1.Gender 0.025 0.002 9.978 0.000 0.025
## .V2.Gender 0.055 0.002 26.922 0.000 0.055
## .V3.Gender 0.030 0.003 11.910 0.000 0.030
## .V4.Gender 0.041 0.003 14.565 0.000 0.041
## .V5.Gender 0.032 0.003 11.990 0.000 0.032
## .V6.Gender 0.051 0.002 21.633 0.000 0.051
## .V7.Gender 0.043 0.003 16.575 0.000 0.043
## .V8.Gender 0.035 0.003 12.254 0.000 0.035
## .V9.Gender 0.030 0.003 11.256 0.000 0.030
## .V10.Gender 0.043 0.003 16.688 0.000 0.043
## .V11.Gender 0.036 0.003 12.537 0.000 0.036
## .V12.Gender 0.031 0.003 11.110 0.000 0.031
## .V13.Gender 0.035 0.003 12.595 0.000 0.035
## .V14.Gender 0.027 0.003 10.540 0.000 0.027
## .V15.Gender 0.041 0.003 14.110 0.000 0.041
## f1 1.000 1.000
## Group 1.000 1.000
## Gender.by.thet 1.000 1.000
## Std.all
## 0.414
## 0.888
## 0.542
## 0.676
## 0.565
## 0.835
## 0.794
## 0.558
## 0.523
## 0.737
## 0.586
## 0.517
## 0.555
## 0.426
## 0.663
## 0.000
## 0.414
## 0.887
## 0.544
## 0.666
## 0.567
## 0.837
## 0.796
## 0.560
## 0.525
## 0.739
## 0.583
## 0.517
## 0.557
## 0.425
## 0.663
## 1.000
## 1.000
## 1.000
Now, we can specify additional models to test for DIF on each item using the constrained baseline approach that involves regressing each individual item on the grouping variable and the interaction term.
For item ‘v1’ the code would look as so:
V1.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group=~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
#here we introduce V1 being regressed on Group(gender) and interaction of theta x gender
V1 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
V1 <- cfa(V1.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
We now can compare this model to our baseline model using a chi-square LR test. Because we specified our estimator to be MLR, R knows to default to the correct chi-square test (i.e., satorra.bentler 2001 method).
anova(V1, fit.base) #testing DIF for item 1
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V1 415 5146.8 5593.8 702.60
## fit.base 417 5143.4 5582.4 703.22 0.81202 2 0.6663
Results imply that item 1 should NOT be flagged as DIF
We repeat this process for items 2-15. Again, for item 2, we would use the same code as above, just simply change ‘V1’ to ‘V2’ in the appropriate places like so:
V2.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group=~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
#here we introduce V1 being regressed on Group(gender) and interaction of theta x gender
V2 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
V2 <- cfa(V2.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
Again, let’s compare model ‘V2’ to our baseline model and see if there is a significant difference.
anova(V2, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V2 415 5132.2 5579.2 688.01
## fit.base 417 5143.4 5582.4 703.22 14 2 0.0009117 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Results suggest that this item SHOULD be flagged as having DIF (p<.001)
In this framework, we continue repeating the same process for ALL items and then evaluate which item has the highest factor loading and is also non-DIF to use as an anchor item for the sequential free baseline MIMIC approach.
The model syntax for all remaining items (V2-V15) is pasted in the below chunk:
#item 3
V3.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
#here we introduce V1 being regressed on Group(gender) and interaction of theta x gender
V3 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 4
V4.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V4 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 5
V5.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V5 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 6
V6.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V6 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 7
V7.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V7 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 8
V8.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V8 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 9
V9.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V9 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 10
V10.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V10 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 11
V11.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V11 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 12
V12.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V12 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 13
V13.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V13 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 14
V14.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V14 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
#item 15
V15.mod<-'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
Group =~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V15 ~ Group + Gender.by.theta
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
Now all models (testing item V3-V15) are quickly ran (no output is displayed though to condense space).
V3 <- cfa(V3.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V4 <- cfa(V4.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V5 <- cfa(V5.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V6 <- cfa(V6.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V7 <- cfa(V7.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V8 <- cfa(V8.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V9 <- cfa(V9.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V10 <- cfa(V10.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V11 <- cfa(V11.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V12 <- cfa(V12.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V13 <- cfa(V13.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V14 <- cfa(V14.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
V15 <- cfa(V15.mod, data = new.data, meanstructure = TRUE, std.lv=T, estimator="MLR")
Now we can see which items are flagged as DIF again as well:
anova(V3, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V3 415 5145.8 5592.8 701.62
## fit.base 417 5143.4 5582.4 703.22 1.783 2 0.41
anova(V4, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V4 415 5139.9 5586.9 695.74
## fit.base 417 5143.4 5582.4 703.22 8.2258 2 0.01636 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(V5, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V5 415 5146.8 5593.8 702.61
## fit.base 417 5143.4 5582.4 703.22 0.69015 2 0.7082
anova(V6, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V6 415 5110.8 5557.8 666.65
## fit.base 417 5143.4 5582.4 703.22 39.101 2 3.231e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(V7, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V7 415 5146.8 5593.9 702.67
## fit.base 417 5143.4 5582.4 703.22 0.54183 2 0.7627
anova(V8, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V8 415 5145.7 5592.7 701.51
## fit.base 417 5143.4 5582.4 703.22 1.9719 2 0.3731
anova(V9, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V9 415 5145.6 5592.6 701.41
## fit.base 417 5143.4 5582.4 703.22 2.0538 2 0.3581
anova(V10, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V10 415 5131.3 5578.3 687.11
## fit.base 417 5143.4 5582.4 703.22 16.119 2 0.0003161 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(V11, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V11 415 5140.5 5587.5 696.34
## fit.base 417 5143.4 5582.4 703.22 8.0973 2 0.01745 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(V12, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V12 415 5144.9 5591.9 700.73
## fit.base 417 5143.4 5582.4 703.22 2.7491 2 0.2529
anova(V13, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V13 415 5147.2 5594.2 703.04
## fit.base 417 5143.4 5582.4 703.22 0.21887 2 0.8963
anova(V14, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V14 415 5138.8 5585.8 694.64
## fit.base 417 5143.4 5582.4 703.22 11.012 2 0.004063 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(V15, fit.base)
## Scaled Chi Square Difference Test (method = "satorra.bentler.2001")
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## V15 415 5142.1 5589.2 697.96
## fit.base 417 5143.4 5582.4 703.22 5.7506 2 0.0564 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Results suggest that items V2,V4,V6,V10,V11,V14 & V15 potentially have DIF.
As a reminder though, this method often has a higher type I error rate, so we now move to the sequential baseline model.
The sequential free baseline approach requires identifying at least one anchor item. Again, we want to find a non-DIF item that is discriminating (i.e., has a high factor loading).
lavInspect(fit.base, what="std")$lambda
## f1 Group Gndr..
## V1 0.765 0 0.000
## V2 0.334 0 0.000
## V3 0.677 0 0.000
## V4 0.569 0 0.000
## V5 0.659 0 0.000
## V6 0.406 0 0.000
## V7 0.454 0 0.000
## V8 0.665 0 0.000
## V9 0.691 0 0.000
## V10 0.513 0 0.000
## V11 0.643 0 0.000
## V12 0.695 0 0.000
## V13 0.667 0 0.000
## V14 0.758 0 0.000
## V15 0.581 0 0.000
## Gender 0.000 1 0.000
## V1.Gender 0.000 0 0.765
## V2.Gender 0.000 0 0.337
## V3.Gender 0.000 0 0.675
## V4.Gender 0.000 0 0.578
## V5.Gender 0.000 0 0.658
## V6.Gender 0.000 0 0.403
## V7.Gender 0.000 0 0.451
## V8.Gender 0.000 0 0.663
## V9.Gender 0.000 0 0.689
## V10.Gender 0.000 0 0.511
## V11.Gender 0.000 0 0.646
## V12.Gender 0.000 0 0.695
## V13.Gender 0.000 0 0.666
## V14.Gender 0.000 0 0.758
## V15.Gender 0.000 0 0.580
Now that we have identified our anchor item, we can use the semTools package to fairly efficiently perform a series of these models. We will be using the “permuteMeasEq” command in semTools, which allows us to flexibily specify our MIMIC model and when specified correctly, follows the same logic as the sequential free baseline approach.
Important arguements to specify when using permuteMeasEq:
con.mimic<- 'f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15
f1 ~ Gender
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender'
mim.con <- cfa(con.mimic, data = new.data, meanstructure = TRUE, std.lv=T)
param <- as.list(paste0("Group + Gender.by.theta =~ V", 1:15))
names(param) <- paste0("V", 1:15)
#can also specify like param <- as.list(paste0("V", 1:15, " ~ Gender + Gender.by.theta")) myAFIs <- c("chisq","cfi","rmsea","mfi","aic")
## function to recalculate interaction terms after permuting the covariate
datafun <- function(data) {
d <- data[, !names(data) %in% paste0("V", 1:15, ".Gender")]
indProd(var1 = paste0("V", 1:15), var2 = "Gender", match = FALSE, data = d)
}
Let’s now actually run the sequential free baseline models to test for DIF using ‘V1’ as an anchor item:
Sequential.mimic<- permuteMeasEq(nPermute=50, # num of iterations
modelType = "mimic",
con=mim.con, #previously defined model
param = param, #list of parameters we are testing for DIF
freeParam = "V1", #anchor item
covariates = "Gender", #grouping vbl in dataset
AFIs = myAFIs, #list of fit indices we want
datafun= datafun, #function that was defined above
showProgress = FALSE) #usually true - but didn't want it to show up in rmarkdown
Extract results:
summary(Sequential.mimic) #exract summary
## Omnibus p value based on parametric chi-squared difference test:
##
## Chisq diff Df diff Pr(>Chisq)
## 704.162 419.000 0.000
##
##
## Omnibus p values based on nonparametric permutation method:
##
## AFI.Difference p.value
## chisq 704.162 0
## cfi 0.934 0
## rmsea 0.041 0
## mfi 0.700 0
## aic 4555.680 1
##
##
## Modification indices for equality constrained parameter estimates,
## with unadjusted 'p.value' based on chi-squared distribution and
## adjusted 'tukey.p.value' based on permutation distribution of the
## maximum modification index per iteration:
##
## test X2 df p.value tukey.p.value flag
## V1 score 0.080 2 0.961 1.00
## V2 score 14.152 2 0.001 0.00 *
## V3 score 0.024 2 0.988 1.00
## V4 score 0.004 2 0.998 1.00
## V5 score 0.376 2 0.828 1.00
## V6 score 34.345 2 0.000 0.00 *
## V7 score 0.010 2 0.995 1.00
## V8 score 0.180 2 0.914 1.00
## V9 score 0.237 2 0.888 1.00
## V10 score 14.835 2 0.001 0.00 *
## V11 score 4.265 2 0.119 0.24
## V12 score 2.145 2 0.342 0.72
## V13 score 0.000 2 1.000 1.00
## V14 score 6.633 2 0.036 0.08
## V15 score 5.455 2 0.065 0.10
Results:
In semTools/Lavaan, the default for MIMIC is based on a ‘Restricted Factor Analysis (RFA)’ model, which is equivalent to MIMIC, but differs in that the factor co-varies with the covariate instead of being regressed on it. The covariate in this case defines a single-indicator construct, and the double-mean-centered products of the indicators define a latent interaction between the factor and the covariate.
To specify the RFA version of MIMIC, we simply define our latent variable as normal (f1), define the grouping variable (i.e., Gender) as an actual latent variable, and then also define a new latent variable called ‘Gender.by.theta’ by all the product terms that we saved in our new dataset called ‘new.data.’ Finally, we allow the residuals of each specific indicator to correlate with the residuals of the product interaction term using the ‘~~’
mod.mimic <- '
f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9 + V10 + V11 + V12 + V13 + V14 + V15
Group=~ Gender #define the grouping variable as an actual LV
#list all product indicators and specify them on their own factor
Gender.by.theta =~ V1.Gender + V2.Gender + V3.Gender + V4.Gender + V5.Gender
+ V6.Gender + V7.Gender + V8.Gender + V9.Gender + V10.Gender +
V11.Gender + V12.Gender + V13.Gender + V14.Gender + V15.Gender
#correlate residuals for original item and product interaction term
V1 ~~ V1.Gender
V2~~ V2.Gender
V3 ~~ V3.Gender
V4 ~~ V4.Gender
V5 ~~ V5.Gender
V6 ~~ V6.Gender
V7 ~~ V7.Gender
V8 ~~ V8.Gender
V9 ~~ V9.Gender
V10 ~~ V10.Gender
V11 ~~ V11.Gender
V12 ~~ V12.Gender
V13 ~~ V13.Gender
V14 ~~ V14.Gender
V15 ~~ V15.Gender
'
Now that our model is specified above, we can go ahead and fit our MIMIC model and extract the DIF information we are interested in.
Fiting the model:
fit.mimic <- cfa(mod.mimic, data = new.data, meanstructure = TRUE, std.lv=T)
summary(fit.mimic, stand = TRUE)
## lavaan 0.6-3 ended normally after 162 iterations
##
## Optimization method NLMINB
## Number of free parameters 110
##
## Number of observations 400
##
## Estimator ML
## Model Fit Test Statistic 703.225
## Degrees of freedom 417
## P-value (Chi-square) 0.000
##
## Parameter Estimates:
##
## Information Expected
## Information saturated (h1) model Structured
## Standard Errors Standard
##
## Latent Variables:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## f1 =~
## V1 0.373 0.021 17.620 0.000 0.373 0.765
## V2 0.167 0.025 6.637 0.000 0.167 0.334
## V3 0.322 0.022 14.950 0.000 0.322 0.677
## V4 0.285 0.024 11.964 0.000 0.285 0.569
## V5 0.315 0.022 14.372 0.000 0.315 0.659
## V6 0.200 0.024 8.481 0.000 0.200 0.406
## V7 0.211 0.023 9.207 0.000 0.211 0.454
## V8 0.332 0.023 14.554 0.000 0.332 0.665
## V9 0.332 0.022 15.310 0.000 0.332 0.691
## V10 0.250 0.023 10.995 0.000 0.250 0.513
## V11 0.318 0.023 14.011 0.000 0.318 0.643
## V12 0.339 0.022 15.422 0.000 0.339 0.695
## V13 0.333 0.023 14.583 0.000 0.333 0.667
## V14 0.380 0.022 17.529 0.000 0.380 0.758
## V15 0.287 0.023 12.434 0.000 0.287 0.581
## Group =~
## Gender 0.500 0.018 28.284 0.000 0.500 1.000
## Gender.by.theta =~
## V1.Gender 0.187 0.011 17.619 0.000 0.187 0.765
## V2.Gender 0.084 0.013 6.691 0.000 0.084 0.337
## V3.Gender 0.160 0.011 14.906 0.000 0.160 0.675
## V4.Gender 0.144 0.012 12.189 0.000 0.144 0.578
## V5.Gender 0.157 0.011 14.337 0.000 0.157 0.658
## V6.Gender 0.099 0.012 8.419 0.000 0.099 0.403
## V7.Gender 0.105 0.011 9.153 0.000 0.105 0.451
## V8.Gender 0.165 0.011 14.506 0.000 0.165 0.663
## V9.Gender 0.165 0.011 15.262 0.000 0.165 0.689
## V10.Gender 0.124 0.011 10.937 0.000 0.124 0.511
## V11.Gender 0.160 0.011 14.086 0.000 0.160 0.646
## V12.Gender 0.169 0.011 15.407 0.000 0.169 0.695
## V13.Gender 0.166 0.011 14.550 0.000 0.166 0.666
## V14.Gender 0.190 0.011 17.556 0.000 0.190 0.758
## V15.Gender 0.143 0.012 12.420 0.000 0.143 0.580
##
## Covariances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .V1 ~~
## .V1.Gender 0.003 0.003 1.069 0.285 0.003 0.061
## .V2 ~~
## .V2.Gender 0.014 0.006 2.469 0.014 0.014 0.126
## .V3 ~~
## .V3.Gender 0.008 0.003 2.326 0.020 0.008 0.126
## .V4 ~~
## .V4.Gender 0.002 0.004 0.395 0.693 0.002 0.021
## .V5 ~~
## .V5.Gender 0.003 0.003 0.765 0.444 0.003 0.041
## .V6 ~~
## .V6.Gender 0.031 0.005 5.679 0.000 0.031 0.301
## .V7 ~~
## .V7.Gender 0.006 0.004 1.272 0.204 0.006 0.065
## .V8 ~~
## .V8.Gender 0.006 0.004 1.551 0.121 0.006 0.083
## .V9 ~~
## .V9.Gender 0.005 0.003 1.617 0.106 0.005 0.088
## .V10 ~~
## .V10.Gender 0.026 0.005 5.571 0.000 0.026 0.299
## .V11 ~~
## .V11.Gender -0.010 0.004 -2.587 0.010 -0.010 -0.139
## .V12 ~~
## .V12.Gender -0.003 0.003 -1.013 0.311 -0.003 -0.055
## .V13 ~~
## .V13.Gender 0.002 0.004 0.629 0.529 0.002 0.034
## .V14 ~~
## .V14.Gender -0.011 0.003 -3.441 0.001 -0.011 -0.198
## .V15 ~~
## .V15.Gender 0.016 0.004 3.687 0.000 0.016 0.196
## f1 ~~
## Group -0.060 0.052 -1.146 0.252 -0.060 -0.060
## Gender.by.thet -0.053 0.055 -0.971 0.332 -0.053 -0.053
## Group ~~
## Gender.by.thet 0.005 0.052 0.088 0.930 0.005 0.005
##
## Intercepts:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .V1 0.608 0.024 24.895 0.000 0.608 1.245
## .V2 0.562 0.025 22.518 0.000 0.562 1.126
## .V3 0.655 0.024 27.576 0.000 0.655 1.379
## .V4 0.512 0.025 20.506 0.000 0.512 1.025
## .V5 0.645 0.024 26.976 0.000 0.645 1.349
## .V6 0.647 0.025 26.260 0.000 0.647 1.313
## .V7 0.682 0.023 29.328 0.000 0.682 1.466
## .V8 0.522 0.025 20.908 0.000 0.522 1.045
## .V9 0.635 0.024 26.410 0.000 0.635 1.320
## .V10 0.657 0.024 27.052 0.000 0.657 1.353
## .V11 0.593 0.025 23.952 0.000 0.593 1.198
## .V12 0.613 0.024 25.096 0.000 0.613 1.255
## .V13 0.492 0.025 19.703 0.000 0.492 0.985
## .V14 0.562 0.025 22.411 0.000 0.562 1.121
## .V15 0.600 0.025 24.253 0.000 0.600 1.213
## .Gender 1.500 0.025 60.000 0.000 1.500 3.000
## .V1.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V2.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V3.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V4.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V5.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V6.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V7.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V8.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V9.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V10.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V11.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V12.Gender -0.000 0.012 -0.000 1.000 -0.000 -0.000
## .V13.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## .V14.Gender -0.000 0.013 -0.000 1.000 -0.000 -0.000
## .V15.Gender 0.000 0.012 0.000 1.000 0.000 0.000
## f1 0.000 0.000 0.000
## Group 0.000 0.000 0.000
## Gender.by.thet 0.000 0.000 0.000
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .V1 0.099 0.008 12.252 0.000 0.099 0.414
## .V2 0.222 0.016 13.975 0.000 0.222 0.888
## .V3 0.122 0.009 13.013 0.000 0.122 0.542
## .V4 0.169 0.013 13.503 0.000 0.169 0.676
## .V5 0.129 0.010 13.116 0.000 0.129 0.565
## .V6 0.203 0.015 13.881 0.000 0.203 0.835
## .V7 0.172 0.012 13.797 0.000 0.172 0.794
## .V8 0.139 0.011 13.086 0.000 0.139 0.558
## .V9 0.121 0.009 12.924 0.000 0.121 0.523
## .V10 0.174 0.013 13.668 0.000 0.174 0.737
## .V11 0.143 0.011 13.200 0.000 0.143 0.586
## .V12 0.123 0.010 12.892 0.000 0.123 0.517
## .V13 0.139 0.011 13.074 0.000 0.139 0.555
## .V14 0.107 0.009 12.331 0.000 0.107 0.426
## .V15 0.162 0.012 13.465 0.000 0.162 0.663
## .Gender 0.000 0.000 0.000
## .V1.Gender 0.025 0.002 12.250 0.000 0.025 0.414
## .V2.Gender 0.055 0.004 13.972 0.000 0.055 0.887
## .V3.Gender 0.030 0.002 13.021 0.000 0.030 0.544
## .V4.Gender 0.041 0.003 13.472 0.000 0.041 0.666
## .V5.Gender 0.032 0.002 13.122 0.000 0.032 0.567
## .V6.Gender 0.051 0.004 13.885 0.000 0.051 0.837
## .V7.Gender 0.043 0.003 13.801 0.000 0.043 0.796
## .V8.Gender 0.035 0.003 13.094 0.000 0.035 0.560
## .V9.Gender 0.030 0.002 12.934 0.000 0.030 0.525
## .V10.Gender 0.043 0.003 13.673 0.000 0.043 0.739
## .V11.Gender 0.036 0.003 13.185 0.000 0.036 0.583
## .V12.Gender 0.031 0.002 12.894 0.000 0.031 0.517
## .V13.Gender 0.035 0.003 13.080 0.000 0.035 0.557
## .V14.Gender 0.027 0.002 12.320 0.000 0.027 0.425
## .V15.Gender 0.041 0.003 13.466 0.000 0.041 0.663
## f1 1.000 1.000 1.000
## Group 1.000 1.000 1.000
## Gender.by.thet 1.000 1.000 1.000
To test for both uniform/non-uniform DIF simultaneously, we can use the code below:
do.call(rbind, lapply(param, function(x) lavTestScore(fit.mimic, add = x)$test))
##
## total score test:
##
## test X2 df p.value
## V1 score 0.620 2 0.733
## V2 score 14.909 2 0.001
## V3 score 1.604 2 0.449
## V4 score 7.408 2 0.025
## V5 score 0.613 2 0.736
## V6 score 34.865 2 0.000
## V7 score 0.550 2 0.760
## V8 score 1.709 2 0.425
## V9 score 1.805 2 0.406
## V10 score 15.769 2 0.000
## V11 score 6.759 2 0.034
## V12 score 2.472 2 0.291
## V13 score 0.183 2 0.912
## V14 score 8.336 2 0.015
## V15 score 5.203 2 0.074
If we want to test each parameter individually for both kinds of DIF, we can also use this command below:
lavTestScore(fit.mimic, add = as.character(param))
## $test
##
## total score test:
##
## test X2 df p.value
## 1 score 98.571 30 0
##
## $uni
##
## univariate score tests:
##
## lhs op rhs X2 df p.value
## 1 Group=~V1 == 0 0.463 1 0.496
## 2 Gender.by.theta=~V1 == 0 0.158 1 0.691
## 3 Group=~V2 == 0 0.949 1 0.330
## 4 Gender.by.theta=~V2 == 0 13.952 1 0.000
## 5 Group=~V3 == 0 1.544 1 0.214
## 6 Gender.by.theta=~V3 == 0 0.059 1 0.808
## 7 Group=~V4 == 0 7.408 1 0.006
## 8 Gender.by.theta=~V4 == 0 0.000 1 0.999
## 9 Group=~V5 == 0 0.125 1 0.723
## 10 Gender.by.theta=~V5 == 0 0.487 1 0.485
## 11 Group=~V6 == 0 0.913 1 0.339
## 12 Gender.by.theta=~V6 == 0 33.964 1 0.000
## 13 Group=~V7 == 0 0.528 1 0.467
## 14 Gender.by.theta=~V7 == 0 0.021 1 0.885
## 15 Group=~V8 == 0 1.592 1 0.207
## 16 Gender.by.theta=~V8 == 0 0.118 1 0.732
## 17 Group=~V9 == 0 1.471 1 0.225
## 18 Gender.by.theta=~V9 == 0 0.333 1 0.564
## 19 Group=~V10 == 0 1.308 1 0.253
## 20 Gender.by.theta=~V10 == 0 14.471 1 0.000
## 21 Group=~V11 == 0 2.122 1 0.145
## 22 Gender.by.theta=~V11 == 0 4.643 1 0.031
## 23 Group=~V12 == 0 0.026 1 0.872
## 24 Gender.by.theta=~V12 == 0 2.447 1 0.118
## 25 Group=~V13 == 0 0.178 1 0.673
## 26 Gender.by.theta=~V13 == 0 0.005 1 0.943
## 27 Group=~V14 == 0 0.999 1 0.318
## 28 Gender.by.theta=~V14 == 0 7.341 1 0.007
## 29 Group=~V15 == 0 0.016 1 0.900
## 30 Gender.by.theta=~V15 == 0 5.186 1 0.023
Recall back to previous homeworks of how to test different levels of measurement invariance using Lavaan and semTools.
To review: we can specify a configural, metric, and scalar model that will test for measurement invariance at the various levels through chi-square LR tests as well as change in other fit indices.
First, we specify the configural model that simply consists of our one factor defined by its 15 indicators:
mod.config <- '
f1 =~ V1 + V2 + V3 + V4 + V5 + V6 + V7 + V8 + V9
+ V10 + V11 + V12 + V13 + V14 + V15'
Next, we physically run this model not using the ‘cfa’ function, but instead use ‘measurementInvariance’ and specify the grouping variable with the ‘group’ arguement.
miout <- measurementInvariance(model = mod.config, data = data, std.lv = TRUE,
group = "Gender")
From here, we save specific information from each of the levels of invariance from the ‘measurementInvariance’ function above to use later on.
fit.config <- miout[["fit.configural"]]
fit.metric <- miout[["fit.loadings"]]
fit.scalar <- miout[["fit.intercepts"]]
We will now use the above measurement invariance model that was fit to examine what items specifically show item bias (either non-uniform or uniform DIF).
##for this purpose only using 50 permutations but would want 500-1000+ in practice.
out.config <- permuteMeasEq(nPermute = 50, con = fit.config, showProgress = FALSE)
## No AFIs were selected, so only chi-squared will be permuted.
Print results:
out.config
## Omnibus p value based on parametric chi-squared difference test:
##
## Chisq diff Df diff Pr(>Chisq)
## 258.061 180.000 0.000
##
##
## Omnibus p values based on nonparametric permutation method:
##
## AFI.Difference p.value
## chisq 258.061 0.46
out.metric <- permuteMeasEq(nPermute = 50, uncon = fit.config, con = fit.metric,
param = "loadings",
AFIs = myAFIs #these were defined earlier
, showProgress = FALSE)
Print results:
summary(out.metric, nd = 10)
## Omnibus p value based on parametric chi-squared difference test:
##
## Chisq diff Df diff Pr(>Chisq)
## 84.22693 14.00000 0.00000
##
##
## Omnibus p values based on nonparametric permutation method:
##
## AFI.Difference p.value
## chisq 84.22692820 0
## cfi -0.03232223 0
## rmsea 0.01525547 0
## mfi -0.07622796 0
## aic 56.22692820 0
##
##
## Modification indices for equality constrained parameter estimates,
## with unadjusted 'p.value' based on chi-squared distribution and
## adjusted 'tukey.p.value' based on permutation distribution of the
## maximum modification index per iteration:
##
## lhs op rhs X2 df p.value tukey.p.value flag
## 1 .p1. == .p48. 1.551049e-01 1 0.6937039467 1.00
## 2 .p2. == .p49. 1.413267e+01 1 0.0001703588 0.00 * -->
## 3 .p3. == .p50. 5.888629e-02 1 0.8082649461 1.00
## 4 .p4. == .p51. 3.617540e-05 1 0.9952010724 1.00
## 5 .p5. == .p52. 4.856101e-01 1 0.4858921205 1.00
## 6 .p6. == .p53. 3.471350e+01 1 0.0000000038 0.00 * -->
## 7 .p7. == .p54. 2.072454e-02 1 0.8855318953 1.00
## 8 .p8. == .p55. 1.208272e-01 1 0.7281390089 1.00
## 9 .p9. == .p56. 3.352515e-01 1 0.5625830773 1.00
## 10 .p10. == .p57. 1.482390e+01 1 0.0001180302 0.00 * -->
## 11 .p11. == .p58. 4.659465e+00 1 0.0308828042 0.32
## 12 .p12. == .p59. 2.447067e+00 1 0.1177447054 0.76
## 13 .p13. == .p60. 4.717869e-03 1 0.9452389704 1.00
## 14 .p14. == .p61. 7.373047e+00 1 0.0066208671 0.00 * -->
## 15 .p15. == .p62. 5.283781e+00 1 0.0215249511 0.10
## parameter group.lhs group.rhs
## 1 f1=~V1 1 2
## 2 f1=~V2 1 2
## 3 f1=~V3 1 2
## 4 f1=~V4 1 2
## 5 f1=~V5 1 2
## 6 f1=~V6 1 2
## 7 f1=~V7 1 2
## 8 f1=~V8 1 2
## 9 f1=~V9 1 2
## 10 f1=~V10 1 2
## 11 f1=~V11 1 2
## 12 f1=~V12 1 2
## 13 f1=~V13 1 2
## 14 f1=~V14 1 2
## 15 f1=~V15 1 2
##
##
## The following equality constraints were flagged as significant:
##
## Parameter 'f1=~V2' may differ between Groups '1' and '2'.
## Parameter 'f1=~V6' may differ between Groups '1' and '2'.
## Parameter 'f1=~V10' may differ between Groups '1' and '2'.
## Parameter 'f1=~V14' may differ between Groups '1' and '2'.
##
## Use lavTestScore(..., epc = TRUE) on your constrained model to display expected parameter changes for these equality constraints
out.scalar <- permuteMeasEq(nPermute = 50, uncon = fit.config, con = fit.metric,
param = c("intercepts","loadings"),
AFIs = myAFIs,#these were defined earlier
showProgress = FALSE)
Print results:
summary(out.scalar, nd = 10)
## Omnibus p value based on parametric chi-squared difference test:
##
## Chisq diff Df diff Pr(>Chisq)
## 84.22693 14.00000 0.00000
##
##
## Omnibus p values based on nonparametric permutation method:
##
## AFI.Difference p.value
## chisq 84.22692820 0
## cfi -0.03232223 0
## rmsea 0.01525547 0
## mfi -0.07622796 0
## aic 56.22692820 0
##
##
## Modification indices for equality constrained parameter estimates,
## with unadjusted 'p.value' based on chi-squared distribution and
## adjusted 'tukey.p.value' based on permutation distribution of the
## maximum modification index per iteration:
##
## lhs op rhs X2 df p.value tukey.p.value flag
## 1 .p1. == .p48. 1.551049e-01 1 0.6937039467 1.00
## 2 .p2. == .p49. 1.413267e+01 1 0.0001703588 0.00 * -->
## 3 .p3. == .p50. 5.888629e-02 1 0.8082649461 1.00
## 4 .p4. == .p51. 3.617540e-05 1 0.9952010724 1.00
## 5 .p5. == .p52. 4.856101e-01 1 0.4858921205 0.98
## 6 .p6. == .p53. 3.471350e+01 1 0.0000000038 0.00 * -->
## 7 .p7. == .p54. 2.072454e-02 1 0.8855318953 1.00
## 8 .p8. == .p55. 1.208272e-01 1 0.7281390089 1.00
## 9 .p9. == .p56. 3.352515e-01 1 0.5625830773 1.00
## 10 .p10. == .p57. 1.482390e+01 1 0.0001180302 0.00 * -->
## 11 .p11. == .p58. 4.659465e+00 1 0.0308828042 0.18
## 12 .p12. == .p59. 2.447067e+00 1 0.1177447054 0.54
## 13 .p13. == .p60. 4.717869e-03 1 0.9452389704 1.00
## 14 .p14. == .p61. 7.373047e+00 1 0.0066208671 0.02 * -->
## 15 .p15. == .p62. 5.283781e+00 1 0.0215249511 0.08
## parameter group.lhs group.rhs
## 1 f1=~V1 1 2
## 2 f1=~V2 1 2
## 3 f1=~V3 1 2
## 4 f1=~V4 1 2
## 5 f1=~V5 1 2
## 6 f1=~V6 1 2
## 7 f1=~V7 1 2
## 8 f1=~V8 1 2
## 9 f1=~V9 1 2
## 10 f1=~V10 1 2
## 11 f1=~V11 1 2
## 12 f1=~V12 1 2
## 13 f1=~V13 1 2
## 14 f1=~V14 1 2
## 15 f1=~V15 1 2
##
##
## The following equality constraints were flagged as significant:
##
## Parameter 'f1=~V2' may differ between Groups '1' and '2'.
## Parameter 'f1=~V6' may differ between Groups '1' and '2'.
## Parameter 'f1=~V10' may differ between Groups '1' and '2'.
## Parameter 'f1=~V14' may differ between Groups '1' and '2'.
##
## Use lavTestScore(..., epc = TRUE) on your constrained model to display expected parameter changes for these equality constraints
Testing for BOTH uniform and non-uniform DIF, our results suggest that item 2, 6, 10, & 14 should be flagged for DIF.