Up

カテゴリ尺度法/評定尺度法

岡本安晴、2021.09

 

刺激の感覚の強さを順序カテゴリで答え、それに基づいて尺度を構成する方法は、カテゴリ尺度法(Gescheider, 1997, p. 214)、評定尺度法(難波・桑野、1998, pp. 72-86)、あるいは継次カテゴリ法/系列カテゴリ法(method of successive categories)(岡本、2006pp. 27-36)、継次間隔法(method of successive intervals)(中谷、1969, p. 162)と呼ばれている。カテゴリ尺度法のデータは、刺激の感覚量とカテゴリ境界の関係にThurstone1927)の判断の基本モデルを適用したカテゴリ判断の法則(The law of categorical judgment; Torgerson, 1958p. 205)を用いて分析することができる。

提示された刺激の感覚の強さをK個のカテゴリから選んで答えるものとする。刺激の引き起こす感覚量を確率変数で表し、平均、標準偏差の正規分布に従うものとする。すなわち、

である。

感覚量の連続体上におけるK個のカテゴリの境界をで、評価カテゴリをで表すとき、

 のとき 

とする。

このとき、刺激の感覚がカテゴリであると判断される確率は、

である。ここで、は累積標準正規分布関数である。

感覚連続体の原点と単位を決めるために、カテゴリ境界の両端を0と1に設定する。

、 

感覚の強さの範囲(ダイナミックレンジ)が刺激の種類に依らずほぼ同じであるという仮定(Teghtsoonian, 1971)に従えば、この制約条件は自然なものである。

感覚の標準偏差については、以下の3つの場合を想定した。

 

F条件: 標準偏差に特に制約条件を設定しない。

 

L条件: 標準偏差に制約条件

を設定する。これはStevens1975, p. 235)がEkmanの法則と呼んでいるものを踏まえたものである。

 

C条件: 標準偏差は全て等しく

とする場合。これは、Fechnerの考え方に対応するものである。

 

上記3条件に対応してPythonプログラムを3通り作成した。これらのプログラムとデータ例のファイルは圧縮ファイルCategoryScalesFiles.zipにまとめた。

3つのプログラムの入力データの形式はすべて同じである。例を図1に示す。

第1行目は、変数名である。2行目からデータを、1列目に提示刺激、2列目から各評価カテゴリの頻度を入力する。

図1

 

刺激は、上の行から順番に強くなるように並べる。社会的・文化的刺激(絵画など)のように、強度の順序が不明の場合は、カテゴリに仮の数値、例えば図1の例では、左から1,2,3,4,5,6,7と数値を与えて、刺激ごとに平均値あるいは中央値を求め、それに基づいて刺激の強さの順序を推測することができる。カテゴリの数値の平均値でも大まかな刺激の数量化が出来るが、さらに本ウェブサイトの分析法を用いれば、計量心理学のモデルに基づいたより精緻な分析が可能である。社会的・文化的刺激をカテゴリ数値の平均値で順序付けたときは、図1の第1欄の刺激値には例えば順番に1から整数値を当てはめておけばよい。この数値はグラフを描くときに用いられるが、分析の結果には影響しない。

図1のように作成したデータを、CSV形式で保存する。CSV形式のファイルは、Excelの場合、保存ファイル名のファイル拡張子として.csv選ぶとCSV形式で保存される。

 

以下に、F条件L条件C条件のプログラムについて順番に説明する。

 

F条件

 

標準偏差に制約条件を置かないときのベイズ分析のStanスクリプトをリストF.1に示すように用意した。

リストF1Stanスクリプトを利用するPythonスクリプトはリストF.2のように用意した。

リストF.2のプログラムを以下のように実行すると、入力データファイル名の設定が求められる。

 

(py39) PS D:\SigmaFree> python .\CategoryScaleFree.py

INFO:numexpr.utils:NumExpr defaulting to 8 threads.

Input file (*.csv) = DataL.csv

 

入力データファイル名(上の場合は、DataL.csv)を設定してEnterキーを押すと計算が始まる。StanスクリプトによるMCMCサンプリングが終了すると、図F.1のように、パラメータの事後分布が順番に表示される。

F.1

 

F.1の3つのグラフの提示後(フォームの右上角のX印をクリックすればフォームが閉じられる)、しばらくの計算の後、次の図F.2のグラフが表示される。

F.2

 

F.2のグラフの提示後、しばらくの計算の後、WAICの値(WAIC2)が次のように表示されて、プログラムの実行終了となる。

 

WAIC2 = 175.73879512540347

(py39) PS D:\SigmaFree>

 

