Yasuharu Okamoto, 2017.07

Bayesian Analysis for Psychometric Functions

2AFC tasks with rating by two, three, or four categories

That three- or four-category rating method is preferable to the two-category method in constant stimuli was shown by simulation of Okamoto (2017).

An observer is usually asked to compare two stimuli, the standard stimulus and a comparison stimulus, and to respond by choosing one of the two response categories, e.g. The left stimulus is brighter than the right one and The right stimulus is brighter than the left one. However, in some cases, the observer feels difficulty to decide. Klein (2001) proposes to use more than two response categories. This website provides Stan scripts for 2AFC task with more than two rating categories, in addition to a Stan script for usual 2AFC task with binary judgment. First, discuss the usual 2AFC with binary response categories, then the one with three rating categories, and the one with four rating categories.

 

Two Alternative Forced Choice Task with Two Response Categories

 

A comparison stimulus  is compared with the standard stimulus , and judgment Which is stronger? is required. The probability that  is judged to be stronger than  is denoted by . In a two-alternative procedure, an observer usually chooses one of two judgments,  ( is stronger than ) and  ( is weaker than ), so the probability  is given by

 can be considered to be a function of  and is called a psychometric function (PF). A psychometric function is well approximated by a cumulative normal distribution (phi-gamma hypothesis; see Luce and Galanter, 1963). Hence, set

where  is the cumulative standard normal distribution.

The point of subjective equality PSE is given by

Just noticeable difference JND is given by

where discrimination probability 0.75 is chosen for JND.  satisfies the equation

Denote the comparison stimulus and its judgment in trial  by  and , where

Then the probability of the data is given by

where

Based on probability (1.2), the posterior distributions are given.

 

A Stan script for the above model is this.

 

================  pf2cat.stan  ===============================

data {

    int N;

    real St[N];

    int<lower = 1, upper = 2> Res[N];

}

 

parameters {

        real mu;

        real<lower = 0.0> sigma;

}

 

transformed parameters {

    vector[2] p[N];

 

    for (i in 1:N){

        p[i][2] = Phi((St[i] - mu) / sigma);

        p[i][1] = 1.0 - p[i][2];

    }

}

 

model {

    for (i in 1:N)

        Res[i] ~ categorical(p[i]);

}

==========================================================

 

The above Stan script is used by the following Python script. The scripts files and a sample data file are archived into the file PF2Cat.zip, which can be down loaded by clicking the file name PF2Cat.zip.

 

=================  main.py  ================================

from subprg import *

import pystan

from pystan import StanModel

import pickle

 

 

X, n, meanSt, sgmSt, fout, nm_fout = InputData()

 

N = n

St = []

Res = []

for v in X:

    St.append(v[0])

    Res.append(v[1])

 

print('\nSt = \n', St)

print('\nRes = \n', Res)

 

Data = {'N': N, 'St': St, 'Res': Res}

 

minSt = St[0]

maxSt = St[0]

for i in range(N):

    if minSt > St[i]:

        minst = St[i]

    if maxSt < St[i]:

        maxSt = St[i]

   

def f_init():

    return dict(mu =  (minSt + maxSt) / 2.0, sigma = (maxSt - minSt) / 2.0)

 

fit = pystan.stan(file = 'pf2cat.stan', data = Data, seed = 999, chains =4,

                      iter = 5000, warmup = 2000,

                      init = f_init, pars = ['mu', 'sigma'], n_jobs = 1)

 

print(fit)

 

mrg_mu = fit['mu']

mrg_sgm = fit['sigma']

   

mrg_mu.sort()

mrg_sgm.sort()

 

NSimu = len(mrg_mu)

print('\nNSimu = ', NSimu)

DispResults( NSimu, X, n, mrg_mu, mrg_sgm, fout)

 

fout.close()

print("\nOutput file {} was saved.".format(nm_fout))

=======================================================

 

Run the program main.py, then the name of the input data file is asked (Figure 1.1).

Figure 1.1

 

