library(wordbankr) # WB data
library(tidyverse) # tidy
library(mirt) # IRT models
library(ltm) # more IRT functions
library(psych) # some psychometric stuff (tests of dimensionality)
library(Gifi)# some more psychometric stuff (tests of dimensionality)
library(knitr) # some formatting, tables, etc
library(patchwork) # combining plots.
library(GGally) # More plottinng options.
library(lordif) # differential item functioning.
In our meeting on 28/June/2021, we noted that by adding Word Forms, we might be able to model some of the intermediate levels of syntactic proficiency between the 0 and 1 items on the complexity scale. I’m going to start with adding word endings and combines as those seem like the most straight forward. The problem with the word forms and word endings is that we expect them to by non-monotonically related to language proficiency, so I’m no sure how well they’ll fit this model. I’ve also made a new branch on github because I realized I could maintain the ordinal scale of the ordinal items, and use an ordinal IRT model – since binary items are just ordinal items with 2 categories. This appears to be working alright, but I want to keep it in a second branch until I verify this is appropriate (I’m 95% sure it’s right).
Download data from Word Bank:
Inst <- get_instrument_data(language="English (American)", form="WS")
Admin <- get_administration_data(language="English (American)", form="WS")
Item <- get_item_data(language="English (American)", form = "WS")
The American English CDI data has 7 types of items other than words, which are also listed in the code above: combine, complexity, word_endings, word_forms_nouns, word_forms_verbs, word_endings_nouns, and word_endings_verbs. The word_endings type seems to be questions about how often a child applies a particular morphological rule (e.g., adding an s to possessives). The word_forms_verbs and word_forms_nouns seem to be about whether a child can produce specific irregular past tenses or plurals. The word_endings_nouns and word_endings_verbs seem to be about overregularizations on nouns and verbs.
Combine data sets. Create binary variable for accuracy.
Complex <- Admin %>%
full_join(.,Inst, by="data_id") %>%
full_join(., Item, by="num_item_id") %>%
filter(longitudinal==FALSE) %>%
filter(type == "combine" |
type == "complexity" |
type == "word_endings" |
type== "word_forms_nouns" |
type == "word_forms_verbs" |
type == "word_endings_nouns" |
type == "word_endings_verbs"
) %>%
mutate(
out = ifelse(value=="complex" | value=="sometimes" | value=="produces", yes=1,
no = ifelse(value=="often", yes=2, no =0))
)
As a quick sanity check, let’s see which items have ordinal data (to make sure the above code worked correctly).
Complex %>%
filter(value=="often") %>%
group_by(definition) %>%
count()
Ok, it’s only the combine variable and word endings variables – that’s right.
Let’s see how many missing values there are.
Complex %>%
filter(is.na(out)) %>%
group_by(definition) %>%
count()
Looks like there are 1426 participants don’t have item-level complexity scores. There are a further 2 who don’t have word forms, and 1 who doesn’t have word endings.
Complex$complexity_category <- ifelse(Complex$complexity_category == "", yes=Complex$type, no=Complex$complexity_category)
Let’s look at means and SDs for each item:
Complex %>%
filter(!is.na(out)) %>%
group_by(definition) %>%
summarise(
mean=mean(out),
sd=sd(out),
sum=sum(out),
category = first(complexity_category)
) %>%
arrange(category) %>%
kable(caption="Means and SDs for Each Item (Arranged by Item)")
| definition | mean | sd | sum | category |
|---|---|---|---|---|
| has your child begun to combine word yet? | 1.2833572 | 0.7960586 | 3578 | combine |
| baby blanket / baby’s blanket | 0.3873745 | 0.4872378 | 1080 | morphology |
| daddy car / daddy’s car | 0.4451220 | 0.4970684 | 1241 | morphology |
| daddy pick me up / daddy picked me up | 0.1743185 | 0.3794512 | 486 | morphology |
| doggie kiss me / doggie kissed me | 0.1879484 | 0.3907410 | 524 | morphology |
| I fall down / I fell down | 0.1807747 | 0.3849005 | 504 | morphology |
| I make tower / I making tower | 0.2360115 | 0.4247055 | 658 | morphology |
| kitty go away / kitty went away | 0.1280488 | 0.3342041 | 357 | morphology |
| kitty sleep / kitty sleeping | 0.4192970 | 0.4935326 | 1169 | morphology |
| more cookie / more cookies | 0.3758967 | 0.4844404 | 1048 | morphology |
| these my tooth / these my teeth | 0.4533716 | 0.4979103 | 1264 | morphology |
| two foot / two feet | 0.4214491 | 0.4938798 | 1175 | morphology |
| two shoe / two shoes | 0.4422525 | 0.4967431 | 1233 | morphology |
| baby crying / baby crying cuz she’s sad | 0.1599713 | 0.3666452 | 446 | syntax |
| baby crying / baby is crying | 0.1624821 | 0.3689586 | 453 | syntax |
| baby want eat / baby want to eat | 0.2650646 | 0.4414468 | 739 | syntax |
| coffee hot / that coffee hot | 0.1929699 | 0.3947004 | 538 | syntax |
| cookie mommy / cookie for mommy | 0.2413917 | 0.4280040 | 673 | syntax |
| doggie table / doggie on table | 0.3511478 | 0.4774147 | 979 | syntax |
| don’t read book / don’t want you read that book | 0.1818508 | 0.3857907 | 507 | syntax |
| go bye-bye / wanna go bye-bye | 0.2679340 | 0.4429625 | 747 | syntax |
| I like read stories / I like to read stories | 0.1779053 | 0.3825016 | 496 | syntax |
| I no do it / I can’t do it | 0.2482066 | 0.4320498 | 692 | syntax |
| I sing song / I sing song for you | 0.1449067 | 0.3520699 | 404 | syntax |
| I want that / I want that one you got | 0.1137016 | 0.3175054 | 317 | syntax |
| lookit / lookit what I got | 0.2410330 | 0.4277870 | 672 | syntax |
| lookit me / lookit me dancing | 0.2596844 | 0.4385400 | 724 | syntax |
| no wash dolly / don’t wash dolly | 0.2116212 | 0.4085310 | 590 | syntax |
| read me story Mommy / read me a story Mommy | 0.1635581 | 0.3699405 | 456 | syntax |
| that my truck / that’s my truck | 0.2822812 | 0.4501902 | 787 | syntax |
| there a kitty / there’s a kitty | 0.3210187 | 0.4669517 | 895 | syntax |
| turn on light / turn on light so I can see | 0.0961263 | 0.2948172 | 268 | syntax |
| want cookies / want cookies and milk | 0.2048063 | 0.4036324 | 571 | syntax |
| want more juice / want juice in there | 0.2051650 | 0.4038946 | 572 | syntax |
| we made this / me and Paul made this | 0.1603300 | 0.3669776 | 447 | syntax |
| where’s my dolly / where’s my dolly name Sam | 0.1093974 | 0.3121932 | 305 | syntax |
| where mommy go / where did mommy go | 0.1822095 | 0.3860863 | 508 | syntax |
| you fix it / can you fix it | 0.1025825 | 0.3034672 | 286 | syntax |
| ed | 0.3589470 | 0.5835291 | 1009 | word_endings |
| ing | 0.6143721 | 0.7237652 | 1727 | word_endings |
| splural | 0.8701530 | 0.8247950 | 2446 | word_endings |
| spossess | 0.9558876 | 0.8877438 | 2687 | word_endings |
| blockses | 0.0211697 | 0.1439757 | 59 | word_endings_nouns |
| childrens | 0.0312164 | 0.1739332 | 87 | word_endings_nouns |
| childs | 0.0093290 | 0.0961526 | 26 | word_endings_nouns |
| feets | 0.1729458 | 0.3782683 | 482 | word_endings_nouns |
| foots | 0.1094367 | 0.3122423 | 305 | word_endings_nouns |
| mans | 0.0940079 | 0.2918921 | 262 | word_endings_nouns |
| mens | 0.0200933 | 0.1403446 | 56 | word_endings_nouns |
| mices | 0.0114819 | 0.1065557 | 32 | word_endings_nouns |
| mouses | 0.0606387 | 0.2387092 | 169 | word_endings_nouns |
| shoeses | 0.0782484 | 0.2686103 | 218 | word_endings_nouns |
| sockses | 0.0692501 | 0.2539245 | 193 | word_endings_nouns |
| teeths | 0.1395766 | 0.3466094 | 389 | word_endings_nouns |
| toeses | 0.0581270 | 0.2340255 | 162 | word_endings_nouns |
| tooths | 0.0534625 | 0.2249943 | 149 | word_endings_nouns |
| ated | 0.0200933 | 0.1403446 | 56 | word_endings_verbs |
| blewed | 0.0118407 | 0.1081882 | 33 | word_endings_verbs |
| blowed | 0.1115895 | 0.3149173 | 311 | word_endings_verbs |
| breaked | 0.0696089 | 0.2545324 | 194 | word_endings_verbs |
| bringed | 0.0487980 | 0.2154841 | 136 | word_endings_verbs |
| broked | 0.0900610 | 0.2863205 | 251 | word_endings_verbs |
| buyed | 0.0617151 | 0.2406806 | 172 | word_endings_verbs |
| camed | 0.0107643 | 0.1032096 | 30 | word_endings_verbs |
| comed | 0.0186581 | 0.1353385 | 52 | word_endings_verbs |
| doed | 0.0448511 | 0.2070141 | 125 | word_endings_verbs |
| dranked | 0.0276283 | 0.1639347 | 77 | word_endings_verbs |
| drinked | 0.0843201 | 0.2779171 | 235 | word_endings_verbs |
| eated | 0.0559742 | 0.2299131 | 156 | word_endings_verbs |
| falled | 0.0961607 | 0.2948645 | 268 | word_endings_verbs |
| flied | 0.0211697 | 0.1439757 | 59 | word_endings_verbs |
| getted | 0.0211697 | 0.1439757 | 59 | word_endings_verbs |
| goed | 0.0487980 | 0.2154841 | 136 | word_endings_verbs |
| gotted | 0.0125583 | 0.1113779 | 35 | word_endings_verbs |
| haved | 0.0143524 | 0.1189598 | 40 | word_endings_verbs |
| heared | 0.0200933 | 0.1403446 | 56 | word_endings_verbs |
| holded | 0.0362397 | 0.1869195 | 101 | word_endings_verbs |
| losed | 0.0211697 | 0.1439757 | 59 | word_endings_verbs |
| losted | 0.0165052 | 0.1274308 | 46 | word_endings_verbs |
| maked | 0.0423394 | 0.2013985 | 118 | word_endings_verbs |
| ranned | 0.0193757 | 0.1378662 | 54 | word_endings_verbs |
| runned | 0.0441335 | 0.2054285 | 123 | word_endings_verbs |
| satted | 0.0060997 | 0.0778763 | 17 | word_endings_verbs |
| seed | 0.0279871 | 0.1649654 | 78 | word_endings_verbs |
| sitted | 0.0258342 | 0.1586690 | 72 | word_endings_verbs |
| taked | 0.0405454 | 0.1972699 | 113 | word_endings_verbs |
| wented | 0.0086114 | 0.0924138 | 24 | word_endings_verbs |
| children | 0.1302943 | 0.3366874 | 363 | word_forms_nouns |
| feet | 0.5681981 | 0.4954161 | 1583 | word_forms_nouns |
| men | 0.1162958 | 0.3206368 | 324 | word_forms_nouns |
| mice | 0.0972721 | 0.2963811 | 271 | word_forms_nouns |
| teeth | 0.6977746 | 0.4593048 | 1944 | word_forms_nouns |
| ate | 0.2882268 | 0.4530185 | 803 | word_forms_verbs |
| blew | 0.1335248 | 0.3402021 | 372 | word_forms_verbs |
| bought | 0.1166547 | 0.3210660 | 325 | word_forms_verbs |
| broke | 0.4605169 | 0.4985281 | 1283 | word_forms_verbs |
| came | 0.1148600 | 0.3189102 | 320 | word_forms_verbs |
| drank | 0.1765973 | 0.3813960 | 492 | word_forms_verbs |
| drove | 0.0717875 | 0.2581821 | 200 | word_forms_verbs |
| fell | 0.3646805 | 0.4814269 | 1016 | word_forms_verbs |
| flew | 0.1105528 | 0.3136338 | 308 | word_forms_verbs |
| got | 0.2781766 | 0.4481813 | 775 | word_forms_verbs |
| had | 0.1256281 | 0.3314893 | 350 | word_forms_verbs |
| heard | 0.1127064 | 0.3162903 | 314 | word_forms_verbs |
| held | 0.0678392 | 0.2515149 | 189 | word_forms_verbs |
| lost | 0.2333094 | 0.4230134 | 650 | word_forms_verbs |
| made | 0.2132089 | 0.4096475 | 594 | word_forms_verbs |
| ran | 0.1819813 | 0.3858984 | 507 | word_forms_verbs |
| sat | 0.1593683 | 0.3660849 | 444 | word_forms_verbs |
| saw | 0.1870065 | 0.3899867 | 521 | word_forms_verbs |
| took | 0.1679828 | 0.3739181 | 468 | word_forms_verbs |
| went | 0.1719311 | 0.3773883 | 479 | word_forms_verbs |
Let’s also check the distribution of means for each category
Complex %>%
filter(!is.na(out)) %>%
group_by(definition) %>%
summarise(
mean=mean(out),
category = first(complexity_category),
cat2 = str_replace(category, "_", " " ) %>% str_replace("_", "\n" )
) %>%
ggplot(aes(x=cat2, y= mean)) + geom_boxplot()
It seems like the word endings and the combine words variable have higher means than the morphological and syntactic complexity items (though they are ordinal, but when I treated these as binary in an older version the results were the same). The over-regularization items have very low means, and the word forms looks a bit like the syntactic and morphological complexity.
One difficulty I just thought of is that the we would expect the word form and over-regularizations to be non-monotonially related to overall grammatical proficiency, given the inverse u shaped curve we typically see in the development of morphology.
Therefore, I’m going to first add the combine item and the word endings variables, see how that changes the results of the IRT model, and then plot the over-regularization and word form scores against this, to see if we see the non-monotonicity.
Prepare data set for IRT modeling. I’m going to re-name each item to its category (morphology or syntax) and its item number so as to make some graphs easier to read.
Complex_short_with_ids <- Complex %>%
dplyr::select(data_id, value, out, complexity_category, num_item_id) %>%
mutate(
label = str_c(complexity_category, num_item_id)
) %>%
pivot_wider(id_cols=data_id, names_from = "label", values_from="out") %>%
drop_na()
Complex_short <- Complex_short_with_ids %>%
dplyr::select(starts_with(c("combine", "morphology", "syntax")), c("word_endings686", "word_endings687", "word_endings688", "word_endings689")) # dataset for IRT can't have IDs
Prior to fitting the IRT model, it’s worth looking at the correlation structure of the data to get a sense of the possible dimensionality.
Create tetrachoric correlation matrix (for binary data). I created a vector with labels, for labeling of the correlation plot.
Complex_poly <- polychoric(Complex_short)
## Warning in polychoric(Complex_short): The items do not have an equal number of
## response alternatives, global set to FALSE.
## Warning in matpLower(x, nvar, gminx, gmaxx, gminy, gmaxy): 13 cells were
## adjusted for 0 values using the correction for continuity. Examine your data
## carefully.
## Warning in cor.smooth(mat): Matrix was not positive definite, smoothing was done
rho <- Complex_poly$rho
lab = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42") # create labels for corPlot because the defintions are long and take up a ton of space
Check out the correlations
corPlot(rho, labels=lab)
They look really high. Let’s examine the dimensionality, using princals, which is basically just a version of PCA that is appropriate for categorical data.
pc <- princals(rho)
plot(pc)
Seems like there are more intermediate points in between the two clouds of vectors I saw with the earlier version of the data set. However, the plot has fipped, so that most of the vectors are pointing toward the positive first component.
fa.parallel(rho, fa="fa", cor="poly", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Parallel analysis suggests that the number of factors = 7 and the number of components = NA
Parallel analysis suggests one very large factor and a small second one. Although with these additional items, I’m seeing some convergence complaints that I wasn’t seeing before. These dimensionality tests aren’t of substantive interest, so I’m not especially worried, but it’s worth keeping this in mind for the later models.
A third metric is very simple structure. This tells us the best solution assuming different values of k, where k is the maximum number of latent variables an observed variable loads on.
vss(rho, cor="poly", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
##
## Very Simple Structure
## Call: vss(x = rho, n.obs = 2785, cor = "poly")
## VSS complexity 1 achieves a maximimum of 1 with 1 factors
## VSS complexity 2 achieves a maximimum of 1 with 2 factors
##
## The Velicer MAP achieves a minimum of 0.02 with 3 factors
## BIC achieves a minimum of 74868.12 with 8 factors
## Sample Size adjusted BIC achieves a minimum of 76625.19 with 8 factors
##
## Statistics by number of factors
## vss1 vss2 map dof chisq prob sqresid fit RMSEA BIC SABIC complex
## 1 1.00 0.00 0.029 819 106986 0 3.38 1 0.22 100489 103092 1.0
## 2 0.50 1.00 0.018 778 96146 0 1.89 1 0.21 89975 92447 1.8
## 3 0.38 0.85 0.016 738 91813 0 1.50 1 0.21 85959 88304 2.4
## 4 0.38 0.83 0.017 699 88809 0 1.30 1 0.21 83265 85486 2.6
## 5 0.36 0.76 0.017 661 85563 0 1.10 1 0.21 80320 82421 3.0
## 6 0.38 0.77 0.017 624 82925 0 0.97 1 0.22 77975 79958 3.0
## 7 0.38 0.77 0.017 588 80796 0 0.85 1 0.22 76132 78000 3.1
## 8 0.38 0.78 0.019 553 79255 0 0.78 1 0.23 74868 76625 3.1
## eChisq SRMR eCRMS eBIC
## 1 4890 0.0319 0.033 -1606
## 2 1903 0.0199 0.021 -4268
## 3 1310 0.0165 0.018 -4544
## 4 1045 0.0148 0.016 -4499
## 5 784 0.0128 0.015 -4459
## 6 631 0.0115 0.013 -4319
## 7 493 0.0101 0.012 -4171
## 8 424 0.0094 0.012 -3963
Very simple structure also suggests one, if we assume each variable loads on one factor only. If we assume everything loads on 2 factors then a 2 factor solution is best. Again, note that the algorithm is having difficulty with estimation.
Let’s first fit the 2pl model and examine the output.
m1 <- mirt(Complex_short, 1, itemtype="graded", verbose=FALSE) # graded because we are treating all variables as ordinal.
m1
##
## Call:
## mirt(data = Complex_short, model = 1, itemtype = "graded", verbose = FALSE)
##
## Full-information item factor analysis with 1 factor(s).
## Converged within 1e-04 tolerance after 341 EM iterations.
## mirt version: 1.33.2
## M-step optimizer: BFGS
## EM acceleration: Ramsay
## Number of rectangular quadrature: 61
## Latent density type: Gaussian
##
## Log-likelihood = -34256.68
## Estimated parameters: 89
## AIC = 68691.36; AICc = 68697.31
## BIC = 69219.31; SABIC = 68936.53
## G2 (1e+10) = 35410.77, p = 1
## RMSEA = 0, CFI = NaN, TLI = NaN
summary(m1)
## F1 h2
## combine760 0.909 0.826
## morphology761 0.931 0.867
## morphology762 0.899 0.808
## morphology763 0.913 0.833
## morphology764 0.930 0.865
## morphology765 0.940 0.883
## morphology766 0.885 0.783
## morphology767 0.906 0.821
## morphology768 0.912 0.832
## morphology769 0.920 0.846
## morphology770 0.942 0.887
## morphology771 0.955 0.911
## morphology772 0.933 0.870
## syntax773 0.959 0.919
## syntax774 0.938 0.880
## syntax775 0.949 0.901
## syntax776 0.956 0.914
## syntax777 0.954 0.909
## syntax778 0.941 0.886
## syntax779 0.900 0.810
## syntax780 0.952 0.906
## syntax781 0.932 0.869
## syntax782 0.935 0.875
## syntax783 0.957 0.916
## syntax784 0.949 0.900
## syntax785 0.968 0.938
## syntax786 0.968 0.937
## syntax787 0.964 0.930
## syntax788 0.943 0.889
## syntax789 0.938 0.880
## syntax790 0.968 0.937
## syntax791 0.972 0.944
## syntax792 0.947 0.897
## syntax793 0.966 0.932
## syntax794 0.935 0.874
## syntax795 0.915 0.838
## syntax796 0.968 0.937
## syntax797 0.954 0.910
## word_endings686 0.839 0.705
## word_endings687 0.865 0.748
## word_endings688 0.845 0.714
## word_endings689 0.886 0.784
##
## SS loadings: 36.511
## Proportion Var: 0.869
##
## Factor correlations:
##
## F1
## F1 1
Let’s check the overall fit of the model. The chi square statistic here isn’t super meaningful given the sample size.
M2(m1)
RMSEA and CFI are good, SRMSR is a bit high but pretty reasonable. We can see if individual items misfit – i.e., do the observed cell counts differ from the expected cell counts.
itemfit(m1)
A few items significantly misfit. Let’s take everything with a p value less than .10. We’ll look at item gam plots of those, which plot the item response probability against the latent variable using a gam function, to look for deviations in the item-response function.
misfit <- itemfit(m1) %>% # Get labels of mis-fitting items.
filter(p.S_X2 <= .10) %>%
dplyr::select(item) %>%
as.vector()
items_good <- dplyr::select(Complex_short, -all_of(misfit$item)) # Well fitting items
items_bad <- dplyr::select(Complex_short, all_of(misfit$item)) # Poorly fitting items
mod_fit <- mirt(items_good, 1, "graded", verbose=FALSE) # Calculate factor scores using only the well fitting items.
Theta <- fscores(mod_fit)
IG760 <- itemGAM(items_bad$combine760, Theta)
IG762 <- itemGAM(items_bad$morphology762,Theta)
IG769<- itemGAM(items_bad$morphology769,Theta)
IG771<- itemGAM(items_bad$morphology771,Theta)
IG775 <- itemGAM(items_bad$syntax775,Theta)
IG776 <- itemGAM(items_bad$syntax776,Theta)
IG785 <- itemGAM(items_bad$syntax785,Theta)
IG787<- itemGAM(items_bad$syntax787,Theta)
IG688 <- itemGAM(items_bad$word_endings688,Theta)
IG689<- itemGAM(items_bad$word_endings689,Theta)
Combine
plot(IG760)
Flattens out pretty quickly at a difficulty of 0.
plot(IG762); plot(IG769); plot(IG771)
Syntax items
plot(IG775); plot(IG776); plot(IG785); plot(IG787)
plot(IG688); plot(IG689)
Next let’s look at the relationship between the true scores and the raw scores.
true_raw <- Complex_short %>%
mutate(
Raw = rowSums(.[,1:42]),
Theta = fscores(m1, method="MAP")
) %>%
ggplot(aes(x=Theta, y=Raw)) + geom_point() + stat_smooth(method="loess") + theme_minimal()
raw_score <- Complex_short %>%
mutate(
Raw = rowSums(.[,1:42])
) %>%
ggplot(aes(x=Raw)) + geom_histogram() + theme_minimal()
true_score <- Complex_short %>%
mutate(
Theta = fscores(m1, method="MAP")
) %>%
ggplot(aes(x=Theta)) + geom_histogram() + theme_minimal()
library(patchwork)
true_raw/(true_score + raw_score)
## `geom_smooth()` using formula 'y ~ x'
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Well, we’re getting a little more sensitivity at the lower end of the scale than we had before, and the results are starting to look a bit closer to gaussian-ish.
Next let’s take a look at the difficulty and discrimination parameter for each item.
Note to self: This block of code will need to be fixed if I treat variables as ordinal, as the output of coef is now different.
#coefs_2pl <- coef(m1, as.data.frame = TRUE) %>%
# t() %>%
# as_tibble() %>%
# dplyr::select(-c(149:150)) %>%
# pivot_longer(everything()) %>%
# separate(, col=name, into=c("item", "parameter"), sep="([.])") %>%
# pivot_wider(id_cols=item, names_from=parameter, values_from=value) %>%
# dplyr::select(item, a1, d) %>%
# mutate(
# category = gsub('[[:digit:]]+', '', item)
# )
#ggplot(coefs_2pl,
# aes(x = a1, y = d)) +
# geom_point(alpha = .3) +
# ggrepel::geom_text_repel(data = coefs_2pl,
# aes(label = item), size = 3) +
# xlab("Discrimination") +
# ylab("Difficulty") + theme_minimal()
#ggplot(coefs_2pl, aes(x=a1, fill=category)) + geom_histogram() + theme_minimal()
#ggplot(coefs_2pl, aes(x=d, fill=category)) + geom_histogram() + theme_minimal()
I was worried that the over-regularization and irregular items would be non-monotonically related to the factor scores, since we expect children to go through qualitatively distinct stages. So I summed these sections and plotted them against the factor score below.
Theta_by_Wordforms <- Complex_short_with_ids %>%
mutate(
rn = row_number()
) %>%
transmute(
Word_form = rowSums(.[,6:30]),
Over_Reg = rowSums(.[,31:75]),
Theta = as.numeric(fscores(m1, "MAP"))
)
p1 <- ggplot(Theta_by_Wordforms, aes(x=Theta, y=Word_form)) + geom_point() + stat_smooth(method="loess")
p2 <- ggplot(Theta_by_Wordforms, aes(x=Theta, y=Over_Reg)) + geom_point() + stat_smooth(method="loess")
p3 <- ggplot(Theta_by_Wordforms, aes(x=Theta)) + geom_histogram()
p4 <- ggplot(Theta_by_Wordforms, aes(x=Word_form)) + geom_histogram()
p5 <- ggplot(Theta_by_Wordforms, aes(x=Over_Reg)) + geom_histogram()
(p1 | p2)/(p3 | p4 | p5)
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Interesting to see that neither is clearly non-monotonic, even though that’s what we would expect from theories of language acquisition.
Now I’m going to try adding each of those sets of variables to the model I fit above. I do the word forms first and then the over-regularization
Complex_short_with_ids <- Complex %>%
dplyr::select(data_id, value, out, complexity_category, num_item_id) %>%
mutate(
label = str_c(complexity_category, num_item_id)
) %>%
pivot_wider(id_cols=data_id, names_from = "label", values_from="out") %>%
drop_na()
Complex_short <- Complex_short_with_ids %>%
dplyr::select(starts_with(c("combine", "morphology", "syntax", "word_forms")), c("word_endings686", "word_endings687", "word_endings688", "word_endings689")) # dataset for IRT can't have IDs
Complex_poly <- polychoric(Complex_short)
## Warning in polychoric(Complex_short): The items do not have an equal number of
## response alternatives, global set to FALSE.
## Warning in matpLower(x, nvar, gminx, gmaxx, gminy, gmaxy): 13 cells were
## adjusted for 0 values using the correction for continuity. Examine your data
## carefully.
## Warning in cor.smooth(mat): Matrix was not positive definite, smoothing was done
rho <- Complex_poly$rho
lab = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60",
"61", "62", "63", "64", "65", "66", "67") # create labels for corPlot because the defintions are long and take up a ton of space
corPlot(rho, labels=lab)
pc <- princals(rho)
plot(pc)
Looks like there’s a big clumb of vectors in one area but then way more pointing out in random directions, which I think will create some fit issues.
fa.parallel(rho, fa="fa", cor="poly", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Parallel analysis suggests that the number of factors = 8 and the number of components = NA
vss(rho, fa="fa", cor="tet", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
##
## Very Simple Structure
## Call: vss(x = rho, n.obs = 2785, cor = "tet", fa = "fa")
## VSS complexity 1 achieves a maximimum of 0.99 with 1 factors
## VSS complexity 2 achieves a maximimum of 1 with 2 factors
##
## The Velicer MAP achieves a minimum of 0.01 with 5 factors
## BIC achieves a minimum of 359826.3 with 8 factors
## Sample Size adjusted BIC achieves a minimum of 365237.3 with 8 factors
##
## Statistics by number of factors
## vss1 vss2 map dof chisq prob sqresid fit RMSEA BIC SABIC complex
## 1 0.99 0.00 0.035 2144 426325 0 14.4 0.99 0.27 409319 416131 1.0
## 2 0.56 1.00 0.018 2078 403934 0 7.0 1.00 0.26 387451 394054 1.7
## 3 0.37 0.85 0.013 2013 391590 0 4.7 1.00 0.26 375623 382019 2.2
## 4 0.37 0.85 0.013 1949 385741 0 4.1 1.00 0.27 370282 376474 2.3
## 5 0.37 0.84 0.012 1886 382110 0 3.4 1.00 0.27 367150 373142 2.4
## 6 0.38 0.83 0.012 1824 378989 0 3.1 1.00 0.27 364521 370316 2.5
## 7 0.38 0.83 0.012 1763 376197 0 2.8 1.00 0.28 362213 367815 2.5
## 8 0.38 0.83 0.013 1703 373335 0 2.6 1.00 0.28 359826 365237 2.5
## eChisq SRMR eCRMS eBIC
## 1 26667 0.047 0.047 9661
## 2 9604 0.028 0.029 -6879
## 3 5044 0.020 0.021 -10923
## 4 3928 0.018 0.019 -11531
## 5 3050 0.016 0.017 -11909
## 6 2636 0.015 0.016 -11832
## 7 2352 0.014 0.015 -11632
## 8 2065 0.013 0.015 -11443
m2 <- mirt(Complex_short, 1, itemtype="graded", verbose=FALSE)
summary(m2)
## F1 h2
## combine760 0.894 0.799
## morphology761 0.924 0.853
## morphology762 0.894 0.800
## morphology763 0.900 0.809
## morphology764 0.919 0.845
## morphology765 0.932 0.869
## morphology766 0.882 0.778
## morphology767 0.897 0.805
## morphology768 0.905 0.820
## morphology769 0.905 0.819
## morphology770 0.941 0.885
## morphology771 0.955 0.911
## morphology772 0.927 0.860
## syntax773 0.952 0.906
## syntax774 0.928 0.861
## syntax775 0.939 0.882
## syntax776 0.953 0.909
## syntax777 0.947 0.898
## syntax778 0.930 0.866
## syntax779 0.891 0.794
## syntax780 0.943 0.889
## syntax781 0.925 0.855
## syntax782 0.929 0.864
## syntax783 0.949 0.901
## syntax784 0.944 0.890
## syntax785 0.963 0.927
## syntax786 0.962 0.926
## syntax787 0.961 0.923
## syntax788 0.933 0.871
## syntax789 0.930 0.864
## syntax790 0.961 0.924
## syntax791 0.964 0.929
## syntax792 0.939 0.881
## syntax793 0.960 0.922
## syntax794 0.928 0.861
## syntax795 0.902 0.814
## syntax796 0.962 0.926
## syntax797 0.950 0.902
## word_forms_nouns690 0.811 0.658
## word_forms_nouns691 0.757 0.573
## word_forms_nouns692 0.736 0.541
## word_forms_nouns693 0.699 0.489
## word_forms_nouns694 0.792 0.627
## word_forms_verbs695 0.878 0.772
## word_forms_verbs696 0.836 0.699
## word_forms_verbs697 0.896 0.803
## word_forms_verbs698 0.821 0.674
## word_forms_verbs699 0.885 0.782
## word_forms_verbs700 0.872 0.761
## word_forms_verbs701 0.866 0.751
## word_forms_verbs702 0.865 0.748
## word_forms_verbs703 0.896 0.803
## word_forms_verbs704 0.880 0.774
## word_forms_verbs705 0.914 0.835
## word_forms_verbs706 0.922 0.850
## word_forms_verbs707 0.905 0.818
## word_forms_verbs708 0.893 0.798
## word_forms_verbs709 0.936 0.877
## word_forms_verbs710 0.830 0.688
## word_forms_verbs711 0.873 0.761
## word_forms_verbs712 0.920 0.846
## word_forms_verbs713 0.877 0.768
## word_forms_verbs714 0.910 0.827
## word_endings686 0.834 0.696
## word_endings687 0.855 0.731
## word_endings688 0.846 0.716
## word_endings689 0.894 0.799
##
## SS loadings: 54.503
## Proportion Var: 0.813
##
## Factor correlations:
##
## F1
## F1 1
M2(m2)
m2b <- mirt(Complex_short, 2, "graded", verbose=FALSE)
summary(m2b, "oblimin", suppress=.20)
##
## Rotation: oblimin
##
## Rotated factor loadings:
##
## F1 F2 h2
## combine760 -0.941 NA 0.823
## morphology761 -0.913 NA 0.862
## morphology762 -0.767 NA 0.802
## morphology763 -0.992 NA 0.832
## morphology764 -0.952 NA 0.859
## morphology765 -0.910 NA 0.876
## morphology766 -0.679 0.218 0.772
## morphology767 -0.868 NA 0.813
## morphology768 -0.856 NA 0.828
## morphology769 -1.044 NA 0.848
## morphology770 -0.733 0.223 0.878
## morphology771 -0.750 0.220 0.905
## morphology772 -0.834 NA 0.858
## syntax773 -0.923 NA 0.914
## syntax774 -0.946 NA 0.873
## syntax775 -0.975 NA 0.896
## syntax776 -0.845 NA 0.907
## syntax777 -0.938 NA 0.904
## syntax778 -1.005 NA 0.884
## syntax779 -0.919 NA 0.804
## syntax780 -0.955 NA 0.901
## syntax781 -0.889 NA 0.862
## syntax782 -0.851 NA 0.864
## syntax783 -0.994 NA 0.914
## syntax784 -0.869 NA 0.893
## syntax785 -0.933 NA 0.933
## syntax786 -0.931 NA 0.932
## syntax787 -0.907 NA 0.926
## syntax788 -0.976 NA 0.885
## syntax789 -0.951 NA 0.876
## syntax790 -0.973 NA 0.935
## syntax791 -0.982 NA 0.941
## syntax792 -0.946 NA 0.893
## syntax793 -0.906 NA 0.927
## syntax794 -0.898 NA 0.865
## syntax795 -0.997 NA 0.835
## syntax796 -0.956 NA 0.934
## syntax797 -0.896 NA 0.906
## word_forms_nouns690 -0.438 0.388 0.644
## word_forms_nouns691 NA 0.714 0.637
## word_forms_nouns692 NA 0.835 0.590
## word_forms_nouns693 NA 0.688 0.513
## word_forms_nouns694 NA 0.682 0.686
## word_forms_verbs695 NA 0.764 0.816
## word_forms_verbs696 NA 0.949 0.770
## word_forms_verbs697 NA 0.733 0.826
## word_forms_verbs698 NA 0.749 0.739
## word_forms_verbs699 NA 0.797 0.814
## word_forms_verbs700 NA 0.921 0.830
## word_forms_verbs701 NA 1.013 0.827
## word_forms_verbs702 NA 0.816 0.816
## word_forms_verbs703 NA 0.801 0.837
## word_forms_verbs704 -0.238 0.676 0.799
## word_forms_verbs705 NA 0.839 0.877
## word_forms_verbs706 -0.207 0.749 0.878
## word_forms_verbs707 NA 0.945 0.882
## word_forms_verbs708 NA 0.774 0.840
## word_forms_verbs709 -0.396 0.571 0.884
## word_forms_verbs710 0.226 1.098 0.817
## word_forms_verbs711 NA 0.947 0.838
## word_forms_verbs712 -0.251 0.703 0.870
## word_forms_verbs713 NA 0.908 0.835
## word_forms_verbs714 -0.260 0.680 0.845
## word_endings686 -0.692 NA 0.695
## word_endings687 -0.793 NA 0.737
## word_endings688 -0.582 0.281 0.708
## word_endings689 NA 0.727 0.778
##
## Rotated SS loadings: 34.058 17.043
##
## Factor correlations:
##
## F1 F2
## F1 1.000 -0.888
## F2 -0.888 1.000
It looks like the word forms are loading consistently on a different factor from the morphosyntactic complexity factor.
Another possibility is that all of those items are guesses–especially since they’re means are all low.
Complex_short_with_ids <- Complex %>%
dplyr::select(data_id, value, out, complexity_category, num_item_id) %>%
mutate(
label = str_c(complexity_category, num_item_id)
) %>%
pivot_wider(id_cols=data_id, names_from = "label", values_from="out") %>%
drop_na()
Complex_short <- Complex_short_with_ids %>%
dplyr::select(starts_with(c("combine", "morphology", "syntax", "word_endings"))) # dataset for IRT can't have IDs
Complex_poly <- polychoric(Complex_short)
## Warning in polychoric(Complex_short): The items do not have an equal number of
## response alternatives, global set to FALSE.
## Warning in matpLower(x, nvar, gminx, gmaxx, gminy, gmaxy): 31 cells were
## adjusted for 0 values using the correction for continuity. Examine your data
## carefully.
## Warning in cor.smooth(mat): Matrix was not positive definite, smoothing was done
rho <- Complex_poly$rho
lab = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60",
"61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80",
"81", "82", "83", "84", "85", "86", "87" ) # create labels for corPlot because the defintions are long and take up a ton of space
corPlot(rho, labels=lab)
pc <- princals(rho)
plot(pc)
fa.parallel(rho, fa="fa", cor="tet", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Parallel analysis suggests that the number of factors = 15 and the number of components = NA
vss(rho, fa="fa", cor="tet", n.obs = 2785)
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
## Warning in fa.stats(r = r, f = f, phi = phi, n.obs = n.obs, np.obs = np.obs, :
## The estimated weights for the factor scores are probably incorrect. Try a
## different factor score estimation method.
##
## Very Simple Structure
## Call: vss(x = rho, n.obs = 2785, cor = "tet", fa = "fa")
## VSS complexity 1 achieves a maximimum of 0.93 with 1 factors
## VSS complexity 2 achieves a maximimum of 0.99 with 2 factors
##
## The Velicer MAP achieves a minimum of 0.01 with 5 factors
## BIC achieves a minimum of 1230373 with 8 factors
## Sample Size adjusted BIC achieves a minimum of 1240137 with 8 factors
##
## Statistics by number of factors
## vss1 vss2 map dof chisq prob sqresid fit RMSEA BIC SABIC complex
## 1 0.93 0.00 0.089 3654 1359800 0 169 0.93 0.37 1330816 1342426 1.0
## 2 0.72 0.99 0.017 3568 1293765 0 26 0.99 0.36 1265464 1276801 1.3
## 3 0.70 0.97 0.015 3483 1281702 0 20 0.99 0.36 1254075 1265142 1.4
## 4 0.69 0.97 0.014 3399 1274046 0 17 0.99 0.37 1247085 1257885 1.5
## 5 0.69 0.96 0.014 3316 1268498 0 15 0.99 0.37 1242196 1252732 1.6
## 6 0.68 0.95 0.014 3234 1263953 0 14 0.99 0.37 1238301 1248576 1.7
## 7 0.69 0.94 0.014 3153 1259157 0 13 0.99 0.38 1234147 1244165 1.7
## 8 0.69 0.94 0.014 3073 1254748 0 11 1.00 0.38 1230373 1240137 1.8
## eChisq SRMR eCRMS eBIC
## 1 407904 0.140 0.142 378921
## 2 39389 0.043 0.045 11087
## 3 27362 0.036 0.038 -265
## 4 21675 0.032 0.034 -5286
## 5 19086 0.030 0.032 -7217
## 6 17125 0.029 0.031 -8527
## 7 15278 0.027 0.029 -9731
## 8 13617 0.026 0.028 -10758
m3b <- mirt(Complex_short, 2, "graded", verbose=FALSE)
## EM cycles terminated after 500 iterations.
summary(m3b, "oblimin", suppress=.20)
##
## Rotation: oblimin
##
## Rotated factor loadings:
##
## F1 F2 h2
## combine760 NA 0.978 0.835
## morphology761 NA 0.908 0.867
## morphology762 NA 0.883 0.806
## morphology763 NA 0.954 0.838
## morphology764 NA 0.935 0.865
## morphology765 NA 0.920 0.883
## morphology766 NA 0.901 0.784
## morphology767 NA 0.889 0.822
## morphology768 NA 0.907 0.831
## morphology769 NA 0.940 0.848
## morphology770 NA 0.871 0.893
## morphology771 NA 0.922 0.915
## morphology772 NA 0.914 0.877
## syntax773 NA 0.965 0.920
## syntax774 NA 0.943 0.881
## syntax775 NA 0.971 0.901
## syntax776 NA 0.946 0.916
## syntax777 NA 0.984 0.911
## syntax778 NA 0.974 0.888
## syntax779 NA 0.854 0.816
## syntax780 NA 0.944 0.906
## syntax781 NA 0.910 0.869
## syntax782 NA 0.950 0.875
## syntax783 NA 0.936 0.918
## syntax784 NA 0.920 0.902
## syntax785 NA 0.965 0.939
## syntax786 NA 0.919 0.939
## syntax787 NA 0.897 0.940
## syntax788 NA 0.871 0.903
## syntax789 NA 0.907 0.881
## syntax790 NA 0.944 0.938
## syntax791 NA 0.955 0.944
## syntax792 NA 0.916 0.897
## syntax793 NA 0.937 0.932
## syntax794 NA 0.901 0.883
## syntax795 NA 0.880 0.844
## syntax796 NA 0.928 0.940
## syntax797 NA 0.901 0.915
## word_endings686 NA 0.782 0.706
## word_endings687 NA 0.865 0.749
## word_endings688 NA 0.718 0.735
## word_endings689 -0.201 0.749 0.820
## word_endings_nouns715 -0.894 NA 0.665
## word_endings_nouns716 -0.602 0.352 0.792
## word_endings_nouns717 -0.776 NA 0.837
## word_endings_nouns718 -0.860 NA 0.664
## word_endings_nouns719 -0.828 NA 0.673
## word_endings_nouns720 -0.552 0.338 0.689
## word_endings_nouns721 -0.551 0.373 0.738
## word_endings_nouns722 -0.660 0.304 0.817
## word_endings_nouns723 -0.538 0.352 0.687
## word_endings_nouns724 -1.046 -0.379 0.667
## word_endings_nouns725 -1.046 -0.356 0.684
## word_endings_nouns726 -0.957 -0.231 0.651
## word_endings_nouns727 -0.986 -0.228 0.700
## word_endings_nouns728 -0.904 NA 0.709
## word_endings_verbs729 -0.903 NA 0.804
## word_endings_verbs730 -0.820 NA 0.786
## word_endings_verbs731 -0.532 0.416 0.776
## word_endings_verbs732 -0.652 0.344 0.866
## word_endings_verbs733 -0.608 0.381 0.850
## word_endings_verbs734 -0.706 0.228 0.784
## word_endings_verbs735 -0.741 NA 0.625
## word_endings_verbs736 -0.834 NA 0.854
## word_endings_verbs737 -0.752 NA 0.787
## word_endings_verbs738 -0.729 NA 0.693
## word_endings_verbs739 -0.782 NA 0.815
## word_endings_verbs740 -0.727 0.217 0.804
## word_endings_verbs741 -0.815 NA 0.840
## word_endings_verbs742 -0.739 NA 0.756
## word_endings_verbs743 -0.546 0.420 0.805
## word_endings_verbs744 -0.899 NA 0.843
## word_endings_verbs745 -0.762 NA 0.730
## word_endings_verbs746 -0.833 NA 0.889
## word_endings_verbs747 -0.758 NA 0.818
## word_endings_verbs748 -0.653 0.341 0.863
## word_endings_verbs749 -0.674 0.288 0.818
## word_endings_verbs750 -0.679 0.325 0.886
## word_endings_verbs751 -0.940 NA 0.843
## word_endings_verbs752 -0.701 0.292 0.873
## word_endings_verbs753 -0.881 NA 0.859
## word_endings_verbs754 -0.633 0.337 0.822
## word_endings_verbs755 -0.778 NA 0.830
## word_endings_verbs756 -0.879 NA 0.916
## word_endings_verbs757 -0.793 NA 0.848
## word_endings_verbs758 -0.662 0.312 0.834
## word_endings_verbs759 -0.833 NA 0.825
##
## Rotated SS loadings: 27.328 37.616
##
## Factor correlations:
##
## F1 F2
## F1 1.000 -0.721
## F2 -0.721 1.000
As was the case with the word form variables, all of the word endings variables loaded onto a separate factor from the morphosyntactic complexity variables.
So a quick re-cap: I was concerned about including the word form and over-regularizations variables to the IRT models because they are theoretically non-monotonically related to syntactic proficiency. We would expect children to initially produce high frequency irregular forms, then go through a stage of over-regularization, and finally to produce the correct irregular word forms again. However, when I plotted the total scores on these subscales against a syntactic proficiency variable derived from the other subscales, I did not see evidence of a non-monotonic relationship. Rather in both cases, subscale scores increased with increasing syntactic proficiency, though it appeared to be leveling off for the over-regularizations.
I then tried fitting two more models, one that included the word form variables and another that included the overgeneralization variables. Exploration of the dimensionality suggested that adding these variables resulted in a much clearer multi-dimensional structure (see categorical PCAs and scree plots). In both cases, exploratory two dimensional IRTs indicated two-factor solutions in which one factor was dominated by the morphosyntactic complexity variables and the other was dominated by the word form or overregularization variables.
It’s difficult to say exactly what this means, as these results could plausibly be due to different psychological demands of learning the relevant structure or some sort of difference in reporting biases across the scales.
I think I’m going to stick with the model that includes the complexity variable and the word endings variables as they seem to increase the sensitivity of the model to lower proficiency children and do not appear to affect the dimensionality much.
Get the data set back.
Complex_short_with_ids <- Complex %>%
dplyr::select(data_id, value, out, complexity_category, num_item_id) %>%
mutate(
label = str_c(complexity_category, num_item_id)
) %>%
pivot_wider(id_cols=data_id, names_from = "label", values_from="out") %>%
drop_na()
Complex_short <- Complex_short_with_ids %>%
dplyr::select(starts_with(c("combine", "morphology", "syntax")), c("word_endings686", "word_endings687", "word_endings688", "word_endings689")) # dataset for IRT can't have IDs
summary(m1)
## F1 h2
## combine760 0.909 0.826
## morphology761 0.931 0.867
## morphology762 0.899 0.808
## morphology763 0.913 0.833
## morphology764 0.930 0.865
## morphology765 0.940 0.883
## morphology766 0.885 0.783
## morphology767 0.906 0.821
## morphology768 0.912 0.832
## morphology769 0.920 0.846
## morphology770 0.942 0.887
## morphology771 0.955 0.911
## morphology772 0.933 0.870
## syntax773 0.959 0.919
## syntax774 0.938 0.880
## syntax775 0.949 0.901
## syntax776 0.956 0.914
## syntax777 0.954 0.909
## syntax778 0.941 0.886
## syntax779 0.900 0.810
## syntax780 0.952 0.906
## syntax781 0.932 0.869
## syntax782 0.935 0.875
## syntax783 0.957 0.916
## syntax784 0.949 0.900
## syntax785 0.968 0.938
## syntax786 0.968 0.937
## syntax787 0.964 0.930
## syntax788 0.943 0.889
## syntax789 0.938 0.880
## syntax790 0.968 0.937
## syntax791 0.972 0.944
## syntax792 0.947 0.897
## syntax793 0.966 0.932
## syntax794 0.935 0.874
## syntax795 0.915 0.838
## syntax796 0.968 0.937
## syntax797 0.954 0.910
## word_endings686 0.839 0.705
## word_endings687 0.865 0.748
## word_endings688 0.845 0.714
## word_endings689 0.886 0.784
##
## SS loadings: 36.511
## Proportion Var: 0.869
##
## Factor correlations:
##
## F1
## F1 1
M2(m1)
m1b <- mirt(Complex_short, 2, "graded", verbose=FALSE)
anova(m1, m1b)
##
## Model 1: mirt(data = Complex_short, model = 1, itemtype = "graded", verbose = FALSE)
## Model 2: mirt(data = Complex_short, model = 2, itemtype = "graded", verbose = FALSE)
summary(m1b, "oblimin", suppress=.2)
##
## Rotation: oblimin
##
## Rotated factor loadings:
##
## F1 F2 h2
## combine760 -0.882 NA 0.822
## morphology761 -0.883 NA 0.876
## morphology762 -0.861 NA 0.802
## morphology763 -0.818 0.389 0.923
## morphology764 -0.894 NA 0.859
## morphology765 -0.920 NA 0.865
## morphology766 -0.858 NA 0.761
## morphology767 -0.861 NA 0.815
## morphology768 -0.894 NA 0.821
## morphology769 -0.843 0.327 0.908
## morphology770 -0.916 NA 0.877
## morphology771 -0.931 NA 0.907
## morphology772 -0.906 NA 0.863
## syntax773 -0.947 NA 0.905
## syntax774 -0.906 NA 0.875
## syntax775 -0.936 NA 0.883
## syntax776 -0.954 NA 0.900
## syntax777 -0.945 NA 0.893
## syntax778 -0.939 NA 0.869
## syntax779 -0.895 NA 0.784
## syntax780 -0.925 NA 0.899
## syntax781 -0.934 NA 0.854
## syntax782 -0.919 NA 0.854
## syntax783 -0.958 NA 0.901
## syntax784 -0.953 NA 0.888
## syntax785 -0.967 NA 0.926
## syntax786 -0.979 NA 0.934
## syntax787 -0.982 NA 0.938
## syntax788 -0.961 -0.211 0.902
## syntax789 -0.942 NA 0.866
## syntax790 -0.973 NA 0.929
## syntax791 -0.972 NA 0.935
## syntax792 -0.951 NA 0.884
## syntax793 -0.973 NA 0.926
## syntax794 -0.939 NA 0.859
## syntax795 -0.922 NA 0.827
## syntax796 -0.977 NA 0.932
## syntax797 -0.962 NA 0.902
## word_endings686 -0.777 0.234 0.717
## word_endings687 -0.785 0.320 0.800
## word_endings688 -0.816 NA 0.690
## word_endings689 -0.863 NA 0.726
##
## Rotated SS loadings: 35.26 0.852
##
## Factor correlations:
##
## F1 F2
## F1 1.000 -0.163
## F2 -0.163 1.000
We have a similar solution to what we had in the first version of this analysis. The factor model fits better but it’s not at all clear what the second factor is adding. Everything loads strongly on the first factor and very little on the second factor.
Comapre with 1 factor graded model with semi-parametric latent variable specification.
m1_nonpar <- mirt(Complex_short, 1, itemtype="graded", dentype = "EH", verbose=FALSE)
M2(m1_nonpar)
Complex_short %>%
mutate(
Theta_EH = fscores(m1_nonpar, method="MAP"),
Theta_gauss = fscores(m1, method="MAP")
) %>%
ggplot(aes(x=Theta_EH, y=Theta_gauss)) + geom_point() + stat_smooth(method="loess") + theme_minimal()
## `geom_smooth()` using formula 'y ~ x'
m1_nonpar2 <- mirt(Complex_short, 1, itemtype="graded", dentype = "EHW", verbose=FALSE)
## EM cycles terminated after 2000 iterations.
M2(m1_nonpar2)
Complex_short %>%
mutate(
Theta_EH = fscores(m1_nonpar2, method="MAP"),
Theta_gauss = fscores(m1, method="MAP")
) %>%
ggplot(aes(x=Theta_EH, y=Theta_gauss)) + geom_point() + stat_smooth(method="loess") + theme_minimal()
## Warning: The following factor score estimates failed to converge successfully:
## 647
## `geom_smooth()` using formula 'y ~ x'
These semi-parametric alternatives look pretty similar. I think in both cases, the Gaussian model is a bit easier to interpret and will likely make it easier to work with other functionality.
I mentioned in the last meeting that these models assume measusrement invariance – basically the probability of producing an item is the same for two children of the same estimated ability who differ according to some other grouping variable. I have begun to assess this, using differential item functioning technques using some of the predictors in the data set. I should say, I’m out over my skis a bit here at this point.
Create data sets with predictor variables for
Complex_short_with_ids <- Complex %>%
dplyr::select(data_id, value, out, complexity_category, num_item_id) %>%
mutate(
label = str_c(complexity_category, num_item_id)
) %>%
dplyr::select(data_id, label, out) %>%
pivot_wider(id_cols=data_id, names_from = "label", values_from="out") %>%
drop_na()
Complex_short <- Complex_short_with_ids %>%
dplyr::select(starts_with(c("combine", "morphology", "syntax")), c("word_endings686", "word_endings687", "word_endings688", "word_endings689")) # dataset for IRT can't have IDs
Complex_with_predictors <- Admin %>%
dplyr::select(data_id, age, ethnicity, sex) %>%
mutate(
female = ifelse(sex=="Female", yes=1, no=0),
age_y = ifelse(age < 25, yes=1, no=0)
) %>%
right_join(., Complex_short_with_ids, by="data_id")
Complex_with_predictors %>%
summarise(
age_missing = sum(is.na(age)),
ethnicity_missing = sum(is.na(ethnicity)), # Some ethnicity variables missing.
sex_missing = sum(is.na(sex))
)
table(Complex_with_predictors$age)
##
## 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
## 185 133 323 219 172 110 112 136 420 246 142 138 104 118 227
table(Complex_with_predictors$ethnicity)
##
## Asian Black Other White Hispanic
## 67 222 93 2202 131
table(Complex_with_predictors$sex)
##
## Female Male Other
## 1372 1413 0
Use lordif to look for DIF. I’ll start with sex first. I’ve not included the code here, the lordif function, which I used, does not allow verbose=FALSE so it produces a lot of extra information.
dif
## Call:
## lordif(resp.data = Complex_short2, group = Complex_with_predictors$female,
## criterion = "Chisqr", alpha = 0.01)
##
## Number of DIF groups: 2
##
## Number of items flagged for DIF: 2 of 42
##
## Items flagged: 32, 41
##
## Number of iterations for purification: 2 of 10
##
## Detection criterion: Chisqr
##
## Threshold: alpha = 0.01
##
## item ncat chi12 chi13 chi23
## 1 1 3 0.7743 0.6636 0.3904
## 2 2 2 0.9241 0.6157 0.3270
## 3 3 2 0.9512 0.9867 0.8794
## 4 4 2 0.8690 0.9863 0.9846
## 5 5 2 0.8975 0.8291 0.5495
## 6 6 2 0.4033 0.5775 0.5274
## 7 7 2 0.3206 0.5316 0.5985
## 8 8 2 0.6836 0.8488 0.6875
## 9 9 2 0.8184 0.9651 0.8925
## 10 10 2 0.0538 0.0621 0.1752
## 11 11 2 0.8915 0.8355 0.5593
## 12 12 2 0.0629 0.0300 0.0594
## 13 13 2 0.4924 0.7323 0.6968
## 14 14 2 0.1439 0.3214 0.7141
## 15 15 2 0.3602 0.3547 0.2663
## 16 16 2 0.3432 0.1444 0.0847
## 17 17 2 0.8740 0.1595 0.0562
## 18 18 2 0.4376 0.6299 0.5705
## 19 19 2 0.0655 0.0956 0.2536
## 20 20 2 0.4993 0.7945 0.9514
## 21 21 2 0.9250 0.3804 0.1654
## 22 22 2 0.7012 0.7687 0.5381
## 23 23 2 0.1270 0.2772 0.6261
## 24 24 2 0.3017 0.3705 0.3377
## 25 25 2 0.6176 0.8807 0.9455
## 26 26 2 0.1887 0.3912 0.6995
## 27 27 2 0.8309 0.5944 0.3186
## 28 28 2 0.9316 0.9880 0.8970
## 29 29 2 0.8823 0.9135 0.6901
## 30 30 2 0.9903 0.4593 0.2122
## 31 31 2 0.5563 0.7444 0.6211
## 32 32 2 0.2245 0.0001 0.0000
## 33 33 2 0.1335 0.2428 0.4463
## 34 34 2 0.5069 0.5350 0.3679
## 35 35 2 0.0138 0.0482 0.9686
## 36 36 2 0.7194 0.8807 0.7236
## 37 37 2 0.8068 0.9214 0.7473
## 38 38 2 0.1434 0.2160 0.3364
## 39 39 3 0.8380 0.9265 0.7391
## 40 40 3 0.1360 0.2886 0.6084
## 41 41 3 0.0046 0.0055 0.1243
## 42 42 3 0.9052 0.9743 0.8456
plot(dif)
Only two items have DIF and they look pretty similar across the two groups te be honest.
I’ve turned off the code I’ll median-split the sample by age, and look for DIF there
Every single item has Dif:
dif
## Call:
## lordif(resp.data = Complex_short2, group = Complex_with_predictors$age_y,
## criterion = "Chisqr", alpha = 0.01)
##
## Number of DIF groups: 2
##
## Number of items flagged for DIF: 42 of 42
##
## Items flagged: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42
##
## Number of iterations for purification: 9 of 10
##
## Detection criterion: Chisqr
##
## Threshold: alpha = 0.01
##
## item ncat chi12 chi13 chi23
## 1 1 3 0.0000 0.0000 0.0000
## 2 2 2 0.0000 0.0000 0.0000
## 3 3 2 0.0000 0.0000 0.0000
## 4 4 2 0.0000 0.0000 0.0000
## 5 5 2 0.0000 0.0000 0.0000
## 6 6 2 0.0000 0.0000 0.0000
## 7 7 2 0.0000 0.0000 0.0000
## 8 8 2 0.0000 0.0000 0.0000
## 9 9 2 0.0000 0.0000 0.0000
## 10 10 2 0.0000 0.0000 0.0000
## 11 11 2 0.0000 0.0000 0.0000
## 12 12 2 0.0000 0.0000 0.0000
## 13 13 2 0.0091 0.0007 0.0055
## 14 14 2 0.0000 0.0000 0.0000
## 15 15 2 0.0000 0.0000 0.0000
## 16 16 2 0.0009 0.0009 0.0791
## 17 17 2 0.0043 0.0160 0.7610
## 18 18 2 0.0000 0.0000 0.0000
## 19 19 2 0.0000 0.0000 0.0000
## 20 20 2 0.0000 0.0000 0.0000
## 21 21 2 0.0000 0.0000 0.0000
## 22 22 2 0.0000 0.0000 0.0000
## 23 23 2 0.0000 0.0000 0.0008
## 24 24 2 0.0000 0.0000 0.0002
## 25 25 2 0.0000 0.0000 0.0000
## 26 26 2 0.0000 0.0000 0.4918
## 27 27 2 0.0000 0.0000 0.0001
## 28 28 2 0.0000 0.0000 0.0242
## 29 29 2 0.0000 0.0000 0.0006
## 30 30 2 0.0000 0.0000 0.0000
## 31 31 2 0.0000 0.0000 0.0003
## 32 32 2 0.0000 0.0000 0.0000
## 33 33 2 0.0000 0.0000 0.0000
## 34 34 2 0.0000 0.0000 0.0000
## 35 35 2 0.0064 0.0001 0.0011
## 36 36 2 0.0000 0.0000 0.0002
## 37 37 2 0.0000 0.0000 0.0000
## 38 38 2 0.0000 0.0000 0.0001
## 39 39 3 0.0000 0.0000 0.0000
## 40 40 3 0.0000 0.0000 0.0000
## 41 41 3 0.0000 0.0000 0.0000
## 42 42 3 0.0000 0.0000 0.0000
I can’t get plots I had above, because every item has dif – the factor scores can’t be equated in any reasonable way.
Plot the distribution of scores by age group to see what’s going on.
Complex_with_predictors %>%
dplyr::select(starts_with(c("morphology", "syntax", "combine")) , c("word_endings686", "word_endings687", "word_endings688", "word_endings689", "age_y")) %>%
mutate(
mean = rowSums(.[1:42])
) %>%
ggplot(aes(y=mean, x=as.factor(age_y))) + geom_boxplot()
There are a lot of 0s in the low vocabulary group. I suspect this makes the IRT model difficult to estimate for them, leading to the DIF issues aboe.
by_Participant <-Complex_short_with_ids %>%
mutate(
Theta = fscores(m1, method="MAP")[,1],
Raw = rowSums(.[2:38])
) %>%
dplyr::select(data_id, Raw, Theta) %>%
full_join(Complex) %>%
group_by(data_id) %>%
slice(1)
## Joining, by = "data_id"
theta_by_age <- by_Participant %>%
ggplot(aes(x=age, y=Theta)) + geom_point() + stat_smooth(method="loess")
raw_by_age <- by_Participant %>%
ggplot(aes(x=age, y=Raw)) + geom_point() + stat_smooth(method="loess")
theta_by_age | raw_by_age
## `geom_smooth()` using formula 'y ~ x'
## Warning: Removed 1429 rows containing non-finite values (stat_smooth).
## Warning: Removed 1429 rows containing missing values (geom_point).
## `geom_smooth()` using formula 'y ~ x'
## Warning: Removed 1429 rows containing non-finite values (stat_smooth).
## Warning: Removed 1429 rows containing missing values (geom_point).
theta_by_prod <- by_Participant %>%
ggplot(aes(x=production, y=Theta)) + geom_point() + stat_smooth(method="loess")
raw_by_prod <- by_Participant %>%
ggplot(aes(x=production, y=Raw)) + geom_point() + stat_smooth(method="loess")
theta_by_prod | raw_by_prod
## `geom_smooth()` using formula 'y ~ x'
## Warning: Removed 1429 rows containing non-finite values (stat_smooth).
## Warning: Removed 1429 rows containing missing values (geom_point).
## `geom_smooth()` using formula 'y ~ x'
## Warning: Removed 1429 rows containing non-finite values (stat_smooth).
## Warning: Removed 1429 rows containing missing values (geom_point).
sessionInfo()
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19042)
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=Dutch_Netherlands.1252 LC_CTYPE=Dutch_Netherlands.1252
## [3] LC_MONETARY=Dutch_Netherlands.1252 LC_NUMERIC=C
## [5] LC_TIME=Dutch_Netherlands.1252
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] lordif_0.3-3 rms_6.2-0 SparseM_1.81 Hmisc_4.5-0
## [5] Formula_1.2-4 survival_3.1-12 GGally_2.1.1 patchwork_1.1.1
## [9] knitr_1.33 Gifi_0.3-9 psych_2.1.3 ltm_1.1-1
## [13] polycor_0.7-10 msm_1.6.8 MASS_7.3-51.6 mirt_1.33.2
## [17] lattice_0.20-41 forcats_0.5.1 stringr_1.4.0 dplyr_1.0.5
## [21] purrr_0.3.4 readr_1.4.0 tidyr_1.1.3 tibble_3.1.1
## [25] ggplot2_3.3.3 tidyverse_1.3.1 wordbankr_0.3.1
##
## loaded via a namespace (and not attached):
## [1] TH.data_1.0-10 colorspace_2.0-0 ellipsis_0.3.1
## [4] htmlTable_2.2.1 base64enc_0.1-3 fs_1.5.0
## [7] rstudioapi_0.13 farver_2.1.0 Deriv_4.1.3
## [10] MatrixModels_0.5-0 fansi_0.4.2 mvtnorm_1.1-1
## [13] lubridate_1.7.10 xml2_1.3.2 codetools_0.2-16
## [16] splines_4.0.2 mnormt_2.0.2 jsonlite_1.7.2
## [19] broom_0.7.6 cluster_2.1.0 dbplyr_2.1.1
## [22] png_0.1-7 compiler_4.0.2 httr_1.4.2
## [25] backports_1.2.1 assertthat_0.2.1 Matrix_1.2-18
## [28] cli_2.5.0 htmltools_0.5.1.1 quantreg_5.85
## [31] tools_4.0.2 gtable_0.3.0 glue_1.4.2
## [34] Rcpp_1.0.6 cellranger_1.1.0 jquerylib_0.1.4
## [37] vctrs_0.3.7 nlme_3.1-148 conquer_1.0.2
## [40] xfun_0.22 rvest_1.0.0 lifecycle_1.0.0
## [43] dcurver_0.9.2 polspline_1.1.19 zoo_1.8-9
## [46] scales_1.1.1 hms_1.0.0 sandwich_3.0-0
## [49] parallel_4.0.2 expm_0.999-6 RColorBrewer_1.1-2
## [52] yaml_2.2.1 gridExtra_2.3 sass_0.3.1
## [55] rpart_4.1-15 reshape_0.8.8 latticeExtra_0.6-29
## [58] stringi_1.5.3 highr_0.9 RMySQL_0.10.21
## [61] checkmate_2.0.0 permute_0.9-5 rlang_0.4.11
## [64] pkgconfig_2.0.3 matrixStats_0.58.0 evaluate_0.14
## [67] labeling_0.4.2 htmlwidgets_1.5.3 quantregGrowth_1.2-1
## [70] tidyselect_1.1.1 plyr_1.8.6 magrittr_2.0.1
## [73] R6_2.5.0 generics_0.1.0 multcomp_1.4-17
## [76] DBI_1.1.1 pillar_1.6.0 haven_2.4.1
## [79] foreign_0.8-81 withr_2.4.2 mgcv_1.8-36
## [82] nnet_7.3-14 modelr_0.1.8 crayon_1.4.1
## [85] utf8_1.2.1 tmvnsim_1.0-2 rmarkdown_2.7
## [88] jpeg_0.1-8.1 grid_4.0.2 readxl_1.3.1
## [91] data.table_1.14.0 vegan_2.5-7 reprex_2.0.0
## [94] digest_0.6.27 GPArotation_2014.11-1 munsell_0.5.0
## [97] bslib_0.2.4