F.1および図F.2のグラフはそれぞれファイル(拡張子.png)として保存されている。計算結果もResults.txtに保存されている。

上の分析の場合、Results.txtの内容は以下のようである。

 

Input Data File = DataL.csv

 

Data:

         1   63   37    0    0    0    0    0

     1.668   33   65    2    0    0    0    0

     2.783   18   67   15    0    0    0    0

     4.642    4   53   40    3    0    0    0

     7.743    3   28   56   13    0    0    0

    12.915    1   13   45   30   11    0    0

    21.544    0    3   21   34   33    8    1

    35.938    0    0    8   20   36   25   11

    59.948    0    0    1   15   21   32   31

       100    0    0    0    2    7   26   65

 

       Stimulus       Mode Psi     Mode Sigma

           1.00          -0.02           0.08

           1.67           0.04           0.09

           2.78           0.10           0.11

           4.64           0.20           0.12

           7.74           0.29           0.14

          12.91           0.41           0.17

          21.54           0.59           0.18

          35.94           0.75           0.21

          59.95           0.89           0.23

         100.00           1.09           0.21

 

 

WAIC = 175.739

 

 

L条件

 

標準偏差に線形制約条件を置くときのベイズ分析のStanスクリプトをリストL.1に示すように用意した。

リストL.1Stanスクリプトを利用するPythonスクリプトはリストL.2のように用意した。

リストL.2のプログラムを以下のように実行すると、入力データファイル名の設定が求められる。

 

(py39) PS D:\SigmaLinear> python .\CategoryScaleL.py

INFO:numexpr.utils:NumExpr defaulting to 8 threads.

Input file (*.csv) = DataL.csv

 

入力データファイル名(上の場合は、DataL.csv)を設定してEnterキーを押すと計算が始まる。StanスクリプトによるMCMCサンプリングが終了すると、図L.1のように、パラメータの事後分布が順番に表示される。

 

L.1

 

L.1の3つのグラフの提示後、しばらくの計算の後、次の図L.2のグラフが表示される。

L.2

 

L.2のグラフの提示後、しばらくの計算の後、WAICの値(WAIC2)が次のように表示されて、プログラムの実行終了となる。

 

WAIC2 = 165.93447882890743

(py39) PS D:\SigmaLinear>

 

L.1および図L.2のグラフは、それぞれファイル(拡張子.png)として保存されている。計算結果もResults.txtに保存されている。

上の分析の場合、Results.txtの内容は以下のようである。

 

Input Data File = DataL.csv

 

Data:

         1   63   37    0    0    0    0    0

     1.668   33   65    2    0    0    0    0

     2.783   18   67   15    0    0    0    0

     4.642    4   53   40    3    0    0    0

     7.743    3   28   56   13    0    0    0

    12.915    1   13   45   30   11    0    0

    21.544    0    3   21   34   33    8    1

    35.938    0    0    8   20   36   25   11

    59.948    0    0    1   15   21   32   31

       100    0    0    0    2    7   26   65

 

       Stimulus       Mode Psi     Mode Sigma

           1.00          -0.03           0.09

           1.67           0.04           0.10

           2.78           0.11           0.11

           4.64           0.20           0.12

           7.74           0.28           0.14

          12.91           0.41           0.16

          21.54           0.58           0.18

          35.94           0.75           0.21

          59.95           0.88           0.23

         100.00           1.11           0.26

 

 

WAIC = 165.934

 

 

C条件

 

標準偏差に等分散の制約条件を置くときのベイズ分析のStanスクリプトをリストC.1に示すように用意した。

リストC.1Stanスクリプトを利用するPythonスクリプトはリストC.2のように用意した。

リストC.2のプログラムを以下のように実行すると、入力データファイル名の設定が求められる。

 

(py39) PS D:\SigmaConst> python .\CategoryScaleC.py

INFO:numexpr.utils:NumExpr defaulting to 8 threads.

Input file (*.csv) = DataC.csv

 

入力データファイル名(上の場合は、DataC.csv)を設定してEnterキーを押すと計算が始まる。StanスクリプトによるMCMCサンプリングが終了すると、図C.1のように、パラメータの事後分布が順番に表示される。

C.1

 

C.1の3つのグラフの提示後、しばらくの計算の後、次の図C.2のグラフが表示される。

C.2

 

C.2のグラフの提示後、しばらくの計算の後、WAICの値(WAIC2)が次のように表示されて、プログラムの実行終了となる。

 

WAIC2 = 162.4332857781438

(py39) PS D:\SigmaConst>

 