The format of the data file is shown in Figure 1.2.

Figure 1.2

 

The data are written after the line with slash / at the head. Each line of the data includes a set of dataID, (denoted by St), and  in each trial. DataID is simply any string and used to identify the individual data, but is not used in analysis. After the last data, a line with slash / at the head is put to indicate the end of the data.

After the input data file name is set and the Enter key is pressed down, then an output text file name is asked. Set a text file name, then calculation starts.

When calculation by Stan ends, a window, in which the PF is drawn, is displayed (Figure 1.3).

Figure 1.3

 

Small green circles represent data points s. To avoid overlapping of circles, small random fluctuations are added to get the drawn points s, where

 is a small random fluctuation.

After closing the window in Figure 1.3, the next window, in which a histogram of samples for PSE () is drawn, will be displayed (Figure 1.4).

Figure 1.4

 

After the window in Figure 1.4 is closed, the window, in which a histogram for JND is drawn, is displayed (Figure 1.5).

 

Figure 1.5

 

Close the window in Figure 1.5, then the program ends (Figure 1.6).

 

Figure 1.6

 

After the program ends, the output file can be opened by an editor. Figure 1.7 shows the contents of the output file.

Figure 1.7

 

At the end of the file, medians, quartile points, and 95% CIs are written.

 

 

Two Alternative Forced Choice Task with Three Rating Categories

 

A task with three rating categories allows dont know or indifferent judgment as an additional one to the binary decision judgment (Böckenholt, 2001; Kaernbach, 2001). Hence, the three categories of judgments are as follows

: which means that  is judged to be weaker than

: which means that  is judged to equal

: which means that  is judged to be stronger than

Combine the two categories  and  into one category, which is denoted by .

Set the following two PFs

Then we have

and

JND is given by

Assuming that C1 and C2 are at an equal distance from PSE, we have

Denote the comparison stimulus and its judgment in trial  by  and , where

Then the probability of the data is given by

where

 

A Stan script for model (2.2) is this.

 

=================  pf3cat.stan  ============================

data {

    int N;

    real St[N];

    int<lower = 1, upper = 3> Res[N];

}

 

parameters {

        ordered[2] C;

        real<lower = 0.0> sigma;

}

 

transformed parameters {

    vector[3] p[N];

    real pse;

 

    pse = (C[1] + C[2]) / 2.0;

 

    for (i in 1:N){

        p[i][3] = Phi((St[i] - C[2]) / sigma);

        p[i][2] = Phi((St[i] - C[1]) / sigma) - Phi((St[i] - C[2]) / sigma);

        p[i][1] = 1.0 -Phi((St[i] - C[1]) / sigma);

    }

}

 

model {

    for (i in 1:N)

        Res[i] ~ categorical(p[i]);

}

==================================================

 

The above Stan script was used by the following Python script. The script files were archived into the file PF3Cat.zip, which can be downloaded by clicking the file name PF3Cat.zip.

 

=================  main.py  ========================

import matplotlib.pyplot as plt

import numpy as np

import math

from RNGen import *

from subprg import *

import pystan

 

 

X, n, meanSt, sgmSt, fout, nm_fout = InputData()

 

N = n

St = []

Res = []

for v in X:

    St.append(v[0])

    Res.append(v[1])

 

print('\nSt = ', St)

print('\nRes = ', Res)

 

Data = {'N': N, 'St': St, 'Res': Res}

 

minSt = St[0]

maxSt = St[0]

for v in St:

    if minSt > v:

        minSt = v

    if maxSt < v:

        maxSt = v

 

def i_func():

    return dict(C = [(3.0 * minSt + 2.0 * maxSt) / 5.0,

                     (2.0 * minSt + 3.0 * maxSt) / 5.0],

                sigma = (maxSt - minSt) / 2.0)

 

fit = pystan.stan(file = 'pf3cat.stan', data = Data, seed = 999,

                  iter = 5000, warmup = 2000, pars = ['C', 'pse', 'sigma'],

                   init = i_func, n_jobs = 1)

                     

