Up

Bayesian Analysis for Triangular Test

Grid method for d

 

In triangular test, three stimuli are presented, two of them, A an A, are the same one, and the observer is required to choose the third one B, which is difficult to discriminate from A or A.

The sensations of A, A and B are represented by random variables , , and , and assumed to have the following distributions, respectively.

The correct response is made, when the following conditions are satisfied (Frijters et al., 1980, p. 177):

Set d as follows

We get the following equation (Frijters et al., 1980, eq.(1)):

where  denotes the probability of choosing the stimulus B.

Now, the probability of k correct responses for the total number of trials N is given by the equation (2):

The script based on the above model is shown in Listing 1.

The script file is archived in the fileTriTestScriptFile.zip, which can be downloaded and used freely on the responsibility of the user. All rights are reserved.

Run the script, values for N and k are required to be set as follows:

 

>>>

= RESTART: D:\yasuharu\XXXXX\TriangularTest_dprime_Grid\TriTestScriptFile\TriangularTest_dprime_grid.py

N = 50

k = 20

 

Set the values, then calculation starts.

After the calculation ends, the results are shown as the graph like Figure 1.

Figure 1

 

In Figure 1, the posterior distribution is shown with the mode and 95% HDI.

The graph is saved automatically by the code:

 

plt.savefig(f'FigN{N}k{k}.png')

 

In the case of Figure 1, the file name is FigN50k20.png.

 

 

Reference

Frijters, J. E. R., Kooistra, A., & Vereijken, P. F. G. (1980). Tables of d' for the triangular method and the 3-AFC signal detection procedure. Perception & Psychophysics, 1980, 27, 176-178.

 

 

Listing 1.  Script for the Bayesian analysis of triangular test

"""

             Yasuharu Okamoto, 2021.11, 2022.01

"""

import numpy as np

import scipy.stats as ss

import scipy.integrate as si

import matplotlib.pyplot as plt

import seaborn as sb

 

N = int(input('N = '))

k = int(input('k = '))

 

class Triangular:

    def __init__(self, d_prime):

        self.d_prime = d_prime

       

    def func(self, u):

        v = ss.norm.cdf(-u * (3.0**0.5) + self.d_prime * ((2/3)**0.5)) + \

                ss.norm.cdf(-u * (3.0**0.5) - self.d_prime * ((2/3)**0.5))

        return v * np.exp(-0.5 * (u**2)) / ((2.0 * np.pi)**0.5)

   

def Pc_d_prime(d_prime):

    d_func = Triangular(d_prime).func

    return 2 * si.quad(d_func, 0.0, +np.inf)[0]

   

dprimes = np.linspace(0, 10, 1001)

Pc = np.empty(len(dprimes))

for i in range(len(dprimes)):

    if i % 100 == 0:

        print('{0:}/{1:}     '.format(i, len(dprimes)))  #, end = '\r')

    Pc[i] = Pc_d_prime(dprimes[i])

   

postPdprime = np.empty(len(dprimes))

for i in range(len(dprimes)):

    postPdprime[i] = (Pc[i] ** k) * ((1 - Pc[i]) ** (N - k))

 

postPdprime = (postPdprime / np.sum(postPdprime))

 

map_idx = np.argmax(postPdprime) 

MAP_Est = map_idx / 100      

print('MAP_Est =', MAP_Est)

 

postPcum = np.cumsum(postPdprime)

 

a = 0.05

Lp = 0

Rp = 0 

w_t = 1000

L_t = Lp

R_t = Rp

while True:

    if Lp == 0:

        cumPL = 0.0

    else:

        cumPL = postPcum[Lp-1]

    while 1.0 - postPcum[Rp] >= a - cumPL:

        Rp += 1

    if Lp == 0:

        L_t = Lp

        R_t = Rp

        w_t = R_t - L_t       

    elif Rp - Lp < w_t:

        L_t = Lp

        R_t = Rp

        w_t = R_t - L_t

    Lp += 1

    if postPcum[Lp] >= a:

        Lp -= 1

        break

 

Lp = 1000

Rp = 1000

w_t = 1000

L_t1 = Lp

R_t1 = Rp

while True:

    if Rp == 1000:

        cumPR = 0.0

    else:

        cumPR = 1.0 - postPcum[Rp]

    while postPcum[Lp] >= a - cumPR:

        if Lp > 0:

            Lp -= 1

        else:

            break

    if Rp == 1000:

        L_t1 = Lp

        R_t1 = Rp

        w_t = R_t1 - L_t1

    elif Rp - Lp < w_t:

        L_t1 = Lp

        R_t1 = Rp

        w_t = R_t1 - L_t1

    Rp -= 1

    if 1.0 - postPcum[Rp] >= a:

        Rp += 1

        break

       

HDI_L = ((L_t + L_t1) / 2.0) / 100.0

HDI_R = ((R_t + R_t1) / 2.0) / 100.0

 

plt.plot(np.linspace(0, 10, 1001), postPdprime)

plt.plot([MAP_Est,MAP_Est], [0, postPdprime[map_idx]], label = 'Mode = {}'.format(MAP_Est))

plt.title(f"Psterior Distribution for d'\nN = {N}, k = {k}", fontsize = 14)

plt.xlabel("d'", fontsize = 12)

plt.plot([0, 10], [0, 0], lw = 1, c = 'k')

plt.yticks([0])

plt.plot([HDI_L, HDI_R], [0, 0], lw = 5, label = '95% HDI = [{0:.2f}, {1:.2f}]'.format(HDI_L, HDI_R))

plt.legend()

plt.savefig(f'FigN{N}k{k}.png')

plt.show()

 

 

Up