[PYTHON] Test of uniqueness in paired comparison method

Test of uniqueness in paired comparison method

I would like to use Python3 etc. to test the uniqueness of the paired comparison method, but even if I start such statistics, I can't even read the introductory book without basic knowledge. Also, up to that point (from the actual counting procedure to the completion of the test), I can't find anything that explains it properly. So I hope I can write in this article from the minimum required basic knowledge to the actual calculation method and implementation. I didn't have any pinpoint information on how to actually perform the test from the aggregation, so the purpose is to leave it as a memo for myself.

I also posted it on Github ↓ https://github.com/Iovesophy/paired_comparison_method.git

(Also in Zenn) The first "test of uniqueness in paired comparison method" https://zenn.dev/_kazuya/articles/0e0c95f82cb931

The second "test of consistency in paired comparison method" https://zenn.dev/_kazuya/articles/66ef65022407ef

3rd "Paired comparison method, scaling using Thurston method, plotting (Python3 + Matplotlib)" https://zenn.dev/_kazuya/articles/a1179ed3f6e027

Final "Test of internal consistency in paired comparison method and Thurston method" https://zenn.dev/_kazuya/articles/0ad3de87944785

environment

No matter which processing software you use, I don't think it will have much effect, but this time I used Python3.

What is the paired comparison method?

Overview of paired comparison method [^ 1]

The ranking method requires all samples to be evaluated and ranked at once in order to measure the strength of sensation and preference. As the number of samples increases, it can be difficult to rank them all at once. In such cases, the two samples are paired and compared. The method of doing this for all pairs is the paired comparison method.

Main types of tests in paired comparison method

etc..

This time we will deal with the coefficient of uniqueness For example, if there are three samples A, B, and C, and A> B, B> C, then A> C should be. Now, let's actually test whether this is the case.

About the method

The following situations can occur when measuring human taste and moderation. For example, when evaluating three samples A, B, and C, if the actual evaluation is performed, A = C or A> C will be evaluated, resulting in a situation where consistent evaluation is not performed. Such a state in which the ranking cannot be assigned among the three parties is called a round triangle.

循環三角.png

** A> B, B> C should be A> C, but sometimes A <C. ** **


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

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

In other words, ** A should be greater than B (A> B), B should be greater than C (B> C), then A should be greater than C (A> C) **, (I think this will happen even if you think intuitively)

But sometimes A is less than C (A <C).

When the number of samples is n Combine 3 each (A, B, C) When counting the number of round triangles,

d (number of round triangles)

If the probability of a round triangle is small enough, each sample can be ranked, In other words, it can be considered that the ranking was unique.

This test method is called a uniqueness test.

In particular,

好み.png

Consider which of the six samples A to F you like better. The arrows in the figure indicate that A → B prefers B to A.

Table 1

i>j A_j B_j C_j D_j E_j F_j a_i=Total
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

Also, the numerical value $ 1 $ in the table indicates that, for example, $ A_j $ in the first column and 1 in $ B_i $ in the second row prefer $ B_i $ to $ A_j $. At this time, consider whether it is safe to assume that there is a preferred ranking among the six samples. [^ 2] [^ 3]

(k is the number of samples) If $ k = 6 , When $ d ≤ 1 $$, it can be said that there is a significant ranking between samples at the 5% level.

In the case of $ k = 7 , when $ d ≤ 3 $$, it can be said that there is a significant ranking between the samples at the 5% level.

In addition, when $ k ≥ 8 $, it is possible to determine whether the rank is statistically significant by performing a chi-square test.

Here, it is important to note when performing this test.

When $ k = 5 $, it can be said that it is unique when the number of round triangles ($ d $) is 0. However, if there is one or more, it can be judged that there is no uniqueness, but the significance level of the test is 12% instead of the usual 5%.

Also, it must first be recognized that when $ k ≤ 4 $, even if $ d = 0 $, it does not reach significantly. In other words, even if the number of round triangles is 0, there is no significant ranking, and it should be noted that the test results cannot be effectively used unless the number of samples is at least 5 or more.

