Yasuharu Okamoto, 2017.7
Analysis of Binary Items by Item Response Theory (IRT)
A Stan script with Python
When is an binary item (e.g. Yes/No), we have
When is a polytomous item, visit this website.
For the binary item , set the following logistic model.
This logistic model can be written in Stan as follows
====================== irt_bin.stan ====================================
data {
int Npsn;
//
Number of persons
int Nitm;
//
Number of items
int Ntot;
//
Number of data, Ntot = Nprn
* Nitm
int<lower = 1, upper =
Npsn> IDpsn[Ntot]; // Person ID
int<lower = 1, upper =
Nitm> IDitm[Ntot]; // Item ID
int<lower = 1, upper =
2> Res[Ntot];
//
Response-> 1: No,
2: Yes
}
parameters {
real<lower = 0.0>
a[Nitm];
real
b[Nitm];
real
F[Npsn];
}
transformed parameters {
vector[2] p[Ntot];
for (i in 1:Ntot){
p[i][2] = inv_logit(1.7 * a[IDitm[i]] * (F[IDpsn[i]] - b[IDitm[i]]));
p[i][1] = 1.0 - p[i][2];
}
}
model {
for (i in 1:Npsn)
F[i]
~ normal(0.0, 1.0);
for (i in 1:Ntot)
Res[i] ~ categorical(p[i]);
}
==============================================================
A Python script with the above Stan script was developed as follows. The script files and a sample data file were archived into a file PrgIRTbin.zip, which can be down loaded by clicking the file name PrgIRTbin.zip.
=================== main.py ==========================
import pystan
def calc_mean_med( d
):
d.sort()
med = d[len(d) // 2]
sum = 0
for v in d:
sum
+= v
mean = sum / len(d)
return mean, med
#
# Prepare
the input data file
#
fn_in =
input("Input data file = ")
f_in = open(fn_in,
"r")
#
# Set the
contents of the input data file in the object data_f
#
data_f =
f_in.readlines()
f_in.close()
#
# Prepare
the output file
#
fn_out =
input("Output data file = ")
f_out = open(fn_out,
"w")
f_out.write("Input
data file = " + fn_in + "\n\n")
pos = 0
while True:
if len(data_f[pos]) > 0:
if
data_f[pos][0] == "/":
break
pos += 1
pos += 1
N = int(data_f[pos])
pos += 1
M = int(data_f[pos])
print('N = ', N,
' M = ', M)
X = []
for i in range(N):
X.append([])
pos += 1
temp_strs =
data_f[pos].split()
for j in range(M):
X[i].append(int(temp_strs[j + 1]))
IDpsn = []
IDitm = []
Res = []
for i in range(N):
for j in range(M):
IDpsn.append(i + 1)
IDitm.append(j + 1)
Res.append(X[i][j] + 1)
f_out.write('\nData...\n')
for i in range(N):
f_out.write('{0:>5}:
'.format(i+1))
for j in range(M):
f_out.write(' {0}'.format(X[i][j]))
f_out.write('\n')
Data = {'Npsn': N,
'Nitm': M, 'Ntot': N * M, 'IDpsn': IDpsn, 'IDitm': IDitm, 'Res': Res}
fit =
pystan.stan(file = 'irt_bin.stan', data = Data,
seed = 999, pars = ["a", "b"], n_jobs = 1)
print(fit)
a = fit['a']
b = fit['b']
print('len(a) = ',
len(a))
print('len(b) = ',
len(b))
a_smpls = []
b_smpls = []
for i in range(M):
a_smpls.append([])
b_smpls.append([])
for v in a:
for i in range(M):
a_smpls[i].append(v[i])
for v in b:
for i in range(M):
b_smpls[i].append(v[i])
print('\nlen(a) = ',
len(a), ' len(a_smpls) = ',
len(a_smpls),
' len(a_smpls[0] = ',
len(a_smpls[0]))
print('\nlen(b) = ',
len(b), ' len(b_smpls) = ',
len(b_smpls),
' len(b_smpls[0] = ',
len(b_smpls[0]))
f_out.write('\n')
for i in range(M):
mean_a, med_a =
calc_mean_med(a_smpls[i])
f_out.write('a[{0}] =
{1:<.5}(mean)
{2:<.5}(med)\n'.
format(i+1, mean_a, med_a))
f_out.write('\n')
for i in range(M):
mean_b, med_b =
calc_mean_med(b_smpls[i])
f_out.write('b[{0}] =
{1:<.5}(mean)
{2:<.5}(med)\n'.
format(i+1, mean_b, med_b))
f_out.close()
=========================================================
Run this script, then file names of input and output data are asked (Figure 1).
Figure 1
The file name of output data is an arbitrary text file name. The input data file should be prepared according to the format shown in Figure 2.
・
・
・
Figure 2
On the next line to the one with slash / at the head, the number of data (persons) is written (“300” in Figure 2). On the next line to this line, the number of items is written (“10” in Figure 2). Then, the responses of persons are written, one person per line, in the way that data ID is set first, followed by the responses of a person. Data ID and responses are separated by one or more than one space characters.
After the file name of output data is set, calculation starts (Figure 3).
Figure 3
The numbers of persons and items are displayed, then sampling by Stan starts. Figure 4 shows that Stan took about 1 minute to sample in a chain for this condition. Total time of sampling is the sum of these times for chains when the chains are run sequentially.
Figure 4
When the program ends, statistics by Stan is displayed (Figure 5).
Figure 5
Parts of the content of the output file are shown in Figures 6 and 7.
Figure 6
The name of the input data file is printed out, then the data read in are printed out, so that you can check whether the intended data are read in correctly or not. Following the read in data, the point estimates of parameters, means and medians, are printed out (Figure 7).
Figure 7