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
| # Introduced |
|---|
1 |
12cc7127cb73 |
* Example code using OpenIRT |
2 |
12cc7127cb73 |
|
3 |
12cc7127cb73 |
* Install: |
4 |
3e5f3f33ee42 |
cap program drop openirt |
5 |
eaff035c689f |
net install http://www.people.fas.harvard.edu/~tzajonc/stata/openirt/openirt, force |
6 |
3e5f3f33ee42 |
|
7 |
3e5f3f33ee42 |
clear |
8 |
3e5f3f33ee42 |
set mem 50m |
9 |
3e5f3f33ee42 |
|
10 |
3e5f3f33ee42 |
* Save 12 NAEP items locally |
11 |
3e5f3f33ee42 |
sysuse naep_items, clear |
12 |
3e5f3f33ee42 |
save naep_items, replace |
13 |
3e5f3f33ee42 |
count |
14 |
3e5f3f33ee42 |
|
15 |
12cc7127cb73 |
* Easier to mix and match with c=0 for 2pl |
16 |
12cc7127cb73 |
recode c (.=0) |
17 |
12cc7127cb73 |
|
18 |
12cc7127cb73 |
** DEMO 1: Item characteristic curves |
19 |
12cc7127cb73 |
|
20 |
12cc7127cb73 |
* 2PL ICC (row 3) |
21 |
12cc7127cb73 |
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)), /// |
22 |
12cc7127cb73 |
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve") |
23 |
12cc7127cb73 |
|
24 |
12cc7127cb73 |
* overlay two ICCs |
25 |
12cc7127cb73 |
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)) /// |
26 |
12cc7127cb73 |
(function 1/(1+exp(-1.7*a[4]*(x-b[4]))), range(-4 4)), /// |
27 |
12cc7127cb73 |
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve") /// |
28 |
12cc7127cb73 |
legend(order(1 "Item 16" 2 "Item 17")) |
29 |
12cc7127cb73 |
|
30 |
12cc7127cb73 |
* 2pl and 3PL curve |
31 |
12cc7127cb73 |
twoway (function 1/(1+exp(-1.7*a[3]*(x-b[3]))), range(-4 4)) /// |
32 |
12cc7127cb73 |
(function c[1] + (1-c[1])/(1+exp(-1.7*a[1]*(x-b[1]))), range(-4 4)), /// |
33 |
12cc7127cb73 |
xtitle("Theta") ytitle("P(X=1|Theta)") title("Item Characteristic Curve") /// |
34 |
12cc7127cb73 |
legend(order(1 "Item 16" 2 "Item 12")) |
35 |
12cc7127cb73 |
|
36 |
12cc7127cb73 |
** Demo 2: Item information curves |
37 |
12cc7127cb73 |
|
38 |
12cc7127cb73 |
* 2PL IIC |
39 |
12cc7127cb73 |
local i = 3 |
40 |
12cc7127cb73 |
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'])))))" |
41 |
12cc7127cb73 |
twoway (function `iic2pl', range(-4 4)), /// |
42 |
12cc7127cb73 |
xtitle("Theta") ytitle("I(Theta)") title("Item Information Curve") |
43 |
12cc7127cb73 |
|
44 |
12cc7127cb73 |
* 3PL IIC |
45 |
12cc7127cb73 |
local i = 2 |
46 |
12cc7127cb73 |
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))" |
47 |
12cc7127cb73 |
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2" |
48 |
12cc7127cb73 |
twoway (function `iic3pl', range(-4 4)), /// |
49 |
12cc7127cb73 |
xtitle("Theta") ytitle("I(Theta)") title("Item Information Curve") |
50 |
12cc7127cb73 |
|
51 |
12cc7127cb73 |
* Multiple graphs |
52 |
12cc7127cb73 |
forvalues i = 1/9 { |
53 |
12cc7127cb73 |
local title = "Item " + string(id[`i']) |
54 |
12cc7127cb73 |
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))" |
55 |
12cc7127cb73 |
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2" |
56 |
12cc7127cb73 |
twoway (function `iic3pl', range(-5 5)), /// |
57 |
12cc7127cb73 |
xtitle("Theta") ytitle("I(Theta)") title("`title'") |
58 |
12cc7127cb73 |
graph save tmp`i', replace |
59 |
12cc7127cb73 |
} |
60 |
12cc7127cb73 |
graph combine tmp1.gph tmp2.gph tmp3.gph tmp4.gph tmp5.gph tmp6.gph tmp7.gph tmp8.gph tmp9.gph, ycommon xcommon |
61 |
12cc7127cb73 |
|
62 |
12cc7127cb73 |
* Test information |
63 |
12cc7127cb73 |
replace c = 0 if c==. |
64 |
12cc7127cb73 |
local tif "0" |
65 |
12cc7127cb73 |
forvalues i = 1/12 { |
66 |
12cc7127cb73 |
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))" |
67 |
12cc7127cb73 |
local iic3pl "((1.7*a[`i'])^2*(1-`p')/`p')*((`p'-c[`i'])/(1-c[`i']))^2" |
68 |
12cc7127cb73 |
local tif "`tif'+`iic3pl'" |
69 |
12cc7127cb73 |
} |
70 |
12cc7127cb73 |
local se "1/sqrt(`tif')" |
71 |
12cc7127cb73 |
twoway (function `tif', range(-4 4)) /// |
72 |
12cc7127cb73 |
(function `se', range(-4 4) yaxis(2)), /// |
73 |
12cc7127cb73 |
xtitle("Theta") ytitle("I(Theta)") title("Test Information") ytitle("Standard Error", axis(2)) /// |
74 |
12cc7127cb73 |
legend(order(1 "Test Information" 2 "Expected Standard Error")) |
75 |
12cc7127cb73 |
|
76 |
12cc7127cb73 |
** Demo 3: Test characteristic curve |
77 |
12cc7127cb73 |
replace c = 0 if c==. |
78 |
12cc7127cb73 |
local tcc "0" |
79 |
12cc7127cb73 |
forvalues i = 1/12 { |
80 |
12cc7127cb73 |
local p "(c[`i']+(1-c[`i'])/(1+exp(-1.7*a[`i']*(x-b[`i']))))" |
81 |
12cc7127cb73 |
local tcc "`tcc'+`p'/12" |
82 |
12cc7127cb73 |
} |
83 |
12cc7127cb73 |
twoway (function `tcc', range(-4 4)), /// |
84 |
12cc7127cb73 |
xtitle("Theta") ytitle("Percent Correct") title("Test Characteristic Curve") |
85 |
12cc7127cb73 |
|
86 |
12cc7127cb73 |
** DEMO 4: Estimating IRT models using OpenIRT |
87 |
12cc7127cb73 |
|
88 |
3e5f3f33ee42 |
* load response data |
89 |
12cc7127cb73 |
sysuse naep_children, clear |
90 |
3e5f3f33ee42 |
|
91 |
3e5f3f33ee42 |
* Preliminary exploration: percent correct score |
92 |
3e5f3f33ee42 |
egen percent_correct = rowmean(item*) |
93 |
3e5f3f33ee42 |
kdensity percent_correct, xtitle("Percent Correct") title(NAEP Percent Correct Score Distribution) |
94 |
3e5f3f33ee42 |
|
95 |
3e5f3f33ee42 |
* Often useful to check that items are positive correlated |
96 |
3e5f3f33ee42 |
* with percent correct score |
97 |
3e5f3f33ee42 |
corr percent_correct item* |
98 |
3e5f3f33ee42 |
|
99 |
12cc7127cb73 |
* Read help... |
100 |
12cc7127cb73 |
help openirt |
101 |
12cc7127cb73 |
|
102 |
12cc7127cb73 |
* Example 1: Estimate both item parameters and ability for single test |
103 |
bf363d47e970 |
openirt, id(id) item_prefix(item) save_item_parameters("items.dta") save_trait_parameters("traits.dta") |
104 |
bf363d47e970 |
|
105 |
3e5f3f33ee42 |
* Merge in ability estimates |
106 |
3e5f3f33ee42 |
merge id using traits, sort |
107 |
3e5f3f33ee42 |
|
108 |
3e5f3f33ee42 |
* Compare distributions using different estimates |
109 |
3e5f3f33ee42 |
|
110 |
3e5f3f33ee42 |
* Multiple plausible values can be combined to form better |
111 |
3e5f3f33ee42 |
* kdensity estimate. Using one plausible value works better though too. |
112 |
3e5f3f33ee42 |
kdensity(theta_pv1), bw(.4) gen(x1 d1) |
113 |
3e5f3f33ee42 |
kdensity(theta_pv2), bw(.4) gen(x2 d2) at(x1) |
114 |
3e5f3f33ee42 |
kdensity(theta_pv3), bw(.4) gen(x3 d3) at(x1) |
115 |
3e5f3f33ee42 |
kdensity(theta_pv4), bw(.4) gen(x4 d4) at(x1) |
116 |
3e5f3f33ee42 |
kdensity(theta_pv5), bw(.4) gen(x5 d5) at(x1) |
117 |
3e5f3f33ee42 |
egen d = rowmean(d*) |
118 |
3e5f3f33ee42 |
line(d x1) |
119 |
3e5f3f33ee42 |
|
120 |
3e5f3f33ee42 |
twoway (kdensity theta, bw(.4)) (kdensity theta_eap, bw(.4)) /// |
121 |
3e5f3f33ee42 |
(line d x1) (kdensity theta_mle, bw(.4)), /// |
122 |
3e5f3f33ee42 |
xtitle("Theta") title("True Theta vs EAP, PV, MLE Estimates") /// |
123 |
3e5f3f33ee42 |
legend(order(1 "True" 2 "EAP" 3 "PV" 4 "MLE")) |
124 |
3e5f3f33ee42 |
|
125 |
3e5f3f33ee42 |
drop x* d* |
126 |
3e5f3f33ee42 |
|
127 |
3e5f3f33ee42 |
* QQ plot distribution comparisons |
128 |
3e5f3f33ee42 |
qqplot(theta_eap theta), xtitle("Theta (True)") ytitle("Theta (EAP)") title("") saving(tmp1.gph, replace) |
129 |
3e5f3f33ee42 |
qqplot(theta_mle theta), xtitle("Theta (True)") ytitle("Theta (MLE)") title("") saving(tmp2.gph, replace) |
130 |
3e5f3f33ee42 |
qqplot(theta_pv1 theta), xtitle("Theta (True)") ytitle("Theta (PV1)") title("") saving(tmp3.gph, replace) |
131 |
3e5f3f33ee42 |
qqplot(theta_pv2 theta), xtitle("Theta (True)") ytitle("Theta (PV2)") title("") saving(tmp4.gph, replace) |
132 |
3e5f3f33ee42 |
graph combine tmp1.gph tmp2.gph tmp3.gph tmp4.gph |
133 |
3e5f3f33ee42 |
|
134 |
3e5f3f33ee42 |
* Graph TRUE vs EAP |
135 |
3e5f3f33ee42 |
twoway (scatter theta_eap theta) (function y=x, range(-3 3)), /// |
136 |
3e5f3f33ee42 |
xtitle("Theta (True)") ytitle("Theta (EAP)") title("") /// |
137 |
3e5f3f33ee42 |
text(3 3 "y = x", place(e)) legend(off) |
138 |
3e5f3f33ee42 |
|
139 |
3e5f3f33ee42 |
* Graph TRUE vs MLE |
140 |
3e5f3f33ee42 |
twoway (scatter theta_mle theta) (function y=x, range(-3 3)), /// |
141 |
3e5f3f33ee42 |
xtitle("Theta (True)") ytitle("Theta (MLE)") title("") /// |
142 |
3e5f3f33ee42 |
text(3 3 "y = x", place(e)) legend(off) |
143 |
3e5f3f33ee42 |
|
144 |
3e5f3f33ee42 |
* Item comparisons |
145 |
3e5f3f33ee42 |
* Load true parameters, for reference. |
146 |
3e5f3f33ee42 |
sysuse naep_items, clear |
147 |
3e5f3f33ee42 |
merge id using items, sort |
148 |
3e5f3f33ee42 |
|
149 |
3e5f3f33ee42 |
twoway (scatter a_eap a) (function y = x, range(0 1.5)), xtitle(Discrimination (True)) ytitle(Discrimination (EAP)) /// |
150 |
3e5f3f33ee42 |
xscale(range(0 1.5)) /// |
151 |
3e5f3f33ee42 |
yscale(range(1 1.5)) |
152 |
3e5f3f33ee42 |
|
153 |
3e5f3f33ee42 |
twoway (scatter b_eap b) (function y = x, range(-2 2)), xtitle(Difficulty (True)) ytitle(Difficulty (EAP)) /// |
154 |
3e5f3f33ee42 |
xscale(range(-2 2)) /// |
155 |
3e5f3f33ee42 |
yscale(range(-2 2)) |
156 |
3e5f3f33ee42 |
|
157 |
3e5f3f33ee42 |
twoway (scatter c_eap c) (function y = x, range(0 .6)), xtitle(Guessing (True)) ytitle(Guessing (EAP)) /// |
158 |
3e5f3f33ee42 |
xscale(range(0 .6)) /// |
159 |
3e5f3f33ee42 |
yscale(range(0 .6)) |
160 |
3e5f3f33ee42 |
|
161 |
12cc7127cb73 |
* Example 2: Link two test forms |
162 |
12cc7127cb73 |
sysuse naep_children, clear |
163 |
12cc7127cb73 |
recode item1-item6 (0/1=.) if _n <= 250 |
164 |
12cc7127cb73 |
recode item7-item13 (0/1=.) if _n >250 |
165 |
12cc7127cb73 |
|
166 |
12cc7127cb73 |
openirt, id(id) item_prefix(item) save_item_parameters("items.dta") save_trait_parameters("traits.dta") |
167 |
12cc7127cb73 |
|
168 |
12cc7127cb73 |
* Merge in ability estimates |
169 |
12cc7127cb73 |
merge id using traits, sort |
170 |
12cc7127cb73 |
|
171 |
12cc7127cb73 |
* Graph TRUE vs EAP |
172 |
12cc7127cb73 |
twoway (scatter theta_eap theta) (function y=x, range(-3 3)), /// |
173 |
12cc7127cb73 |
xtitle("Theta (True)") ytitle("Theta (EAP)") title("") /// |
174 |
12cc7127cb73 |
text(3 3 "y = x", place(e)) legend(off) |
175 |
12cc7127cb73 |
|
176 |
12cc7127cb73 |
* Graph TRUE vs MLE |
177 |
12cc7127cb73 |
twoway (scatter theta_mle theta) (function y=x, range(-3 3)), /// |
178 |
12cc7127cb73 |
xtitle("Theta (True)") ytitle("Theta (MLE)") title("") /// |
179 |
12cc7127cb73 |
text(3 3 "y = x", place(e)) legend(off) |
180 |
12cc7127cb73 |
|
181 |
12cc7127cb73 |
* Example 3: Fixed item parameters |
182 |
12cc7127cb73 |
sysuse naep_items, clear |
183 |
12cc7127cb73 |
save fixed_items, replace |
184 |
12cc7127cb73 |
|
185 |
12cc7127cb73 |
sysuse naep_children, clear |
186 |
12cc7127cb73 |
openirt, id(id) save_item_parameters("items.dta") save_trait_parameters("traits.dta") /// |
187 |
12cc7127cb73 |
fixed_item_file("fixed_items.dta") item_prefix(item) |
188 |
12cc7127cb73 |
|
189 |
12cc7127cb73 |
* Merge in ability estimates |
190 |
12cc7127cb73 |
merge id using traits, sort |
191 |
12cc7127cb73 |
|
192 |
12cc7127cb73 |
* Graph TRUE vs EAP |
193 |
12cc7127cb73 |
twoway (scatter theta_eap theta) (function y=x, range(-3 3)), /// |
194 |
12cc7127cb73 |
xtitle("Theta (True)") ytitle("Theta (EAP)") title("") /// |
195 |
12cc7127cb73 |
text(3 3 "y = x", place(e)) legend(off) |
196 |
12cc7127cb73 |
|
197 |
12cc7127cb73 |
* Graph TRUE vs MLE |
198 |
12cc7127cb73 |
twoway (scatter theta_mle theta) (function y=x, range(-3 3)), /// |
199 |
12cc7127cb73 |
xtitle("Theta (True)") ytitle("Theta (MLE)") title("") /// |
200 |
12cc7127cb73 |
text(3 3 "y = x", place(e)) legend(off) |