a formula

The calculation formula is shown below [^ 2] [^ 3] The number d of round triangles is expressed by the following formula

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

2.png

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

$ k $ indicates the number of samples and $ a_i $ indicates the total number of votes obtained by sample i (see Table 1).

The degree of freedom $ f $ is expressed by the following formula

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

The uniqueness coefficient $ \ zeta $ is expressed by the following formula (when $ k $ is an even number)

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

The uniqueness coefficient $ \ zeta $ is expressed by the following formula (when $ k $ is odd)

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

The chi-square value is expressed by the following formula

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

From the chi-square distribution table (Table 2), for example, it is judged whether the judgment of the subject at the significance level of 5% can be said to be consistent.

Table 2

|Degree of freedom|Significance level.05 |Significance level.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 |

Quote: Takeshi Yamada, Junichiro Murai "Understanding Psychological Statistics" From Appendix 3 [^ 4]

Actually perform data aggregation to uniqueness test using Python3

flow

Sample preparation

Points to keep in mind when preparing the sample ** k represents the number of samples ** As mentioned above,

When $ k = 5 $, it can be said that it is unique when the number of round triangles ($ d $) is 0. However, if there is one or more, it can be judged that there is no uniqueness, but the significance level of the test is 12% instead of the usual 5%.

Also, it must first be recognized that when $ k ≤ 4 $, even if $ d = 0 $, it does not reach significantly. In other words, even if there are no round triangles, there is no significant ranking. Please note that the test results cannot be used effectively unless the number of samples is at least 5 **.

Sample presentation and experiment

When the sample is ready, the sample is verbalized or plotted so that it can be presented as an icon. It is best to make this icon easy for you and the subject to understand, and in the worst case, just arranging the alphabets will function as an icon. (For example, A to F mentioned above) And if one subject makes a paired comparison, it is known that "the judge has no discriminating power" can be rejected at the significance level of 5% when the number of round triangles in Table 3 is $ d $. There is. [^ 5]

Table 3

k 5 or less 6 7 8 9
d Significance level 5%Does not reach 1 or less 3 or less 7 or less 14 or less
{}_kC_3 less than 10 20 35 56 84

Quote: "A Study on Paired Comparison Method in AHP Use of Paired Comparison Method in Sensory Test" from Hiroshi Iida

Data aggregation, output of experimental data obtained by PCPS to csv

Immediately made "Paired comparison method data processing software, codename PCPS" as an experimental assistance application (hereinafter referred to as PCPS)

Experimental flow

実験フロー.png

PCPS source code (paired_comparison_method/select_main.py)

paired_comparison_method/select_main.py


# -*- coding: utf8 -*-
# paired_comparison_method/select_main.py
# made by kazuya yuda.
import time
import datetime
import math
import itertools
import sys
import random
import pandas as pd
import csv

def welcome_mes(): #Message at startup
    print("Welcome to paired comparison method experimental tabulation system ver2:2021,1,3")
    print("Paired comparison method data processing software, PCPS")
    print("made by kazuya yuda.")

def exit_all(): #End processing
    sys.exit()

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

def initial_data_set(): #Data initialization
    data=[] #Data retention in an integrated manner

    print("Enter subject information:",end=" ")
    data.append(input()) #Store subject's name

    print("Remarks:",end=" ")
    data.append(input()) #Store remarks about the subject

    print("Enter the number of samples:",end=" ")
    n = input()
    if int(n) < 2:
        print("Not enough samples.")
        exit_all()
    data.append(int(n)) #Store the number of samples
    print("%s set" % n)
    return data