print(fit)

 

mrg_sgm = fit['sigma']

mrg_C = fit['C']

mrg_C1 = []

mrg_C2 = []

for v in mrg_C:

    mrg_C1.append(v[0])

    mrg_C2.append(v[1])

       

mrg_pse = fit['pse']

   

mrg_C1.sort()

mrg_C2.sort()

mrg_pse.sort()

mrg_sgm.sort()

 

NSimu = len(mrg_sgm)

 

n = N

DispResults( NSimu, X, n, mrg_C1, mrg_C2, mrg_pse, mrg_sgm, fout )

 

fout.close()

print("\nOutput file {} was saved.".format(nm_fout))

   

 

 

====================================================

 

Run this Python script, then the name of the input data file is asked (Figure 2.1).

Figure 2.1

 

The format of the data file is shown in Figure 2.2.

Figure 2.2

 

The data are written after the line with slash / at the head. Each line of the data includes a set of dataID, S_comp (denoted by St), and Res in each trial. DataID is simply any string and used to identify the set, but is not used in analysis. After the last data, a line with slash / at the head is put to indicate the end of the data.

After the input data file name is set and the Enter key is pressed down, then an output text file name is asked. Set a text file name, then calculation starts.

When calculation by Stan ends, a window, in which the three PFs are drawn, is displayed (Figure 2.3).

Figure 2.3

 

Two PFs denoted by PF1 and PF2 are corresponding functions to C1 and C2. The PF denoted by pse is the one, which is estimated by the value PSE given by Assumption (2.1).

Small green circles represent data points s. To avoid overlapping of circles, small random fluctuations are added to get the drawn points s, where

 is a small random fluctuation.

After closing the window in Figure 2.3, the next window, in which frequency polygons of samples for C1, pse and C2 are drawn, will be displayed (Figure 2.4).

Figure 2.4

 

After the window in Figure 2.4 is closed, the window, in which a histogram for JND is drawn, is displayed (Figure 2.5).

Figure 2.5

 

Close the window in Figure 2.5, then the program ends.

After the program ends, the output file can be opened by an editor. Figure 2.6 shows the contents of the output file.

Figure 2.6

 

At the end of the file, medians, quartile points, and 95% CIs for C1, pse, C2 and JND are written.

 

 

Two Alternative Forced Choice Task with Four Rating Categories

 

A 2AFC task with four rating categories allows four response categories:

: which means that  is judged to be weaker than

: which means that  is probably weaker than

: which means that  is probably stronger than

: which means that  is judged to be stronger than

Combine the three response categories , , and  into one category, which is denoted by .

Combine the two categories  and  into one category, which is denoted by , and the two categories  and  into one category, which is denoted by .

Then, set the following three PFs

We have the following probabilities

PSE is given by

and JND is given by

Denote the comparison stimulus and its judgment in trial  by  and , where

Then the probability of the data is given by

where

 

A Stan script for model (3.1) is this.

 

===================  pf4cat.stan  ==========================

data {

    int N;

    real St[N];

    int<lower = 1, upper = 4> Res[N];

}

 

parameters {

        ordered[3] C;

        real<lower = 0.0> sigma;

}

 

transformed parameters {

    vector[4] p[N];

 

    for (i in 1:N){

        p[i][4] = Phi((St[i] - C[3]) / sigma);

        p[i][3] = Phi((St[i] - C[2]) / sigma) - Phi((St[i] - C[3]) / sigma);

        p[i][2] = Phi((St[i] - C[1]) / sigma) - Phi((St[i] - C[2]) / sigma);

        p[i][1] = 1.0 -Phi((St[i] - C[1]) / sigma);

    }

}

 

model {

    for (i in 1:N)

        Res[i] ~ categorical(p[i]);

}

=====================================================

 

A Python script using the above Stan script is as follows. The script files were archived into the file PF4Cat.zip, which can be down loaded by clicking the file name PF4Cat.zip.

 

=====================================================