C.1および図C.2のグラフは、それぞれファイル(拡張子.png)として保存されている。計算結果もResults.txtに保存されている。

上の分析の場合、Results.txtの内容は以下のようである。

 

Input Data File = DataC.csv

 

Data:

         1   51   36   13    0    0    0    0

     1.668   43   46   10    1    0    0    0

     2.783   23   48   27    2    0    0    0

     4.642   10   44   36   10    0    0    0

     7.743    1   21   54   22    2    0    0

    12.915    0   11   37   42    9    1    0

    21.544    0    0   23   49   25    3    0

    35.938    0    0    0   19   49   28    4

    59.948    0    0    0    1   32   37   30

       100    0    0    0    0    3   16   81

 

       Stimulus       Mode Psi

           1.00           0.00

           1.67           0.03

           2.78           0.11

           4.64           0.19

           7.74           0.31

          12.91           0.42

          21.54           0.53

          35.94           0.76

          59.95           0.92

         100.00           1.13

 

Sigma: mode =            0.15

 

 

WAIC = 162.433

 

 

 

Python用のStanPyStan)の説明を<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>で行っている。

 

 

参考文献

 

Gescheider, G. A. (1997). Psychophysics: The fundamentals, Third edition. Mahwah: Lawrence Erlbaum Associations, Publishers.

中谷和夫(1969).第5章 尺度構成法. 八木冕(監修)/田中良久(編)講座心理学2 計量心理学.東京大学出版会.

難波精一郎・桑野園子(1998).音の評価のための心理学的測定法.コロナ社.

岡本安晴(2006).計量心理学.培風館.

Stevens, S. S. (1975). Psychophysics: Introduction to its perceptual, neural, and social prospects. New York: Wiley.

Tesghtsoonian, R. (1971). On the exponents in Stevens law and the constant in Ekmans law. Psychological Review, 78, 71-80.

Thurstone, L. L. (1927). A law of comparative judgment. Psychological Review, 34, 273-286.

Torgerson, W. S. (1958). Theory and methods of scaling. New York: John Wiley & Sons, Inc.

 

 

リストF.1. 標準偏差に制約がないモデルのStanスクリプト

 

data {

    int NSt;

    int K;

    real Sts[NSt];

    int Rs[NSt, K];

}

transformed data {

    vector[NSt+1] a_Psis;

    vector[K-2] a_C;

   

    for (i in 1:(NSt+1)) {

        a_Psis[i] = 1.0;

    }

    for (i in 1:(K-2)) {

        a_C[i] = 1.0;

    }

}

parameters {

    real<lower = 0.0> sgms[NSt];

    simplex[NSt+1] prePsis;

    simplex[K-2] preC;

}

transformed parameters {

    real Psis[NSt];

    real C[K-1];

    simplex[K] theta[NSt];

    real v;

 

    v = 0.0;

    for (s in 1:NSt) {

        v = v + prePsis[s];

        Psis[s] = logit(v);

    }

   

    C[1] = 0.0;

    C[K-1] = 1.0;

    v = 0.0;

    for (k in 2:(K-2)) {

        v = v + preC[k-1];

        C[k] = C[1] + v;

    }

    for (s in 1:NSt) {

        theta[s][1] = normal_cdf(C[1], Psis[s], sgms[s]);

        theta[s][K] = 1.0 - normal_cdf(C[K-1], Psis[s], sgms[s]);

        for (k in 2:(K-1)) {

            theta[s][k] = normal_cdf(C[k], Psis[s], sgms[s]) -

                            normal_cdf(C[k-1], Psis[s], sgms[s]);

        }

    }

}

model {

    for (s in 1:NSt) {

        sgms[s] ~ exponential(0.001);

    }

    prePsis ~ dirichlet(a_Psis);

    preC ~ dirichlet(a_C);

    //a ~ exponential(0.001);

    //b ~ exponential(0.001);

    for (s in 1:NSt) {

        Rs[s] ~ multinomial(theta[s]);

    }

}

 

 

リストF.2. リストF.1を用いるPythonスクリプト

 

import csv

import numpy as np

import pystan

import matplotlib.pyplot as plt

import seaborn as sb

import scipy.stats as ss

import estmode as em

import CalcWAIC2 as cw

 

fout = open('Results.txt', 'w')

 

fin_nm = input('Input file (*.csv) = ')

with open(fin_nm, 'r') as f:

    Data_in = [v for v in csv.reader(f)]

 

fout.write('Input Data File = {}\n'.format(fin_nm))

 

NSt = len(Data_in) - 1

K = len(Data_in[0]) - 1

Sts = np.empty(NSt)