def material_get(n): #Sample loading
    material=[]
    print("Do you want to read sample information from csv??y,n:", end=" ")
    if input() == n:
        for i in range(int(n)):
            count = i + 1
            print("sample%Enter the information you want to present in 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) #Output header
        count = 0
        for row in f: #Data read
            if count == int(n):
                break
            else:
                count += 1
                print(row)
                material.append(row[1])
    return material

def itertools_make_material(material): #Generation of comparative samples for paired comparison for the number of trials
    itertools_material=[]
    for i in itertools.permutations(material, r=2):
        itertools_material.append(i)
    random.shuffle(itertools_material)
    return itertools_material

def confirmation(data): #Parameter confirmation phase
    print("Check the parameters ↓")
    caption = ["subject","Remarks","Number of samples:","sample:","Number of trials:"]
    for i in range(5):
        print(caption[i],end=' ')
        print(data[i])

def start(): #Experiment start processing
    print("Do you want to start the experiment? y,n:",end=" ")
    if input() == "y":
        print("Start the experiment.")
        print("Start time:")
        print(datetime.datetime.now())
        start_time = datetime.datetime.now()
        return start_time
    else:
        print("Suspend,Please start over.")
        exit_all()
        
def process(itertools_material,main_data,count,i,option): #Main interface
        print(itertools_material[i])
        print("%dth Which sample was selected?:Left → 0,Right → 1,Back → r" % count)
        ans = input()
        if ans == "r" and i != 0 and option != "final":
            print("How many times do you go back?:",end=" ")
            count_val = input()
            if str.isalpha(count_val):
                print("Please enter a number")
                l = process(itertools_material,main_data,count,i,option)
            else:
                count_change = int(count_val)
                if count_change <= 0:
                    print("It cannot exist after the 0th time.")
                    l = process(itertools_material,main_data,count,i,option)
                elif count_change >= count:
                    print("You can't go back more than the current number.Please request again on the finalize screen.")
                    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("Please enter the specified number,First time,You can't go back when finalizing.")
            l = process(itertools_material,main_data,count,i,option)
        return l

def final_process(itertools_material,main_data,countneo,i,option): #Finalize
    print("That is all,Are you sure you want to finish?y,n:",end=" ")
    ans = input()
    if ans == "n" and i != 0 and ans != "0":
        print("How many times do you go back?:",end=" ")
        count_val = input()
        if str.isalpha(count_val):
            print("Please enter a number")
            l = process(itertools_material,main_data,count,i,option)
        else:
            count_change = int(count_val)
            if count_change <= 0:
                print("It cannot exist after the 0th time.")
                l = final_process(itertools_material,main_data,countneo,i,option)
            elif count_change >= countneo:
                print("You can't go back more than the current number.The specified number of times does not exist.")
                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("Start the termination process.")
        print("* Do not interrupt the program during processing")

    return main_data

def process_end(): #End time record
    end_time = datetime.datetime.now()
    return end_time

def result_export_process(data,integration_data,end_time): #Export in CSV format
    print(data[0:])
    print(integration_data[0:])
    Coulum = ['Comparison data left','Comparison data right','Selection result']
    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))

def main(): #Main ops
    welcome_mes() #welcome message generation

    data = initial_data_set() #Store in data after initial data setting

    material = material_get(data[2]) #Store sample information
    data.append(material[0:])

    N = int(data[2]) #Obtaining the number of samples

    try_num = combinations_count(N,2) #Get the number of trials(Calculate combination)
    data.append(try_num)

    itertools_material = itertools_make_material(material) #Shuffle the sample,take out

    confirmation(data) #Parameter confirmation phase

    # start
    data.append(start())
    
    main_data=[] #For storing experimental data for paired comparison
    
    option="none"
    for i in range(try_num): #Iteration for the number of trials
        count = i + 1
        main_data.append(process(itertools_material,main_data,count,i,option))

    #Start 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)

if __name__ == "__main__":
    main()

Also, make a HID device (PCPS_selector) for PCPS using Arduino Leonardo (Pro Micro).

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

スクリーンショット 2021-01-08 22.06.46.png

State to present

HID device (PCPS_selector) PCPS_selector.ino

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);
}

Explanation of "Paired comparison method data processing software, codename PCPS"

Python3 library to use (some need to be installed with pip)

Library import

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

Welcome message display, Here, I think it is good to describe the information you want to display and the confirmation points at the time of the experiment.


