一対比較法における一意性の検定

公開:2021/01/07
更新:2021/02/05
24 min読了の目安(約22100字TECH技術記事

一対比較法における一意性の検定

Python3などを利用して一対比較法における一意性の検定を行いたいが、こういった統計を始めるにも、基礎知識がないと、入門書すら読めない。またそこまで(実際の集計手順から検定完了まで)、ちゃんとまとめて解説しているものが意外にも見当たらない。
ということで最低限必要な基礎知識から実際の計算方法、実装までこの記事で書けたらいいなと思っています。
実際にどのようにして集計から検定を行うのか、ピンポイントの情報がなかったので、自分用メモとして残す目的です。
Githubにも上げています↓

https://github.com/Iovesophy/paired_comparison_method.git

間接的尺度構成法

一対比較法はこの間接的尺度構成法の一つである。
普段の生活で感覚の質的特性を直感的に理解するのは難しい。
なぜなら、日常の生活中に感覚的に体験する事象は、観察対象の質的な特性で、量的な特性でないからということが原因の一つとして挙げられる。
このため、人間は自分の感覚量を直接表現することは困難と考える研究者が多い。
こうした際に、研究者は感覚量を直接表現するのではなく、質的な判断から間接的に間隔尺度を構成する方法が提案されている。
こうした方法のことを間接的尺度構成法と呼ぶ。[1]

間接的尺度構成法主な種類

  • 一対比較法
  • 系列カテゴリー法
  • 累積丁度可知差異法
    etc..

一対比較法とは

一対比較法概要[2]

順位法では、感覚や好みの強さを測るのに、全ての試料を一度に評価して順位付けする必要があります。
試料数が多くなると、一度に順位付けすることが困難になることがあります。
そのような場合、2つの試料を対にして比較します。これを全ての対について行う方法が一対比較法です。

人間が順位付けを行う際に、試料数が増えれば増えるほど、困難になる(例えば、今日着る服を選ぶときに選択肢が多いと迷って決められない)
この場合に、2つの試料を比較し、全ての対を比較する方法。

※上図はイメージです。

一対比較法における試験の主な種類

  • 一意性の係数
  • 一致性の係数
  • ブラッドレイの一対比較法
  • サーストンの一対比較法
  • シェフェの一対比較法

etc..

今回は一意性の係数を取り扱う
例えば3つの試料A、B、Cがある時、A>B、B>Cならば、A>Cであるはず。
では実際に、このようになっているかを検定する。

手法について

人間の好みや匙加減を測るときには、以下のような状況が生じる可能性がある。
例えば、A、B、Cの3つの試料の評価時に、実際に評価を行うとA=Cや、A>Cと評価されてしまい、一貫した評価がなされない状況が生じる。
このような3者間で順位がつけられない状態を一巡三角形と呼ぶ。

循環三角.png

A>B、B>CならA>Cなはずである、しかし時に、A<Cな場合がある。


A>B>C
A>B,B>C,C<A

A>B>C>A
A>B,B>C,C>A

言い換えると、
AはBより大きい(A>B),BはCより大きい(B>C)ならばAはCより大きい(A>C)はずである
(直観的に考えてもこうなると思います)

しかし時に、AはCより小さい(A<C)場合がある。

試料の数がn個あった時に、
3つずつ組み合わせて(A,B,C)
一巡三角形の数を数える場合、

d (一巡三角形数)

一巡三角形が生じる確率が十分に小さいなら、各試料間に順位をつけられる、
つまり、順位に一意性があったと考えてよい。

この検定法を一意性の検定という。

具体的には、

好み.png

A ~ Fの6つの試料に対して、どちらがより好きかということを考えてみる。
図中の矢印は、A → Bは、AよりもBの方が好きなことを示すものとする。

表1

i>j A_j B_j C_j D_j E_j F_j a_i=計
A_i - 1 a_1=1
B_i 1 - 1 a_2=2
C_i 1 1 - 1 a_3=3
D_i 1 1 1 - a_4=3
E_i 1 1 1 - 1 a_5=4
F_i 1 1 - a_6=2

また、表中の数値1は、例えば、1列目A_j、2行目B_iの1はA_jよりもB_iの方が好きなことを示すものとする。
この時、6つの試料間に好みの順位が存在すると考えてよいのか考えてみる。[3] [4]

(kは試料数)k=6の場合は、

d ≦ 1
の時に、5%水準で試料間に有意な順位があるといえる。

k=7の場合は、

d ≦ 3
の時に、5%水準で試料間に有意な順位があるといえる。

また、

k ≧ 8
の時は、カイ二乗検定を行うことにより統計的に有意な順位であるかを判定することができる。

ここで、この検定を行う際に注意しなければならない点であるが、

k = 5

ただし、1個以上の場合には一意性がないと判断できるが、検定の有意水準が通常の5%ではなく、12%水準での検定となる。

また、

k ≦ 4
の時は、d = 0であっても、有意に達しないということをまず認識しておかなければならない。
つまり、一巡三角形が0個であっても有意な順位があることにはならず、試料数が最低でも5個以上でないと検定結果を有効活用できないので注意が必要。

計算式

計算式を以下に示す[3:1] [4:1]
一巡三角形の個数dは次の式で表す

d = {}_k C_3 -\sum_{i=1}^k {}_{a _i}C _2

{}_k \mathrm{C}_3 = \frac{k!}{3!(k-3)!} = \frac{1}{6}k(k-1)(k-2)

\sum_{i=1}^k {}_{a _i}C _2=\frac{1}{2}\sum_{i=1}^k a_i(a_i-1)

展開

d = \frac{1}{6}k(k-1)(k-2)-\frac{1}{2}\sum_{i=1}^k a_i(a_i-1)

kは試料数を示しており、a_iは試料iが得た票の合計を示す(表1を参照)

自由度fは次の式で表す

f = \frac{k(k-1)(k-2)}{(k-4)^2}

一意性の係数\zetaは次の式で表す(kが偶数の時)

\zeta = 1 - \frac{24d}{k^3 - 4k}

一意性の係数\zetaは次の式で表す(kが奇数の時)

\zeta = 1 - \frac{24d}{k^3 - k}

カイ二乗値は以下の式で表す

\chi_o^2= \frac{8}{k-4} \left\{ \frac{k(k-1)(k-2)}{24} -d + \frac{1}{2} \right\} +f

\chi^2 カイ二乗分布表(表2)より,有意水準\alpha%での被験者の判定は首尾一貫しているといえるかどうか判断する。

\chi_0^2 \geqq \chi^2 (f, \alpha)

表2

自由度 有意水準 .05 有意水準 .01
1 3.841 6.635
2 5.991 9.210
3 7.815 11.345
4 9.488 13.277
5 11.070 15.086
6 12.592 16.812
7 14.067 18.475
8 15.507 20.090
9 16.919 21.666
10 18.307 23.209
- - -
11 19.675 24.725
12 21.026 26.217
13 22.362 27.688
14 23.685 29.141
15 24.996 30.578
16 26.296 32.000
17 27.587 33.409
18 28.869 34.805
19 30.144 36.191
20 31.410 37.566
- - -
21 32.671 38.932
22 33.924 40.289
23 35.172 41.638
24 36.415 42.980
25 37.652 44.314
26 38.885 45.642
27 40.113 46.963
28 41.337 48.278
29 42.557 49.588
30 43.773 50.892

引用: 山田剛史・村井潤一郎「よくわかる心理統計」付表3より[5]

Python3を用いて実際にデータ集計から一意性の検定までを行う

環境

  • Python 3.9.1 (default, Dec 24 2020, 16:23:16)
  • macOS Big Sur ver11.1(このOSで動作を確認)

流れ

  • 試料の準備
  • 試料の提示と実験
  • データ集計
  • 実験データをcsvに出力
  • csvデータから一巡三角形の個数dを算出
  • csvデータから自由度fを算出
  • csvデータから一意性の係数\zetaを算出
  • csvデータからカイ二乗値\chi_o^2を算出
  • 算出した\chi_o^2とカイ二乗分布表(表2)を用いて一意性の検定を行う

試料の準備

試料の準備の際に注意しなければならない点
kは試料数を表しています
上述の通りだが、

k = 5

ただし、1個以上の場合には一意性がないと判断できるが、検定の有意水準が通常の5%ではなく、12%水準での検定となる。

また、

k ≦ 4
の時は、d = 0であっても、有意に達しないということをまず認識しておかなければならない。
つまり、一巡三角形が0個であっても有意な順位があることにはならず、
試料数が最低でも5個以上でないと検定結果を有効活用できないので注意が必要。

試料の提示と実験

試料の準備ができたら、試料について言語化、あるいは図化し、アイコンとして提示できる形にしていく。
このアイコンは自分にとっても被験者にとってもわかりやすい形にしておくのがベスト、最悪アルファベットを並べるだけでもアイコンとしては機能する。(例えば、前述のA~Fなど)
そしてもし被験者が1人で一対比較を行った場合,表3の一巡三角形の個数dの値のとき有意水準5%で「判断者に識別力がないこと」を棄却できることが知られている。[6]

表3

k 5以下 6 7 8 9
d 有意水準5%に達しない 1以下 3以下 7以下 14以下
{}_kC_3 10以下 20 35 56 84

引用:「AHPにおける一対比較法に関する一考察 官能検査における一対比較法の利用」 飯田洋市より

データ集計、PCPSによって得られた実験データをcsvに出力

即席で実験補助アプリとして「一対比較法実験集計システムver2:2021,1,3 Paired comparison method data processing software, codename PCPS」を作った(以下 PCPSと呼ぶ)

PCPSによる実験フロー

実験フロー.png

また、Arduino Leonardo(Pro Micro)を用いてPCPS用のHIDデバイス(PCPS_selector)を作る。

0EBE9FEA-E8FE-4130-9C89-FCC6EEDF8646.jpeg


PCPS_selectorを用いた提示の様子(※試料はどのような形でも構わない)

「一対比較法実験集計システムver2:2021,1,3 Paired comparison method data processing software, codename PCPS」解説

使用するpython3のライブラリ(一部pipでインストールする必要があります)

  • time
  • datetime
  • math
  • itertools
  • sys
  • random
  • pandas
  • csv

ライブラリのインポート

import time
import datetime
import math
import itertools
import sys
import random
import pandas as pd
import csv

Welcomeメッセージの表示、
ここでは、表示させたい情報や、実験時の確認ポイントなどを記載すると良いと思う。


def welcome_mes(): # 起動時のメッセージ
    print("Welcome to 一対比較法実験集計システムver2:2021,1,3")
    print("Paired comparison method data processing software, PCPS")
    print("made by kazuya yuda.")

プログラム終了のためのサブルーチンで,主にパラメータ確認のフェーズで間違っていた時に終了させる用途。

def exit_all(): # 終了処理
    sys.exit()

試行回数を算出するため、組み合わせを計算する、mathライブラリを使用


def combinations_count(n, r):
    return math.factorial(n) // (math.factorial(n - r) * math.factorial(r))

被験者情報の入力や試料数の確認等の初期設定
ちなみに絶対バリデーションすべきですが、未実装、余裕があるさい改良予定
(Unittest等も行うべき)


def initial_data_set(): # データ初期設定 
    data=[] # 統合的にデータを保持

    print("被験者情報を入力:",end=" ")
    data.append(input()) # 被験者の名前を格納

    print("備考:",end=" ")
    data.append(input()) # 被験者についての備考を格納

    print("試料数を入力:",end=" ")
    n = input()
    if int(n) < 2:
        print("試料数が足りません.")
        exit_all()
    data.append(int(n)) # 試料数を格納
    print("%s を設定しました" % n)
    return data

試料数分の試料の読み込み、ない場合は生成も行う、
csvを読み込んで試料情報を取得することも可能
その際、同一ディレクトリにsample_info.csvを用意しておく必要がある。
以下のような内容でcsvを作成しておく↓(一例:試料数が6でA~Fの英字アイコン、ヘッダーもつけておきましょう)

sample_info.csv
No,試料情報
1,A
2,B
3,C
4,D
5,E
6,F

  • 上述のcsvを読み込む部分(UTF-8形式で作成しておく必要があります)
  • 初回、csvを用意していなかった場合にその場で提示情報を作成可能(ちなみにこの場合提示情報の記録はしません)

提示試料アイコンの格納にmaterialという名前で配列を作成(今後も使います)


def material_get(n): # 試料読み込み
    material=[]
    print("試料情報をcsvから読み込みますか?y,n:", end=" ")
    if input() == n:
        for i in range(int(n)):
            count = i + 1
            print("試料%dの提示したい情報を入力:" % count , end=" ")
            material.append(input())
    else:
        csv_file = open("./sample_info.csv", "r", encoding="utf_8", errors="", newline="" )
        f = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)
        header = next(f)
        print(header) # ヘッダーを出力
        count = 0
        for row in f: # データ読み込み
            if count == int(n):
                break
            else:
                count += 1
                print(row)
                material.append(row[1])
    return material