Rs = np.empty((NSt, K), dtype = 'int')

for j in range(NSt):

    Sts[j] = float(Data_in[j+1][0])

    for k in range(K):

        Rs[j][k] = int(Data_in[j+1][k+1])

 

fout.write('\nData:\n')

for j in range(NSt):

    print(f'{Sts[j]:>10g}', end = '')

    fout.write(f'{Sts[j]:>10g}')

    for k in range(K):

        print(f'{Rs[j][k]:>5d}', end = '')

        fout.write(f'{Rs[j][k]:>5d}')

    print()

    fout.write('\n')

 

sm = pystan.StanModel(file = 'RatingFree.stan')

 

Data = {'NSt':NSt, 'K':K, 'Sts':Sts, 'Rs':Rs}

 

fit = sm.sampling(data = Data, n_jobs = 1)

 

print(fit)

 

Psis = fit['Psis']

sgms = fit['sgms']

Cs = fit['C']

 

for i in range(NSt):

    sb.kdeplot(Psis.T[i], label = '$\psi_{}$'.format(i+1))

plt.title('Posterior Ditributions of $\psi_s$', fontsize = 20)

plt.yticks([])

plt.legend()

plt.tight_layout()

plt.savefig('FigPsiPost.png')

plt.show()

 

for i in range(NSt):

    sb.kdeplot(sgms.T[i], label = r'$\sigma_{}$'.format(i))

plt.title('Posterior Distributions of $\sigma_s$', fontsize = 20)

plt.legend()

plt.savefig('FigSgmPost.png')

plt.show()

 

plt.plot([0, 0], [0, 5], label = r'$C_0$')

for i in range(1, K-2):

    sb.kdeplot(Cs.T[i], label = r'$C_{}$'.format(i+1))

plt.plot([1,1], [0, 5], label = r'$C_{}$'.format(K-1))

plt.title('Posterior Distributions of Cs', fontsize = 20)

plt.yticks([])

plt.legend()

plt.savefig('FigCsPost.png')

plt.show()

 

v_psis = np.empty(NSt)

v_sgms = np.empty(NSt)

for i in range(NSt):

    v_psis[i] = em.EstMode(Psis.T[i])

    v_sgms[i] = em.EstMode(sgms.T[i])

 

fout.write('\n{0:>15s}{1:>15s}{2:>15s}\n'.format('Stimulus', 'Mode Psi', 'Mode Sigma'))

for vst, vpsi, vsgm in zip(Sts, v_psis, v_sgms):

    fout.write('{0:>15.2f}{1:>15.2f}{2:>15.2f}\n'.format(vst, vpsi, vsgm))

   

v_Cs = np.empty(K-1)

v_Cs[0] = 0.0

v_Cs[K-2] = 1.0

for k in range(1, K-2):

    v_Cs[k] = em.EstMode(Cs.T[k])

for k in range(K-1):

    plt.plot([Sts[0], Sts[-1]], [v_Cs[k], v_Cs[k]], color = 'y')

plt.plot([], 'y-', label = 'C')

plt.plot(Sts, v_psis, color = 'b', lw = 3, label = '$\psi$')

plt.plot(Sts, v_psis - v_sgms, color = 'g', linestyle = '--', lw = 1,label = '$\psi - \sigma$')

plt.plot(Sts, v_psis + v_sgms, color = 'g', linestyle = '--', lw = 1, label = '$\psi+ \sigma$')

   

plt.xlabel('Stimulus', fontsize = 16)

plt.ylabel('$\psi$', fontsize = 16)

plt.legend(loc = 'upper left')

plt.title('Relation of Stimulus and $\psi$', fontsize = 18)

plt.tight_layout()

plt.savefig('PsiSgmC.png')

plt.show()

 

Probs = np.empty((NSt, K))

for s in range(NSt):

    Probs[s][0] = ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[0])

    Probs[s][K-1] = 1.0 - ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[K-2])

    for k in range(1, K-1):

        Probs[s][k] = ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[k]) -\

                        ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[k-1])

           

print('Predicted Prob(Rating|Stimulus):')

for s in range(NSt):

    for k in range(K):

        print(f'  {Probs[s][k]:7.3f}', end = '')

    print()

   

Pred_Rs = np.empty((NSt, K))

N_Rs = np.sum(Rs, axis = 1)

for i in range(NSt):

    Pred_Rs[i] = N_Rs[i] * Probs[i]

   

print('Predicted Frequencies of Rating for Stimulus:')

for s in range(NSt):

    for k in range(K):

        print(f'  {Pred_Rs[s][k]:7.1f}', end = '')

    print()

 