def welcome_mes(): #Message at startup
    print("Welcome to paired comparison method experimental tabulation system ver2:2021,1,3")
    print("Paired comparison method data processing software, PCPS")
    print("made by kazuya yuda.")

A subroutine for terminating a program, mainly used to terminate when an error occurs in the parameter confirmation phase.

def exit_all(): #End processing
    sys.exit()

Use the math library to calculate combinations to calculate the number of trials


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

Initial settings such as inputting subject information and checking the number of samples By the way, it should be absolutely validated, but it is not implemented and will be improved if there is room. (Unit test etc. should also be done)


def initial_data_set(): #Data initialization
    data=[] #Data retention in an integrated manner

    print("Enter subject information:",end=" ")
    data.append(input()) #Store subject's name

    print("Remarks:",end=" ")
    data.append(input()) #Store remarks about the subject

    print("Enter the number of samples:",end=" ")
    n = input()
    if int(n) < 2:
        print("Not enough samples.")
        exit_all()
    data.append(int(n)) #Store the number of samples
    print("%s set" % n)
    return data

Read as many samples as there are samples, and if not, generate them. It is also possible to read csv and acquire sample information At that time, it is necessary to prepare sample_info.csv in the same directory. Create a csv with the following contents ↓ (Example: Let's add an alphabetic icon and header of A to F with 6 samples)

sample_info.csv


No,Sample information
1,A
2,B
3,C
4,D
5,E
6,F

Create an array with the name material to store the presented sample icon (will continue to be used)


def material_get(n): #Sample loading
    material=[]
    print("Do you want to read sample information from csv??y,n:", end=" ")
    if input() == n:
        for i in range(int(n)):
            count = i + 1
            print("sample%Enter the information you want to present in 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) #Output header
        count = 0
        for row in f: #Data read
            if count == int(n):
                break
            else:
                count += 1
                print(row)
                material.append(row[1])
    return material

Generation of comparative samples for paired comparison for the number of trials Also, since the sample to be presented needs to be determined randomly, shuffle the material mentioned earlier.


def itertools_make_material(material): #Generation of comparative samples for paired comparison for the number of trials
    itertools_material=[]
    for i in itertools.permutations(material, r=2):
        itertools_material.append(i)
    random.shuffle(itertools_material)
    return itertools_material

Now that all the settings are complete, we will finally set up a phase to check various parameters and subject information. I refactored it later and summarized the caption, but up to the above all parameter information is in the data array When adding a function, you can basically refer to the data array.


def confirmation(data): #Parameter confirmation phase
    print("Check the parameters ↓")
    caption = ["subject","Remarks","Number of samples:","sample:","Number of trials:"]
    for i in range(5):
        print(caption[i],end=' ')
        print(data[i])

Experiment start processing, record time as a role After going through the parameter confirmation phase, we also confirm whether it is really okay to start. By the way, if there is an error in the parameter, the process will be terminated once.


def start(): #Experiment start processing
    print("Do you want to start the experiment? y,n:",end=" ")
    if input() == "y":
        print("Start the experiment.")
        print("Start time:")
        print(datetime.datetime.now())
        start_time = datetime.datetime.now()
        return start_time
    else:
        print("Suspend,Please start over.")
        exit_all()

Phase of actually presenting and processing data It is also possible to respond when an input error occurs (if you make a HID device, you make a mistake in pressing the subject). This is basically achieved by recursion


def process(itertools_material,main_data,count,i,option): #Main interface
        print(itertools_material[i])
        print("%dth Which sample was selected?:Left → 0,Right → 1,Back → r" % count)
        ans = input()
        if ans == "r" and i != 0 and option != "final":
            print("How many times do you go back?:",end=" ")
            count_val = input()
            if str.isalpha(count_val):
                print("Please enter a number")
                l = process(itertools_material,main_data,count,i,option)
            else:
                count_change = int(count_val)
                if count_change <= 0:
                    print("It cannot exist after the 0th time.")
                    l = process(itertools_material,main_data,count,i,option)
                elif count_change >= count:
                    print("You can't go back more than the current number.Please request again on the finalize screen.")
                    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("Please enter the specified number,First time,You can't go back when finalizing.")
            l = process(itertools_material,main_data,count,i,option)
        return l

Finalizing process, by the way, the reason why I wrote the phase is that I made a mistake during the last trial during the experiment, the process ended as it was, the end time could not be recorded correctly, and it was necessary to manually correct csv later. This is because the convenience has been significantly impaired. The basics are the same as the subroutine (process) above.


def final_process(itertools_material,main_data,countneo,i,option): #Finalize
    print("That is all,Are you sure you want to finish?y,n:",end=" ")
    ans = input()
    if ans == "n" and i != 0 and ans != "0":
        print("How many times do you go back?:",end=" ")
        count_val = input()
        if str.isalpha(count_val):
            print("Please enter a number")
            l = process(itertools_material,main_data,count,i,option)
        else:
            count_change = int(count_val)
            if count_change <= 0:
                print("It cannot exist after the 0th time.")
                l = final_process(itertools_material,main_data,countneo,i,option)
            elif count_change >= countneo:
                print("You can't go back more than the current number.The specified number of times does not exist.")
                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("Start the termination process.")
        print("* Do not interrupt the program during processing")

    return main_data

End time record

def process_end(): #End time record
    end_time = datetime.datetime.now()
    return end_time

Output to csv with pandas


def result_export_process(data,integration_data,end_time): #Export in CSV format
    print(data[0:])
    print(integration_data[0:])
    Coulum = ['Comparison data left','Comparison data right','Selection result']
    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))