試行回数分一対比較用の比較試料生成
また、提示試料はランダムに決定する必要があるので、先程のmaterialをシャッフル


def itertools_make_material(material): # 試行回数分一対比較用の比較試料生成
    itertools_material=[]
    for i in itertools.permutations(material, r=2):
        itertools_material.append(i)
    random.shuffle(itertools_material)
    return itertools_material

以上で全ての設定が完了なので、最後に各種パラメータや被験者情報を確認するフェーズを設ける
のちにリファクタリングして、captionをまとめているが、前述まで全てのパラメータ情報はdata配列に入っている
機能追加の際は基本的にdata配列を参照すれば良い


def confirmation(data): # パラメータ確認フェーズ
    print("パラメータを確認してください↓")
    caption = ["被験者","備考","試料数:","試料:","試行回数:"]
    for i in range(5):
        print(caption[i],end=' ')
        print(data[i])

実験開始処理、役割としては時間を記録しておくこと
パラメータ確認フェーズを経て、本当に開始していいかの確認も行う
ちなみに、パラメータに誤りがある場合は一旦処理を終了する


def start(): # 実験開始処理
    print("実験を開始しますか? y,n:",end=" ")
    if input() == "y":
        print("実験開始します.")
        print("開始時間:")
        print(datetime.datetime.now())
        start_time = datetime.datetime.now()
        return start_time
    else:
        print("中断します,最初からやり直してください.")
        exit_all()