cum_data_Rs = np.cumsum(Rs, axis = 1)

cum_pred_Rs = np.cumsum(Pred_Rs, axis = 1)

 

print(cum_data_Rs)

 

for s in range(NSt):

    if s == 0:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3, label = 'Data')

    else:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3 )

    if s == 0:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-', label = 'Model')

    else:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-')

 

plt.xticks(range(K), range(1, K+1))

plt.xlabel('Rating', fontsize = 14)

plt.ylabel('Cum. Prop.', fontsize = 14)

plt.legend()

plt.title('Data and Model Prediction', fontsize = 18)

plt.tight_layout()

plt.savefig('FigDataModel.png')

plt.show()

 

waic2 = cw.calcwaic2(Rs, fit['theta'])

print('WAIC2 =', waic2)

fout.write('\n\nWAIC = {0:.3f}\n'.format(waic2))

 

fout.close()

 

 

リストL.1. 標準偏差に線形制約を用いるモデルのStanスクリプト

 

data {

    int NSt;

    int K;

    real Sts[NSt];

    int Rs[NSt, K];

}

transformed data {

    vector[NSt+1] a_Psis;

    vector[K-2] a_C;

   

    for (i in 1:(NSt+1)) {

        a_Psis[i] = 1.0;

    }

    for (i in 1:(K-2)) {

        a_C[i] = 1.0;

    }

}

parameters {

    simplex[NSt+1] prePsis;

    simplex[K-2] preC;

    real<lower = 0.0> a;

    real<lower = 0.0> b;

}

transformed parameters {

    real Psis[NSt];

    real sgms[NSt];

    real C[K-1];

    simplex[K] theta[NSt];

    real v;

 

    v = 0.0;

    for (s in 1:NSt) {

        v = v + prePsis[s];

        Psis[s] = logit(v);

    }

   

    for (s in 1:NSt) {

        sgms[s] = a + b * (Psis[s] - Psis[1]);

    }

    C[1] = 0.0;

    C[K-1] = 1.0;

    v = 0.0;

    for (k in 2:(K-2)) {

        v = v + preC[k-1];

        C[k] = C[1] + v;

    }

    for (s in 1:NSt) {

        theta[s][1] = normal_cdf(C[1], Psis[s], sgms[s]);

        theta[s][K] = 1.0 - normal_cdf(C[K-1], Psis[s], sgms[s]);

        for (k in 2:(K-1)) {

            theta[s][k] = normal_cdf(C[k], Psis[s], sgms[s]) -

                            normal_cdf(C[k-1], Psis[s], sgms[s]);

        }

    }

}

model {

    prePsis ~ dirichlet(a_Psis);

    preC ~ dirichlet(a_C);

    a ~ exponential(0.001);

    b ~ exponential(0.001);

    for (s in 1:NSt) {

        Rs[s] ~ multinomial(theta[s]);

    }

}

 

 

リストL.2. リストL.2Stanスクリプトを用いるPythonスクリプト

 

import csv

import numpy as np

import pystan

import matplotlib.pyplot as plt

import seaborn as sb

import scipy.stats as ss

import estmode as em

import CalcWAIC2 as cw

 

fout = open('Results.txt', 'w')

 

fin_nm = input('Input file (*.csv) = ')

with open(fin_nm, 'r') as f:

    Data_in = [v for v in csv.reader(f)]

 

fout.write('Input Data File = {}\n'.format(fin_nm))

 

NSt = len(Data_in) - 1

K = len(Data_in[0]) - 1

Sts = np.empty(NSt)

Rs = np.empty((NSt, K), dtype = 'int')

for j in range(NSt):

    Sts[j] = float(Data_in[j+1][0])

    for k in range(K):

        Rs[j][k] = int(Data_in[j+1][k+1])

 

fout.write('\nData:\n')

for j in range(NSt):

    print(f'{Sts[j]:>10g}', end = '')

    fout.write(f'{Sts[j]:>10g}')

    for k in range(K):

        print(f'{Rs[j][k]:>5d}', end = '')

        fout.write(f'{Rs[j][k]:>5d}')

    print()

    fout.write('\n')

 

sm = pystan.StanModel(file = 'RatingEkman.stan')

 

Data = {'NSt':NSt, 'K':K, 'Sts':Sts, 'Rs':Rs}

 

fit = sm.sampling(data = Data, n_jobs = 1)

 

print(fit)

 

Psis = fit['Psis']

sgms = fit['sgms']

Cs = fit['C']

a = fit['a']

b = fit['b']

 