Finally, call various subroutines in main

def main(): #Main ops
    welcome_mes() #welcome message generation

    data = initial_data_set() #Store in data after initial data setting

    material = material_get(data[2]) #Store sample information
    data.append(material[0:])

    N = int(data[2]) #Obtaining the number of samples

    try_num = combinations_count(N,2) #Get the number of trials(Calculate combination)
    data.append(try_num)

    itertools_material = itertools_make_material(material) #Shuffle the sample,take out

    confirmation(data) #Parameter confirmation phase

    # start
    data.append(start())
    
    main_data=[] #For storing experimental data for paired comparison
    
    option="none"
    for i in range(try_num): #Iteration for the number of trials
        count = i + 1
        main_data.append(process(itertools_material,main_data,count,i,option))

    #Start 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)

By the way, the iteration of process is processed in main.

    option="none"
    for i in range(try_num): #Iteration for the number of trials
        count = i + 1
        main_data.append(process(itertools_material,main_data,count,i,option))

Make a HID device (PCPS_selector) for PCPS using Arduino Leonardo (Pro Micro)

It is very simple to make, and the image of making a keyboard that allows you to enter the numerical values ​​required to select a pair of PCPS presentation samples.

The following numbers are assigned to the selection. Left: 0 Right: 1

I created a device that allows you to enter 0 by pressing the left button and 1 by pressing the right button. It is easier to understand if the buttons are color-coded.

What to prepare

By the way, as a reference, we are the switch science version of Arduino Pro Micro

IMG_6328_500.jpg

The following push button switches are available. mxuteuk 12 pcs 1A 250V AC 2 pin SPST 6 color normal open mini instant push button switch PBS-110-6C

btn.png

See the following page for writing 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);
}

Various calculations from CSV data

** Calculate the number of round triangles $ d $ from csv data ** ** Calculate degrees of freedom $ f $ from csv data ** ** Calculate uniqueness coefficient $ \ zeta $ from csv data ** ** Calculate chi-square value $ \ chi_o ^ 2 $ from csv data ** ** Test uniqueness using the calculated $ \ chi_o ^ 2 $ and the chi-square distribution table (Table 2) **

flow

PCVS source code (paired_comparison_method/vote_aggregate.py)

vote_aggregate.py


# -*- coding: utf8 -*-
# paired_comparison_method/vote_aggregate.py
# made by kazuya yuda.

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