実際にデータを提示し、処理するフェーズ
入力間違えが発生した際に(HIDデバイスを作るなら、被験者の押し間違え)対応も可能だ
これは基本的に再帰で実現している

  • itertools_material は提示する試料が格納されている
  • main_data は選択結果を記録するもの
  • count は現在の試行回数を把握するもの
  • i はイテレータのi
  • option はファイナライズ中かそうでないかを判断するためのフラグ

def process(itertools_material,main_data,count,i,option): # メインインターフェース
        print(itertools_material[i])
        print("%d回目 どちらの試料が選ばれましたか?:左 → 0 , 右 → 1 , 戻る→ r" % count)
        ans = input()
        if ans == "r" and i != 0 and option != "final":
            print("何回目に戻りますか?:",end=" ")
            count_val = input()
            if str.isalpha(count_val):
                print("数値を入力してください")
                l = process(itertools_material,main_data,count,i,option)
            else:
                count_change = int(count_val)
                if count_change <= 0:
                    print("0回目以下は存在し得ません.")
                    l = process(itertools_material,main_data,count,i,option)
                elif count_change >= count:
                    print("現在の回数より後へは戻れません.ファイナライズ画面で再度リクエストしてください.")
                    l = process(itertools_material,main_data,count,i,option)
                else:
                    i_change = count_change-1
                    l = process(itertools_material,main_data,count_change,i_change,option)
                    main_data[i_change]=l

                    if option == "final":
                        final_process(itertools_material,main_data,count,i,option)
                    else:
                        l = process(itertools_material,main_data,count,i,option)
        elif ans == "0" or ans == "1":
            l = list(itertools_material[i])
            l.append(ans)
        else:
            print("指定された数値を入力してください,1回目,ファイナライズ時は戻れません.")
            l = process(itertools_material,main_data,count,i,option)
        return l