for i in range(NSt):

    sb.kdeplot(Psis.T[i], label = '$\psi_{}$'.format(i+1))

plt.title('Posterior Ditributions of $\psi_s$', fontsize = 20)

plt.yticks([])

plt.legend()

plt.tight_layout()

plt.savefig('FigPsiPost.png')

plt.show()

 

for i in range(NSt):

    sb.kdeplot(sgms.T[i], label = r'$\sigma_{}$'.format(i))

plt.title('Posterior Distributions of $\sigma_s$', fontsize = 20)

plt.legend()

plt.savefig('FigSgmPost.png')

plt.show()

 

plt.plot([0, 0], [0, 5], label = r'$C_0$')

for i in range(1, K-2):

    sb.kdeplot(Cs.T[i], label = r'$C_{}$'.format(i+1))

plt.plot([1,1], [0, 5], label = r'$C_{}$'.format(K-1))

plt.title('Posterior Distributions of Cs', fontsize = 20)

plt.yticks([])

plt.legend()

plt.savefig('FigCsPost.png')

plt.show()

 

mode_a = em.EstMode(a)

mode_b = em.EstMode(b)

 

plt.figure(figsize = (8,4))

plt.subplot(1,2,1)

sb.kdeplot(a)

plt.yticks([])

plt.xlabel('a', fontsize = 14)

plt.title('Posterior Distribution of a' +

          '\nmode = {0:.2f}'.format(mode_a),

          fontsize = 14)

 

plt.subplot(1,2,2)

sb.kdeplot(b)

plt.title('Posterior Distribtuion of b' +

          '\nmode = {0:.2f}'.format(mode_b), fontsize = 14)

plt.yticks([])

plt.xlabel('b', fontsize = 14)

plt.tight_layout()

plt.savefig('Fig_a_b_Post.png')

plt.show()

 

v_psis = np.empty(NSt)

v_sgms = np.empty(NSt)

for i in range(NSt):

    v_psis[i] = em.EstMode(Psis.T[i])

    v_sgms[i] = em.EstMode(sgms.T[i])

 

fout.write('\n{0:>15s}{1:>15s}{2:>15s}\n'.format('Stimulus', 'Mode Psi', 'Mode Sigma'))

for vst, vpsi, vsgm in zip(Sts, v_psis, v_sgms):

    fout.write('{0:>15.2f}{1:>15.2f}{2:>15.2f}\n'.format(vst, vpsi, vsgm))

   

v_Cs = np.empty(K-1)

v_Cs[0] = 0.0

v_Cs[K-2] = 1.0

for k in range(1, K-2):

    v_Cs[k] = em.EstMode(Cs.T[k])

for k in range(K-1):

    plt.plot([Sts[0], Sts[-1]], [v_Cs[k], v_Cs[k]], color = 'y')

plt.plot([], 'y-', label = 'C')

plt.plot(Sts, v_psis, color = 'b', lw = 3, label = '$\psi$')

plt.plot(Sts, v_psis - v_sgms, color = 'g', linestyle = '--', lw = 1,label = '$\psi - \sigma$')

plt.plot(Sts, v_psis + v_sgms, color = 'g', linestyle = '--', lw = 1, label = '$\psi+ \sigma$')

   

plt.xlabel('Stimulus', fontsize = 16)

plt.ylabel('$\psi$', fontsize = 16)

plt.legend(loc = 'upper left')

plt.title('Relation of Stimulus and $\psi$', fontsize = 18)

plt.tight_layout()

plt.savefig('PsiSgmC.png')

plt.show()

 

Probs = np.empty((NSt, K))

for s in range(NSt):

    Probs[s][0] = ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[0])

    Probs[s][K-1] = 1.0 - ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[K-2])

    for k in range(1, K-1):

        Probs[s][k] = ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[k]) -\

                        ss.norm(loc = v_psis[s], scale = v_sgms[s]).cdf(v_Cs[k-1])

           

print('Predicted Prob(Rating|Stimulus):')

for s in range(NSt):

    for k in range(K):

        print(f'  {Probs[s][k]:7.3f}', end = '')

    print()

   

Pred_Rs = np.empty((NSt, K))

N_Rs = np.sum(Rs, axis = 1)

for i in range(NSt):

    Pred_Rs[i] = N_Rs[i] * Probs[i]

   

print('Predicted Frequencies of Rating for Stimulus:')

for s in range(NSt):

    for k in range(K):

        print(f'  {Pred_Rs[s][k]:7.1f}', end = '')

    print()

 

cum_data_Rs = np.cumsum(Rs, axis = 1)

cum_pred_Rs = np.cumsum(Pred_Rs, axis = 1)

 