def welcome_mes(): #Message at startup
    print("Welcome to paired comparison method vote counting system ver2:2021,1,3")
    print("paired comparison method vote data aggregate software, PCVS")
    print("made by kazuya yuda.")

def import_csv(): #Sample loading
    material=[]
    info=[]

    #File name confirmation
    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("Enter the main data file name you want to analyze, including the extension")
    filename = input()
    print(filename,end=" ")
    print("Loaded.")
    print("Are you sure you want to start counting?y,n:", end=" ")
    ans = input()
    if ans == "n":
        print("I'm done.")
    elif ans == "y":
        #Import 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:]) #Output header
        for row in f: #Data read
            #print(row[1:])
            material.append(row[1:])

        #Import 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:]) #Output header
        for row in f: #Data read
            #print(row[1:])
            info.append(row[1:])
    else:
        pass

    #Summarize
    material_result=['','']
    material_result[0] = info
    material_result[1] = material

    return material_result

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

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

def zeta_calculation(f,d,info):
    k = get_k(info)
    if k % 2 == 0:
        print("Number of samples k: Even")
        v_1 = 24*d
        v_2 = (k^3) - (4*k)
        v_3 = float(v_1/v_2)
        zeta = 1 - v_3
    else:
        print("Number of samples k: Odd")
        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