ファイナライズ処理、ちなみに、なぜこのフェーズを書いたかというと、筆者の経験で実験中最後の試行中に誤って選択をし、実験そのものが失敗に終わることがあったため...
間違えた際そのまま処理を終了してしまい、終了時間を正しく記録できない問題と、後でcsvをマニュアル修正する必要が出てきて、著しく利便性を損なったため。
基本は前述のサブルーチン(process)と同じです。


def final_process(itertools_material,main_data,countneo,i,option): # ファイナライズ
    print("以上で終了です,本当に終了してもよろしいですか?y,n:",end=" ")
    ans = input()
    if ans == "n" and i != 0 and ans != "0":
        print("何回目に戻りますか?:",end=" ")
        count_val = input()
        if str.isalpha(count_val):
            print("数値を入力してください")
            l = process(itertools_material,main_data,count,i,option)
        else:
            count_change = int(count_val)
            if count_change <= 0:
                print("0回目以下は存在し得ません.")
                l = final_process(itertools_material,main_data,countneo,i,option)
            elif count_change >= countneo:
                print("現在の回数より後へは戻れません.指定された回数は存在しません.")
                l = final_process(itertools_material,main_data,countneo,i,option)
            else:
                option = "final"
                i_change = count_change-1
                l = process(itertools_material,main_data,count_change,i_change,option)
                main_data[i_change]=l
                l = final_process(itertools_material,main_data,countneo,i,option)
    else:
        print("終了処理を開始します.")
        print("※ 処理中はプログラムを中断しないでください")

    return main_data