print(cum_data_Rs)

 

for s in range(NSt):

    if s == 0:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3, label = 'Data')

    else:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3 )

    if s == 0:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-', label = 'Model')

    else:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-')

 

plt.xticks(range(K), range(1, K+1))

plt.xlabel('Rating', fontsize = 14)

plt.ylabel('Cum. Prop.', fontsize = 14)

plt.legend()

plt.title('Data and Model Prediction', fontsize = 18)

plt.tight_layout()

plt.savefig('FigDataModel.png')

plt.show()

 

waic2 = cw.calcwaic2(Rs, fit['theta'])

print('WAIC2 =', waic2)

fout.write('\n\nWAIC = {0:.3f}\n'.format(waic2))

 

fout.close()

 

 

リストC.1. 標準偏差が等しい等分散モデルのStanスクリプト

 

data {

    int NSt;

    int K;

    real Sts[NSt];

    int Rs[NSt, K];

}

transformed data {

    vector[NSt+1] a_Psis;

    vector[K-2] a_C;

   

    for (i in 1:(NSt+1)) {

        a_Psis[i] = 1.0;

    }

    for (i in 1:(K-2)) {

        a_C[i] = 1.0;

    }

}

parameters {

    simplex[NSt+1] prePsis;

    simplex[K-2] preC;

    real<lower = 0.0> sgm;

}

transformed parameters {

    real Psis[NSt];

    real C[K-1];

    simplex[K] theta[NSt];

    real v;

 

    v = 0.0;

    for (s in 1:NSt) {

        v = v + prePsis[s];

        Psis[s] = logit(v);

    }

   

    C[1] = 0.0;

    C[K-1] = 1.0;

    v = 0.0;

    for (k in 2:(K-2)) {

        v = v + preC[k-1];

        C[k] = C[1] + v;

    }

    for (s in 1:NSt) {

        theta[s][1] = normal_cdf(C[1], Psis[s], sgm);

        theta[s][K] = 1.0 - normal_cdf(C[K-1], Psis[s], sgm);

        for (k in 2:(K-1)) {

            theta[s][k] = normal_cdf(C[k], Psis[s], sgm) -

                            normal_cdf(C[k-1], Psis[s], sgm);

        }

    }

}

model {

    prePsis ~ dirichlet(a_Psis);

    preC ~ dirichlet(a_C);

    sgm ~ exponential(0.001);

    for (s in 1:NSt) {

        Rs[s] ~ multinomial(theta[s]);

    }

}

 

 

リストC.2. リストC.1Stanスクリプトを用いるPythonスクリプト

 

import csv

import numpy as np

import pystan

import matplotlib.pyplot as plt

import seaborn as sb

import scipy.stats as ss

import estmode as em

import CalcWAIC2 as cw

 

fout = open('Results.txt', 'w')

 

fin_nm = input('Input file (*.csv) = ')

with open(fin_nm, 'r') as f:

    Data_in = [v for v in csv.reader(f)]

 

fout.write('Input Data File = {}\n'.format(fin_nm))

 

NSt = len(Data_in) - 1

K = len(Data_in[0]) - 1

Sts = np.empty(NSt)

Rs = np.empty((NSt, K), dtype = 'int')

for j in range(NSt):

    Sts[j] = float(Data_in[j+1][0])

    for k in range(K):

        Rs[j][k] = int(Data_in[j+1][k+1])

 

fout.write('\nData:\n')

for j in range(NSt):

    print(f'{Sts[j]:>10g}', end = '')

    fout.write(f'{Sts[j]:>10g}')

    for k in range(K):

        print(f'{Rs[j][k]:>5d}', end = '')

        fout.write(f'{Rs[j][k]:>5d}')

    print()

    fout.write('\n')

 

sm = pystan.StanModel(file = 'RatingFechner.stan')

 

Data = {'NSt':NSt, 'K':K, 'Sts':Sts, 'Rs':Rs}

 

fit = sm.sampling(data = Data, n_jobs = 1)

 

print(fit)

 

Psis = fit['Psis']

sgm = fit['sgm']

Cs = fit['C']

 

for i in range(NSt):

    sb.kdeplot(Psis.T[i], label = '$\psi_{}$'.format(i+1))

plt.title('Posterior Ditributions of $\psi_s$', fontsize = 20)

plt.yticks([])

plt.legend()

plt.tight_layout()

plt.savefig('FigPsiPost.png')

plt.show()

 

for i in range(NSt):

    sb.kdeplot(sgm, label = r'$\sigma_{}$'.format(i))