from subprgs4Cat import *

import pystan

 

 

X, n, meanSt, sgmSt, fout, nm_fout = InputData()

 

N = n

St = []

Res = []

for v in X:

    St.append(v[0])

    Res.append(v[1])

 

print('\nSt = \n', St)

print('\nRes = \n', Res)

 

Data = {'N': N, 'St': St, 'Res': Res}

 

minSt = St[0]

maxSt = St[0]

for v in St:

    if minSt > v:

        minSt = v

    if maxSt < v:

        maxSt = v

 

def i_func():

    return dict(C = [(3.0 * minSt + 2.0 * maxSt) / 5.0,

                     (minSt + maxSt) / 2.0,

                     (2.0 * minSt + 3.0 * maxSt) / 5.0],

                    sigma = (maxSt - minSt) / 2.0)

 

 

fit = pystan.stan(file = 'pf4cat.stan', data = Data, init = i_func,

                   iter = 5000, warmup = 2000, pars = ['C', 'sigma'], n_jobs = 1)

                   

 

                      

print(fit)

 

mrg_sgm = fit['sigma']

mrg_C = fit['C']

mrg_C1 = []

mrg_Mu = []

mrg_C2 = []

for v in mrg_C:

    mrg_C1.append(v[0])

    mrg_Mu.append(v[1])

    mrg_C2.append(v[2])

 

mrg_sgm.sort()

mrg_C1.sort()

mrg_Mu.sort()

mrg_C2.sort()

NSimu = len(mrg_sgm)

n = N

   

DispResults( NSimu, X, n, mrg_C1, mrg_Mu, mrg_C2, mrg_sgm, fout )

 

fout.close()

print("\nOutput file {} was saved.".format(nm_fout))

====================================================

 

Run the above Python script, then the name of the input data file is asked (Figure 3.1).

Figure 3.1

 

The format of the data file is shown in Figure 3.2.

Figure 3.2

 

The data are written after the line with slash / at the head. Each line of the data includes a set of dataID, S_comp (denoted by St), and Res in each trial. DataID is simply any string and used to identify the set, but is not used in analysis. After the last data, a line with slash / at the head is put to indicate the end of the data.

After the input data file name is set and the Enter key is pressed down, then an output text file name is asked. Set a text file name, then calculation starts.

When calculation ends, a window, in which the three PFs are drawn, is displayed (Figure 3.3).

Figure 3.3

 

Small green circles represent data points s. To avoid overlapping of circles, small random fluctuations are added to get the drawn points s, where

 is a small random fluctuation.

After closing the window in Figure 3.3, the next window, in which frequency polygons of samples for C1, PSE, and C2 are drawn, will be displayed (Figure 3.4).

Figure 3.4

 

After the window in Figure 3.4 is closed, the window, in which a histogram for JND is drawn, is displayed (Figure 3.5).

Figure 3.5

 

Close the window in Figure 3.5, then the program ends.

After the program ends, the output file can be opened by an editor. Figure 3.6 shows the contents of the output file.

Figure 3.6

 

At the end of the file, medians, quartile points, and 95% CIs for C1, PSE, C2 and JND are written.

 

 

Reference

Böckenholt,U. (2001). Threshold and intransitivities in pairwise judgments: A multilevel analysis. Journal of Educational and Behavioral Sciences, 26, 269-282.

Kaernbach, C. (2001). Adaptive threshold estimation with unforced-choice tasks. Perception & Psychophysics, 63, 1377-1388.

Klein, S. A. (2001). Measuring, estimating, and understanding the psychometric function: A commentary. Perception & Psychophysics, 63, 1421-1455.

Luce, R. D. & Galanter, E. (1963). Discrimination. In P. Suppes, J. L. Zinnes, R. R. Bush, E. Galanter, R.D. Luce, W. J. McGill, A. Newell, and H. A. Simon (Eds.), Handbook of Mathematical Psychology, Vol. I (pp. 191-243). New York: John Wiley and Sons, Inc.

 

 

Back