終了時間記録

def process_end(): # 終了時間記録
    end_time = datetime.datetime.now()
    return end_time

pandasでcsvに出力


def result_export_process(data,integration_data,end_time): # CSV形式でエクスポート
    print(data[0:])
    print(integration_data[0:])
    Coulum = ['比較データ左','比較データ右','選択結果']
    df_info = pd.DataFrame(data)
    df_main = pd.DataFrame(integration_data,columns=Coulum)
    df_info.to_csv("%s%s%s_result_info.csv" % (data[0],"-",end_time))
    df_main.to_csv("%s%s%s_result_main.csv" % (data[0],"-",end_time))

最後にmainで各種サブルーチンの呼び出し

def main(): # メインops
    welcome_mes() # welcomeメッセージ生成

    data = initial_data_set() # データ初期設定後dataへ格納

    material = material_get(data[2]) # 試料情報を格納する
    data.append(material[0:])

    N = int(data[2]) # 試料数取得

    try_num = combinations_count(N,2) # 試行回数取得(組み合わせを計算)
    data.append(try_num)

    itertools_material = itertools_make_material(material) # 試料をシャッフル,取り出し

    confirmation(data) # パラメータ確認フェーズ

    # start
    data.append(start())
    
    main_data=[] # 一対比較の実験データ格納用
    
    option="none"
    for i in range(try_num): # 試行回数分イテレーション
        count = i + 1
        main_data.append(process(itertools_material,main_data,count,i,option))

    # finalaize開始
    option="final"
    main_data = final_process(itertools_material,main_data,try_num+1,i,option)
    integration_data = main_data

    # end
    end_time = process_end()
    data.append(end_time)

    # export
    result_export_process(data,integration_data,end_time)

ちなみに、processのイテレーションはmainで処理しています。

    option="none"
    for i in range(try_num): # 試行回数分イテレーション
        count = i + 1
        main_data.append(process(itertools_material,main_data,count,i,option))

Arduino Leonardo(Pro Micro)を用いてPCPS用のHIDデバイス(PCPS_selector)を作る

作りは非常にシンプルで、PCPSの一対提示試料の選択に必要な数値を入力できるキーボードを作るイメージ。

選択には以下の数値を割り当てている。
左 : 0
右 : 1

左のボタンを押せば0を右のボタンを押せば1を入力できるデバイスを作成した。
ボタンに関しては色分けしておくほうがわかりやすい。

用意するもの

  • Arduino Leonardo もしくは Arduino Pro Micro
  • プッシュボタンスイッチ

ちなみに参考として、当方はスイッチサイエンス版のArduino Pro Micro

IMG_6328_500.jpg

プッシュボタンスイッチは以下のものを用意した
mxuteuk 12個1A 250V AC 2ピンSPST 6色ノーマルオープンミニ瞬間プッシュボタンスイッチPBS-110-6C

btn.png

書き込みに関しては以下のページを参照

https://www.arduino.cc/en/Guide
PCPS_selector.ino

#include "Keyboard.h"
#define select_left_0 5
#define select_right_1 6

void setup() {
  Keyboard.begin();
  pinMode(select_left_0, INPUT_PULLUP);
  pinMode(select_right_1, INPUT_PULLUP);
}

