Up

差尺度法の実験

提示刺激組をランダムに選ぶ場合

 

感覚の強さの測定法としては、マグネチュード推定法あるいはカテゴリ尺度法がよく知られている。これらでは、提示された個々の刺激の感覚の評定が求められる。Maloney & Yang (2003)の最尤法による差尺度法では、4つの刺激i, j, s,tが提示されて、対(i, j)における刺激iと刺激jの感覚の差と、対(s, t)における刺激sと刺激tの感覚の差の比較が求められ、差の大きい方の対がどちらの対であるかの比較判断が求められる。図1に4つの円盤が表示された例を示す。

図1

 

観察者は、上の2枚の円盤の大きさの差と、下の2枚の円盤の大きさの差を比較して、差が大きい方の対がどちらであるかの判断が求められる。

実験の実施も、PCを利用すれば、4つの刺激の選択提示と比較判断の記録は自動的に行うことができる。刺激提示がPCで出来ないときは、提示する刺激のラベルをディスプレイ画面に示して、提示されたラベルの刺激を実験者が提示すればよい。観察者の比較判断は実験者がキーボードで入力すればよい。

実験実施のためのスクリプト例は、リスト1に示したが、このスクリプトでは、提示刺激はディスプレイ上の円盤になっている。円盤以外の刺激、あるいはディスプレイ画面以外での刺激用の実験のためにリスト1を書き換えることは容易であろう。円盤の替わりに提示刺激の番号を提示し、実験者が提示された刺激番号に合わせて刺激を提示すればよい。観察者の判断は実験者がキーを押して入力すればよい。リスト1のスクリプトでは、提示刺激は1からの整数値で表されている。刺激の総数がSであれば、1からSまでの整数値で表され、刺激値の強さと刺激値を表す整数値は単調関係にあるものとする。スクリプト中では、整数値で刺激値を表しているが、刺激値の間隔は任意である。スクリプトファイルなどはまとめて圧縮ファイルexp_files.zipとした。

 

リスト1では、刺激数は5としている。

 

N_st = 5       #   刺激の総数

 

5つの刺激は、次のコードにより作成した画像をリストL_patchesに格納している。

 

"""

      N_st=5 に対して作成

   大きさの異なる5枚の円盤のイメージをリストL_patchesに格納

"""

L_patches = []

for ip in range(5):

    temp_s = pygame.Surface((scl_img, scl_img))

    c_r = scl_img*0.5*0.2 + 0.8 * scl_img * 0.5 * ip/4

    pygame.draw.circle(temp_s, (255,255,255), (scl_img/2, scl_img/2), c_r)

    L_patches.append(temp_s)

 

 

N_st個の刺激から、4個の刺激をランダムに選び、上の刺激対st0と下の刺激対st1を設定している。

 

    st_choice = rng.choice(np.arange(N_st)+1, size=4, replace=False)

    st0 = st_choice[:2]        #     上に提示する対

    st1 = st_choice[2:]        #     下に提示する対

 

 

リスト1のスクリプトによる実験は、以下のように進む。

 

リスト1のスクリプトを実行すると、図2の画面になる。

図2

 

Enterキーの押下で、図3の画面になる。

実験中の画面であり、図3の場合、画面上部に「1/400」と表示されているのは、総試行数400試行における第1試行であることを示している。

図3

 

総試行数は、次のコード

 

n_trials = 400    #    総試行数

 

で与えられている。

実験は、適宜休憩できる。また総試行数を減らして、複数の観察者に対して行い、それらをまとめて分析することもできる。刺激提示はランダムであるので、条件が同じであれば異なる実験結果を単純にまとめることができる。1つ1つの実験の結果は、CSV形式でデータが格納されるが、それらはExcelなどで簡単にまとめることができる。

 

スペースキーを押すと、刺激図形が表示され、図4のようになる。

図4

 

上の対の方が大きさの差が大きいと思えば、上矢印キー↑、下の対に方が大きさの差が大きいと思えば、下矢印キー↓を押す。

矢印キーを押すと、円盤は消えて、図5のような画面になる。

図5

 

スペースキーを押すと次の試行の4つの円盤が提示されて対の差の比較判断が求められる。以下、図3から図5までが同様に繰り返される。

すべての試行(総試行数は400試行である)が終わると、刺激提示画面は消えて、データの出力用ファイル名の設定を求めるメッセージが端末上に表示される。データの出力用ファイル名の拡張子は「.csv」を指定する。次の例では、raw_disc.csvが設定されている。

 

Output file name (*.csv) = raw_data.csv

 

