tristanz / OpenIRT (http://people.fas.harvard.edu/~tzajonc/openirt.html)
Bayesian and Maximum Likelihood Estimation of Item Response Theory (IRT) Models
Clone this repository (size: 589.9 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/tristanz/openirt
| commit 37: | 0d32afb3521b |
| parent 36: | 4e35255dd116 |
| branch: | default |
| tags: | tip |
Fixed 10.5 OSX compile issue
| r37:0d32afb3521b | 194 loc | 6.7 KB | embed / history / annotate / raw / |
|---|
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | * Example code using OpenIRT
* Install:
cap program drop openirt
net install http://www.people.fas.harvard.edu/~tzajonc/stata/openirt/openirt, force
clear
set mem 50m
* Save 12 NAEP items locally
sysuse naep_items, clear
save naep_items, replace
count
* Easier to mix and match with c=0 for 2PL
recode c (.=0)
** DEMO 1: Item characteristic curves
* 2PL ICC (row 3)
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)), ///
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve")
* overlay two ICCs
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)) ///
(function 1/(1+exp(-1.7*a[4]*(x-b[4]))), range(-4 4)), ///
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve") ///
legend(order(1 "Item 16" 2 "Item 17"))
* 2pl and 3PL curve
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)) ///
(function c[1] + (1-c[1])/(1+exp(-1.7*a[1]*(x-b[1]))), range(-4 4)), ///
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve") ///
legend(order(1 "Item 16" 2 "Item 12"))
** Demo 2: Item information curves
* 2PL IIC
local i = 3
local iic2pl "(1.7*a[`i'])^2*(1/(1+exp(-1.7*a[`i']*(x-b[`i']))))*(1-(1/(1+exp(-1.7*a[`i']*(x-b[`i'])))))"
twoway (function `iic2pl', range(-4 4)), ///
xtitle("Theta") ytitle("I(Theta)") title("Item Information Curve")
* 3PL IIC
local i = 2
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))"
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2"
twoway (function `iic3pl', range(-4 4)), ///
xtitle("Theta") ytitle("I(Theta)") title("Item Information Curve")
* Multiple graphs
forvalues i = 1/9 {
local title = "Item " + string(id[`i'])
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))"
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2"
twoway (function `iic3pl', range(-5 5)), ///
xtitle("Theta") ytitle("I(Theta)") title("`title'")
graph save tmp`i', replace
}
graph combine tmp1.gph tmp2.gph tmp3.gph tmp4.gph tmp5.gph tmp6.gph tmp7.gph tmp8.gph tmp9.gph, ycommon xcommon
* Test information
replace c = 0 if c==.
local tif "0"
forvalues i = 1/12 {
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))"
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2"
local tif "`tif'+`iic3pl'"
}
local se "1/sqrt(`tif')"
twoway (function `tif', range(-4 4)) ///
(function `se', range(-4 4) yaxis(2)), ///
xtitle("Theta") ytitle("I(Theta)") title("Test Information") ytitle("Standard Error", axis(2)) ///
legend(order(1 "Test Information" 2 "Expected Standard Error"))
** Demo 3: Test characteristic curve
replace c = 0 if c==.
local tcc "0"
forvalues i = 1/12 {
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))"
local tcc "`tcc'+`p'/12"
}
twoway (function `tcc', range(-4 4)), ///
xtitle("Theta") ytitle("Percent Correct") title("Test Characteristic Curve")
** DEMO 4: Estimating IRT models using OpenIRT
* load response data
sysuse naep_children, clear
* Preliminary exploration: percent correct score
egen percent_correct = rowmean(item*)
kdensity percent_correct, xtitle("Percent Correct") title(NAEP Percent Correct Score Distribution)
* Often useful to check that items are positive correlated
* with percent correct score
corr percent_correct item*
* Read help...
help openirt
* Example 1: Estimate both item parameters and ability for single test
openirt, id(id) item_prefix(item) save_item_parameters("items.dta") save_trait_parameters("traits.dta")
* Merge in ability estimates
merge id using traits, sort
* Compare distributions using different estimates
* Multiple plausible values can be combined to form better
* kdensity estimate. Using one plausible value works better though too.
kdensity(theta_pv1), bw(.4) gen(x1 d1)
kdensity(theta_pv2), bw(.4) gen(x2 d2) at(x1)
kdensity(theta_pv3), bw(.4) gen(x3 d3) at(x1)
kdensity(theta_pv4), bw(.4) gen(x4 d4) at(x1)
kdensity(theta_pv5), bw(.4) gen(x5 d5) at(x1)
egen d = rowmean(d*)
line(d x1)
twoway (kdensity theta, bw(.4)) (kdensity theta_eap, bw(.4)) ///
(line d x1) (kdensity theta_mle, bw(.4)), ///
xtitle("Theta") title("True Theta vs EAP, PV, MLE Estimates") ///
legend(order(1 "True" 2 "EAP" 3 "PV" 4 "MLE"))
drop x* d*
* QQ plot distribution comparisons
qqplot(theta_eap theta), xtitle("Theta (True)") ytitle("Theta (EAP)") title("") saving(tmp1.gph, replace)
qqplot(theta_mle theta), xtitle("Theta (True)") ytitle("Theta (MLE)") title("") saving(tmp2.gph, replace)
qqplot(theta_pv1 theta), xtitle("Theta (True)") ytitle("Theta (PV1)") title("") saving(tmp3.gph, replace)
qqplot(theta_pv2 theta), xtitle("Theta (True)") ytitle("Theta (PV2)") title("") saving(tmp4.gph, replace)
graph combine tmp1.gph tmp2.gph tmp3.gph tmp4.gph
* Graph TRUE vs EAP
twoway (scatter theta_eap theta) (function y=x, range(-3 3)), ///
xtitle("Theta (True)") ytitle("Theta (EAP)") title("") ///
text(3 3 "y = x", place(e)) legend(off)
* Graph TRUE vs MLE
twoway (scatter theta_mle theta) (function y=x, range(-3 3)), ///
xtitle("Theta (True)") ytitle("Theta (MLE)") title("") ///
text(3 3 "y = x", place(e)) legend(off)
* Example 2: Link two test forms
sysuse naep_children, clear
* Simulate two test forms by setting some responses to missing
* Must leave some common items.
recode item1-item6 (0/1=.) if _n <= 250
recode item7-item13 (0/1=.) if _n >250
* Estimate
openirt, id(id) item_prefix(item) save_item_parameters("items.dta") save_trait_parameters("traits.dta")
* Merge in ability estimates
merge id using traits, sort
* Graph TRUE vs EAP
twoway (scatter theta_eap theta) (function y=x, range(-3 3)), ///
xtitle("Theta (True)") ytitle("Theta (EAP)") title("") ///
text(3 3 "y = x", place(e)) legend(off)
* Graph TRUE vs MLE
twoway (scatter theta_mle theta) (function y=x, range(-3 3)), ///
xtitle("Theta (True)") ytitle("Theta (MLE)") title("") ///
text(3 3 "y = x", place(e)) legend(off)
* Example 3: Link to TIMSS using test formed from TIMSS item bank.
sysuse timss_items, clear
save fixed_items, replace
sysuse timss_children, clear
openirt, id(id) save_item_parameters("items.dta") save_trait_parameters("traits.dta") ///
fixed_item_file("fixed_items.dta") item_prefix(q)
* load results
use traits, clear
* place on TIMSS scale (mu=500 sd=100), see TIMSS 1999.
foreach x of varlist theta_eap theta_mle theta_pv1 theta_pv2 theta_pv3 theta_pv4 theta_pv5 {
replace `x' = `x'*100 + 500
}
kdensity(theta_pv1), bw(15) gen(x1 d1)
kdensity(theta_pv2), bw(15) gen(x2 d2) at(x1)
kdensity(theta_pv3), bw(15) gen(x3 d3) at(x1)
kdensity(theta_pv4), bw(15) gen(x4 d4) at(x1)
kdensity(theta_pv5), bw(15) gen(x5 d5) at(x1)
egen d = rowmean(d*)
line(d x1)
twoway (kdensity theta_eap, bw(20)) ///
(line d x1) (kdensity theta_mle, bw(20)), ///
xtitle("Theta") title("EAP, PV, MLE Estimates") ///
legend(order(1 "EAP" 2 "PV" 3 "MLE"))
drop x* d*
|