plt.title('Posterior Distribution of $\sigma$', fontsize = 20)

plt.legend()

plt.savefig('FigSgmPost.png')

plt.show()

 

plt.plot([0, 0], [0, 5], label = r'$C_0$')

for i in range(1, K-2):

    sb.kdeplot(Cs.T[i], label = r'$C_{}$'.format(i+1))

plt.plot([1,1], [0, 5], label = r'$C_{}$'.format(K-1))

plt.title('Posterior Distributions of Cs', fontsize = 20)

plt.yticks([])

plt.legend()

plt.savefig('FigCsPost.png')

plt.show()

 

v_psis = np.empty(NSt)

for i in range(NSt):

    v_psis[i] = em.EstMode(Psis.T[i])

v_sgm = em.EstMode(sgm)

 

fout.write('\n{0:>15s}{1:>15s}\n'.format('Stimulus', 'Mode Psi'))

for vst, vpsi in zip(Sts, v_psis):

    fout.write('{0:>15.2f}{1:>15.2f}\n'.format(vst, vpsi))

fout.write('\nSigma: mode = {0:>15.2f}\n'.format(v_sgm))

   

v_Cs = np.empty(K-1)

v_Cs[0] = 0.0

v_Cs[K-2] = 1.0

for k in range(1, K-2):

    v_Cs[k] = em.EstMode(Cs.T[k])

for k in range(K-1):

    plt.plot([Sts[0], Sts[-1]], [v_Cs[k], v_Cs[k]], color = 'y')

plt.plot([], 'y-', label = 'C')

plt.plot(Sts, v_psis, color = 'b', lw = 3, label = '$\psi$')

plt.plot(Sts, v_psis - v_sgm, color = 'g', linestyle = '--', lw = 1,label = '$\psi - \sigma$')

plt.plot(Sts, v_psis + v_sgm, color = 'g', linestyle = '--', lw = 1, label = '$\psi+ \sigma$')

   

plt.xlabel('Stimulus', fontsize = 16)

plt.ylabel('$\psi$', fontsize = 16)

plt.legend(loc = 'upper left')

plt.title('Relation of Stimulus and $\psi$', fontsize = 18)

plt.tight_layout()

plt.savefig('PsiSgmC.png')

plt.show()

 

Probs = np.empty((NSt, K))

for s in range(NSt):

    Probs[s][0] = ss.norm(loc = v_psis[s], scale = v_sgm).cdf(v_Cs[0])

    Probs[s][K-1] = 1.0 - ss.norm(loc = v_psis[s], scale = v_sgm).cdf(v_Cs[K-2])

    for k in range(1, K-1):

        Probs[s][k] = ss.norm(loc = v_psis[s], scale = v_sgm).cdf(v_Cs[k]) -\

                        ss.norm(loc = v_psis[s], scale = v_sgm).cdf(v_Cs[k-1])

           

print('Predicted Prob(Rating|Stimulus):')

for s in range(NSt):

    for k in range(K):

        print(f'  {Probs[s][k]:7.3f}', end = '')

    print()

   

Pred_Rs = np.empty((NSt, K))

N_Rs = np.sum(Rs, axis = 1)

for i in range(NSt):

    Pred_Rs[i] = N_Rs[i] * Probs[i]

   

print('Predicted Frequencies of Rating for Stimulus:')

for s in range(NSt):

    for k in range(K):

        print(f'  {Pred_Rs[s][k]:7.1f}', end = '')

    print()

 

cum_data_Rs = np.cumsum(Rs, axis = 1)

cum_pred_Rs = np.cumsum(Pred_Rs, axis = 1)

 

print(cum_data_Rs)

 

for s in range(NSt):

    if s == 0:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3, label = 'Data')

    else:

        plt.plot(range(K), cum_data_Rs[s]/cum_data_Rs[s][K-1], 'g:', lw = 3 )

    if s == 0:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-', label = 'Model')

    else:

        plt.plot(range(K), cum_pred_Rs[s]/cum_pred_Rs[s][K-1], 'b-')

 

plt.xticks(range(K), range(1, K+1))

plt.xlabel('Rating', fontsize = 14)

plt.ylabel('Cum. Prop.', fontsize = 14)

plt.legend()

plt.title('Data and Model Prediction', fontsize = 18)

plt.tight_layout()

plt.savefig('FigDataModel.png')

plt.show()

 

waic2 = cw.calcwaic2(Rs, fit['theta'])

print('WAIC2 =', waic2)

fout.write('\n\nWAIC = {0:.3f}\n'.format(waic2))

 

fout.close()

 

 

 

Up