データはCSV形式のファイルに保存されるが、CSV形式のファイルについては、岡本(2019pp.33-35で説明している。

保存されたデータの分析は、別のウェブサイトで説明している。

 

 

 

参考文献

 

Maloney, L. T., & Yang, J. N. (2003). Maximum likelihood difference scaling. Journal of Vision, 3, 573-585.

岡本安晴(2019)いまさら聞けないPythonでデータ分析.丸善出版

 

 

 

リスト1 差尺度の実験

 

import pygame

import sys

import numpy as np

import copy

import pickle

 

N_st = 5       #   刺激の総数

 

pygame.init()

desk_w, desk_h = pygame.display.get_desktop_sizes()[0]

print(desk_w, ',  ', desk_h)

 

my_font = pygame.font.SysFont(None,int(desk_h/30))   

 

rng = np.random.default_rng()

 

screen = pygame.display.set_mode((desk_w, desk_h))  #    Full screen

"""

         教示(msg_intro.png)の表示

"""

msg_img = pygame.image.load('msg_intro.png')

msg_w, msg_h = msg_img.get_size()

print(msg_w, ',  ', msg_h)

screen.fill((255,255,255))

screen.blit(msg_img, ((desk_w - msg_w)//2, (desk_h - msg_h)//2))

pygame.display.flip()

"""

          Enterキーが押されるまで表示

"""

ck = True

while ck:

    for e in pygame.event.get():

        if e.type == pygame.QUIT:

            pygame.quit()

            sys.exit()

        elif e.type == pygame.KEYDOWN:

            if e.key == pygame.K_ESCAPE:     #   Escapeキーを押すと強制終了

                pygame.quit()

                sys.exit()

            elif e.key == pygame.K_RETURN:    #   Enter key pressed?

                ck = False

                break

 

screen.fill((0,0,0)) 

 

scl_img = int(desk_h / 5)          #   パッチサイズ

 

"""

      N_st=5 に対して作成

   大きさの異なる5枚の円盤のイメージをリストL_patchesに格納

"""

L_patches = []

for ip in range(5):

    temp_s = pygame.Surface((scl_img, scl_img))

    c_r = scl_img*0.5*0.2 + 0.8 * scl_img * 0.5 * ip/4

    pygame.draw.circle(temp_s, (255,255,255), (scl_img/2, scl_img/2), c_r)

    L_patches.append(temp_s)

                      

 

n_trials = 400    #    総試行数

L_res = []        #    提示刺激組と反応のリスト

i_trial = 0       #    試行数

for i_trial in range(n_trials):

    """

            N_st個の刺激から提示刺激として4個を選ぶ

    """

    st_choice = rng.choice(np.arange(N_st)+1, size=4, replace=False)

    st0 = st_choice[:2]        #     上に提示する対

    st1 = st_choice[2:]        #     下に提示する対

 

    screen.fill((0,0,0)) 

   

    srfc_str = my_font.render(

        f'Difference Scaling: {i_trial+1}/{n_trials}', True, (255,255,255))

    str_w, str_h = srfc_str.get_size()

    screen.blit(srfc_str, ((desk_w - str_w)//2, 5))

    pygame.display.flip()

    """

           スペースキーの押下を待つ

    """

    ck = True

    while ck:

        for e in pygame.event.get():

            if e.type == pygame.QUIT:

                pygame.quit()

                sys.exit()

            elif e.type == pygame.KEYDOWN:

                if e.key == pygame.K_ESCAPE:

                    pygame.quit()

                    sys.exit()

            elif e.type == pygame.KEYUP:

                if e.key == pygame.K_SPACE:

                    ck = False

                    break

    """

             刺激組の提示

    """

    screen.blit(L_patches[st0[0]-1],

                (int(desk_w/2 - scl_img*1.2), int(scl_img)))

    screen.blit(L_patches[st0[1]-1],

                (int(desk_w/2 + scl_img*0.2), int(scl_img)))

    screen.blit(L_patches[st1[0]-1],

                (int(desk_w/2 - scl_img*1.2), int(3*scl_img)))

    screen.blit(L_patches[st1[1]-1], (int(desk_w/2 + scl_img*0.2), int(3*scl_img)))

    pygame.display.flip()

 

    X = None

    ck = True  

    while ck:

        for e in pygame.event.get():

            #print('e =',e)

            if e.type == pygame.QUIT:

                pygame.quit()

                sys.exit()

            elif e.type == pygame.KEYDOWN:

                if e.key == pygame.K_ESCAPE:

                    pygame.quit()

                    sys.exit()

                elif e.key == pygame.K_UP:

                    X = 1                      #    上の対が選ばれた

                    screen.fill((0,0,0))

                    pygame.display.flip()

                    ck = False

                    break

                elif e.key == pygame.K_DOWN:

                    X = 0                      #    下の対が選ばれた

                    screen.fill((0,0,0)) 

                    pygame.display.flip()

                    ck = False

                    break

            if ck == False:

               break

    L_res.append([[st0,st1],X])       #   提示刺激の組と反応の記録

           

pygame.quit()

 

print(L_res)

 

fout_csv_nm = input('Output file name (*.csv) = ')

with open(fout_csv_nm, 'w') as f:

    f.write(',i,j,s,t,X\n')

    for i,v in enumerate(L_res):

        print(i, v)

        f.write(f'{i+1},{v[0][0][0]},{v[0][0][1]},' +

                f'{v[0][1][0]},{v[0][1][1]},{v[1]}\n')

       

print('\n' + fout_csv_nm + ' was saved.\n')

 

 

 

 

Home