void loop() {
  if(digitalRead(select_left_0) == LOW){
    Keyboard.write('0');
    Keyboard.write('\n');
    delay(100);

    while(digitalRead(select_left_0) == LOW);
  }

  if(digitalRead(select_right_1) == LOW){
    Keyboard.write('1');
    Keyboard.write('\n');
    delay(100);

    while(digitalRead(select_right_1) == LOW);
  }

  delay(100);
}

CSVデータより各種計算

csvデータから一巡三角形の個数dを算出
csvデータから自由度fを算出
csvデータから一意性の係数\zetaを算出
csvデータからカイ二乗値\chi_o^2を算出
算出した\chi_o^2とカイ二乗分布表(表2)を用いて一意性の検定を行う

流れ

  • まず、実験データを元に得票を行列に変換
  • 行列から得票合計a_iを算出
  • 一巡三角形の個数dを計算式より算出

このような形で表示されます。
一例.png

ちなみに、上図のデータは一例として提示するために、筆者が適当に生成。
当然dの数が1以上なので、回答に一意性がないことがわかる。

解説

各種ライブラリのインポート

import math
import itertools
import pandas as pd
import csv
import subprocess
import re
import numpy as np

csvファイルのインポート
実験を完了した被験者分のデータがあると思うので、解析したいデータを確認し、入力する。
その際、mainのcsvファイル名が確認できるようにsubprocessを用いてlsコマンドを実行する。
標準出力にフィルターをかけて、csvファイルのみ抽出する。

def import_csv(): # 試料読み込み
    material=[]
    info=[]

    # ファイル名確認
    return_code = subprocess.check_output(['ls'])
    code = return_code.split(b"\n")
    for i in range(len(code)):
        stdout_txt = str(code[i]).replace("b","").replace('\'',"")
        if re.search("csv",stdout_txt) and stdout_txt != "sample_info.csv":
            print(stdout_txt)
    print("拡張子を含めて、解析したいmainデータファイル名を入力してください")
    filename = input()
    print(filename,end=" ")
    print("を読み込みました.")
    print("集計開始してよろしいですか?y,n:", end=" ")
    ans = input()
    if ans == "n":
        print("終了します。")
    elif ans == "y":
        # main file のインポート
        csv_file = open(filename, "r", encoding="utf_8", errors="", newline="" )
        f = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)
        header = next(f)
        #print(header[1:]) # ヘッダーを出力
        for row in f: # データ読み込み
            #print(row[1:])
            material.append(row[1:])

        # info file のインポート
        filename2 = filename.replace("main","info")
        csv_file = open(filename2, "r", encoding="utf_8", errors="", newline="" )
        f = csv.reader(csv_file, delimiter=",", doublequote=True, lineterminator="\r\n", quotechar='"', skipinitialspace=True)
        header = next(f)
        #print(header[1:]) # ヘッダーを出力
        for row in f: # データ読み込み
            #print(row[1:])
            info.append(row[1:])
    else:
        pass

    # まとめる
    material_result=['','']
    material_result[0] = info
    material_result[1] = material

    return material_result

試料数kと試行回数nをPCPSより出力されたinfo.csvからロード

def get_k(info):
    k = int(str(info[2]).replace("[","").replace("]","").replace("\'",""))
    return k

def get_n(info):
    n = int(str(info[4]).replace("[","").replace("]","").replace("\'",""))
    return n

自由度f算出


def f_calculation(info):
    k = get_k(info)
    k_1 = k * (k-1) * (k-2)
    k_2 = (k-4) * (k-4)
    f = float(k_1/k_2)
    return f

得票を集計、一巡三角形の個数dを算出する。
集計の際に行列に変換するためnumpyを利用している。

