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

 

 

Home