def make_vote_list_and_calculation(info,material):
    #info to k,n read
    k = get_k(info)
    n = get_n(info)

    #list Create horizontal axis
    vote_list = []
    for i in range(k+1):
        vote_list.append('')

    #list Create vertical axis
    vote_list_all = []
    for i in range(k+1):
        vote_list_all.append(vote_list)

    #Matrix transformation
    vote_list_all_np = np.array(vote_list_all,dtype=object)

    #Unnecessary line-Insert(For display)
    for i in range(k+1):
        vote_list_all_np[i][i] = '-'

    #The vertical axis icon is larger than the horizontal axis icon(For display)
    vote_list_all_np[0][0] = 'i>j'

    #Presentation sample information extraction(For display)
    get_icon = info[3]
    icon = get_icon[0].replace("[","").replace("]","").replace("\'","").replace("\"","").replace(" ","").split(',')

    #Presented sample information set(For display)
    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

    #Insert vote
    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

    #Vote matrix display
    print("Voting table")
    print(vote_list_all_np)

    #Calculation symbol → 0
    for i in range(k+1):
        vote_list_all_np[i][i] = 0

    #Calculation symbol → 0
    vote_list_all_np[0][0] = 0

    #Presentation sample information for calculation → 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("Vote a_i:",end=" ")
    print(vote_sum)
    print("Vote Σ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

def main():
    welcome_mes()
    material = import_csv()
    info = material[0]
    main = material[1]

    d = make_vote_list_and_calculation(info,main)
    print("Number of round triangles:",end=" ")
    print(d)

    f = f_calculation(info)
    print("Degrees of freedom f:",end=" ")
    print(f)

    zeta = zeta_calculation(f,d,info)
    print("Uniqueness coefficient ζ:",end=" ")
    print(zeta)

    chi_2_0 = chi_2_0_caluculation(f,d,info)
    print("Chi-square value:",end=" ")
    print(chi_2_0)

if __name__ == "__main__":
    main()

It will be displayed like this. 一例.png

By the way, the data in the above figure is generated appropriately by the author to present it as an example. Of course, since the number of d is 1 or more, it can be seen that the answer is not unique.

Commentary

Import of various libraries

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

Import csv file I think there is data for the subjects who completed the experiment, so check the data you want to analyze and enter it. At that time, execute the ls command using subprocess so that the csv file name of main can be confirmed. Filter the standard output and extract only the csv file.

def import_csv(): #Sample loading
    material=[]
    info=[]

    #File name confirmation
    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("Enter the main data file name you want to analyze, including the extension")
    filename = input()
    print(filename,end=" ")
    print("Loaded.")
    print("Are you sure you want to start counting?y,n:", end=" ")
    ans = input()
    if ans == "n":
        print("I'm done.")
    elif ans == "y":
        #Import 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:]) #Output header
        for row in f: #Data read
            #print(row[1:])
            material.append(row[1:])

        #Import 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:]) #Output header
        for row in f: #Data read
            #print(row[1:])
            info.append(row[1:])
    else:
        pass

    #Summarize
    material_result=['','']
    material_result[0] = info
    material_result[1] = material

    return material_result

Load the number of samples k and the number of trials n from info.csv output from PCPS

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

Degree of freedom f calculation


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

The votes are totaled and the number d of the round triangles is calculated. Numpy is used to convert to a matrix at the time of aggregation.

def make_vote_list_and_calculation(info,material):
    #info to k,n read
    k = get_k(info)
    n = get_n(info)

    #list Create horizontal axis
    vote_list = []
    for i in range(k+1):
        vote_list.append('')

    #list Create vertical axis
    vote_list_all = []
    for i in range(k+1):
        vote_list_all.append(vote_list)

    #Matrix transformation
    vote_list_all_np = np.array(vote_list_all,dtype=object)

    #Unnecessary line-Insert(For display)
    for i in range(k+1):
        vote_list_all_np[i][i] = '-'

    #The vertical axis icon is larger than the horizontal axis icon(For display)
    vote_list_all_np[0][0] = 'i>j'

    #Presentation sample information extraction(For display)
    get_icon = info[3]
    icon = get_icon[0].replace("[","").replace("]","").replace("\'","").replace("\"","").replace(" ","").split(',')

    #Presented sample information set(For display)
    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

    #Insert vote
    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

    #Vote matrix display
    print("Voting table")
    print(vote_list_all_np)

    #Calculation symbol → 0
    for i in range(k+1):
        vote_list_all_np[i][i] = 0

    #Calculation symbol → 0
    vote_list_all_np[0][0] = 0

    #Presentation sample information for calculation → 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("Vote a_i:",end=" ")
    print(vote_sum)
    print("Vote Σ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

Finally, calculate $ \ zeta $ and $ \ chi_o ^ 2 $


def zeta_calculation(f,d,info):
    k = get_k(info)
    if k % 2 == 0:
        print("Number of samples k: Even")
        v_1 = 24*d
        v_2 = (k^3) - (4*k)
        v_3 = float(v_1/v_2)
        zeta = 1 - v_3
    else:
        print("Number of samples k: Odd")
        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

Once uniqueness is found, a consistency test can be performed next to test whether the responses between valid subjects match.

I will write a consistency test in the next article. The second "test of consistency in paired comparison method" https://zenn.dev/_kazuya/articles/66ef65022407ef

reference

[^ 1]: "Paired comparison method" Kansei/sensory evaluation system J-SEMS https://j-sems.com/%E4%B8%80%E5%AF%BE%E6%AF%94%E8%BC % 83% E6% B3% 95 /

[^ 2]: "About the seasonal feeling of the three primary colors seen on the PC screen" https://core.ac.uk/download/pdf/233608433.pdf

[^ 3]: Master's thesis "Method of presenting force feedback using pseudo-tactile sensation between augmented reality" Takashi Otsuka, Department of Electrical Engineering, Graduate School of Engineering, The University of Tokyo February 6, 2013

[^ 4]: "Understanding Psychological Statistics" Takeshi Yamada, Junichiro Murai Minerva Shobo

[^ 5]: "A Study on Paired Comparison Process in AHP Use of Paired Comparison Method in Sensory Test" Hiroshi Iida

Recommended Posts

Test of uniqueness in paired comparison method
Summary of test method
Comparison of Japanese conversion module in Python3
Comparison of solutions in weight matching problems
Clustering of clustering method
Speed comparison of each language by Monte Carlo method
A quick comparison of Python and node.js test libraries
parallelization of class method
Comparison of LDA implementations
Comparison of fitting programs
Simplex method (simplex method) in Python
Private method in python
Comparison of how to use higher-order functions in Python 2 and 3
Comparison of color detection methods in OpenCV inRange, numpy, cupy
Comparison of data frame handling in Python (pandas), R, Pig