def make_vote_list_and_calculation(info,material):
    # infoからk,n読み込み
    k = get_k(info)
    n = get_n(info)

    # list 横軸作成
    vote_list = []
    for i in range(k+1):
        vote_list.append('')

    # list 縦軸作成
    vote_list_all = []
    for i in range(k+1):
        vote_list_all.append(vote_list)

    # 行列変換
    vote_list_all_np = np.array(vote_list_all,dtype=object)

    # 不要行 - 挿入(表示用)
    for i in range(k+1):
        vote_list_all_np[i][i] = '-'

    # 横軸iconより縦軸iconの方が大きい(表示用)
    vote_list_all_np[0][0] = 'i>j'

    # 提示試料情報抽出(表示用)
    get_icon = info[3]
    icon = get_icon[0].replace("[","").replace("]","").replace("\'","").replace("\"","").replace(" ","").split(',')

    # 提示試料情報セット(表示用)
    count = 0
    for send_icon in icon:
        vote_list_all_np[0][count+1] = send_icon
        vote_list_all_np[count+1][0] = send_icon
        count += 1

    # 得票挿入
    for i in range(n):
        selector = int(material[i][2])
        yoko = icon.index(str(material[i][selector]))
        selector2 = 0 if selector == 1 else 1
        tate = icon.index(str(material[i][selector2]))

        # add 1 point
        vote_list_all_np[yoko+1][tate+1] = 1

        # add 0 point
        vote_list_all_np[tate+1][yoko+1] = 0

    # 得票行列表示
    print("得票表")
    print(vote_list_all_np)

    # 計算用記号→0
    for i in range(k+1):
        vote_list_all_np[i][i] = 0

    # 計算用記号→0
    vote_list_all_np[0][0] = 0

    # 計算用提示試料情報→0
    count = 0
    for send_icon in icon:
        vote_list_all_np[0][count+1] = 0
        vote_list_all_np[count+1][0] = 0
        count += 1

    vote_sum = np.sum(vote_list_all_np, axis=1)

    print("得票 a_i:",end=" ")
    print(vote_sum)
    print("得票 Σa_i:",end=" ")
    print(np.sum(vote_sum))

    vote_calc_result = 0
    for i in range(k):
        vote_calc = vote_sum[i]*(vote_sum[i] - 1)
        vote_calc_result += vote_calc

    d = float(1/ 6) * float(k) * (float(k)-1.0) * (float(k)-2.0) - 0.5 * float(vote_calc_result)
    return d

最後に\zeta\chi_o^2を算出する


def zeta_calculation(f,d,info):
    k = get_k(info)
    if k % 2 == 0:
        print("試料数k:偶数")
        v_1 = 24*d
        v_2 = (k^3) - (4*k)
        v_3 = float(v_1/v_2)
        zeta = 1 - v_3
    else:
        print("試料数k:奇数")
        v_1 = 24*d
        v_2 = (k^3) - (k)
        v_3 = float(v_1/v_2)
        zeta = 1 - v_3

    return zeta

def chi_2_0_caluculation(f,d,info):
    k = get_k(info)
    v_1 = k-4
    v_2 = k*(k-1)*(k-2)
    v_3 = float(v_2/24)
    chi_2_0 = float(8/v_1) * (float(v_3) - float(d) + 0.5) + float(f)
    return chi_2_0

一意性が認められたら、次は一致性の検定を行い、有効被験者間の回答は一致しているかを検定できる。

次回の記事で一致性の検定を書きます。(2021年1月9日に投稿済み)

https://zenn.dev/_kazuya/articles/66ef65022407ef

※間違い、誤植等、発見されましたらご指摘、ご指南いただけると幸いです。

脚注
  1. 「基礎心理学実験法ハンドブック」 ,日本基礎心理学会 (責任編集:坂上貴之,河原純一郎,木村英司,三浦佳世,行場次郎,石金浩史) ↩︎

  2. 「一対比較法」 感性・官能評価システム J-SEMS https://j-sems.com/一対比較法/ ↩︎

  3. 武庫川女子大紀要(自然科学) 「PC 画面上で見る三原色の季節感について」 伊佐治せつ子,和泉 志穂 https://core.ac.uk/download/pdf/233608433.pdf ↩︎ ↩︎

  4. 修士論文 「拡張現実間における擬似触覚を用いた力覚フィードバック提示手法」 東京大学大学院工学系研究科電気系工学専攻 大塚 隆史 平成25年2月6日 ↩︎ ↩︎

  5. 「よくわかる心理統計」 山田剛史・村井潤一郎 ミネルヴァ書房 ↩︎

  6. 「AHPにおける一対比較法に関する一考察 官能検査における一対比較法の利用」 飯田洋市 ↩︎