Up

度数の分析

カイ二乗検定とベイズ分析

 

度数の検定において適合度検定クロス表(Contingency Table)の検定のためのPythonスクリプト例を用意した。スクリプトファイルは圧縮ファイルFrequencies.zipにまとめた。Jupyter Notebook用にまとめたものも、圧縮ファイルFrequencies_jn.zipとして用意した。それぞれダウンロード・解凍して自由に利用されたい。

なお、ベイズ分析の例も上げておいた。

 

 

適合度検定

 

観測度数の分布の確率分布モデル(一様分布)への適合度の検定のためのスクリプト例を作成した。

サイコロを50回投げて表A.1に示す結果を得たとする。

A.1  サイコロ投げの結果

1の目

2の目

3の目

4の目

5の目

6の目

度数

9

11

9

6

8

7

 

A.1のデータを図A.1に示すCSVファイルとした。

A.1

 

A.1CSVファイルTestUniFreq.csvの形式は、1行目にカテゴリ名を書き、その下に度数を書くものである。

A.1のファイルを入力として適合度検定を行うスクリプトをリストA.1のように用意した。

リストA.1のスクリプトを実行すると、入力データファイル名の設定が求められる。

例として、図A.1のファイルTestUniFreq.csvを設定する。

 

File Name = TestUniFreq.csv

 

ファイル名を設定すると、ファイルが読み込まれて、図A.2のグラフが表示される。

A.2

 

グラフのタイトル部にカイ2乗の値、自由度、p値が表示されている。

グラフには、一様分布のときの確率が赤の直線で、データの度数の相対頻度が青の折れ線で表示されている。

グラフのWindowを閉じると、端末には以下の出力が表示されている。

 

 

cat_names =

 ['1の目' '2の目' '3の目' '4の目' '5の目' '6の目']

data =

 [ 9 11  9  6  8  7]

chi_squared =  1.84

p-value =  0.870811402015331

df =  5

FigFit.png was saved.

 

 

 

リストA.1 適合度検定のスクリプト

 

import pandas as pd

import numpy as np

import scipy.stats as ss

import matplotlib. pyplot as plt

import japanize_matplotlib

 

fin_nm = input('File Name = ')

d_frm = pd.read_csv(fin_nm, encoding='shift-jis')

cat_names = d_frm.keys().values

data = d_frm.values[0]

print('cat_names =\n', cat_names)

print('data =\n', data)

chi2, p = ss.chisquare(data)

print('chi_squared = ', chi2)

print('p-value = ', p)

df = len(data) - 1

print('df = ', df)

N = np.sum(data)

n_cat = len(cat_names)

p_uni = np.ones(n_cat) / n_cat

plt.title(f'$\\chi^2$={chi2:.3f},  df={df},  p-value={p:.5f}')

plt.plot(cat_names, data / N, label='Observed')

plt.plot(cat_names, np.ones(n_cat) * p_uni, label='Expected')

plt.xticks(rotation=45)

plt.yticks([0, 0.5, 1])

plt.ylabel('Probability')

plt.legend()

plt.tight_layout()

plt.savefig('FigFit.png')

plt.show()

print('FigFit.png was saved.')

 

 

 

 

クロス表の検定

 

2×2のクロス表のFisher’s Exact検定については、このウェブサイトで扱っている。

 

B.1は京都大学学部入学者の平成31年度の男女別データであある。

 

B.1 平成31年度男女別合格者(京都大学ホームページより)

合計が200名以上の学部、内数で「認」と「他」を除く)

 

文学部

法学部

理学部

工学部

農学部

男性

130

236

281

858

189

女性

83

75

32

79

103

 

B.1のデータを、図B.1に示すCSVファイルDataCrossTable.csvとした。

B.1

 

1行目2列目から列項目のカテゴリ名を書き、1列目2行目から行項目のカテゴリ名を書いている。列カテゴリと行カテゴリの組み合わせのセルに度数を書いている。

B.1の形式のCSVファイルを読み込んで分析するスクリプトをリストB.1のように作成した。

リストB.1のスクリプトを実行すると、入力データファイル名の設定が求められる。

例として、図B.1のファイル名DataCrossTable.csvを設定する。

 

File Name(*.csv) = DataCrossTable.csv

 

ファイル名を設定すると、ファイルが読み込まれて、図B.2のグラフが表示される。

B.2

 

グラフのタイトル部に、カイ2乗の値、自由度、p値が表示されている。

グラフは、 を表し、カイ2乗値を計算するときの和に現れる各項の平方根をデータ値と期待値の差の符号に合わせたものである。クロス表の検定対象である交互作用が視覚的に表されている。

B.2Windowを閉じると、プログラムの実行終了となる。端末画面には、以下の出力が表示されている。

 

 

 

File Name(*.csv) = DataCrossTable.csv

row_cat =

 ['男性' '女性']

col_cat =

 ['文学部' '法学部' '理学部' '工学部' '農学部']

data =

 [[130 236 281 858 189]

 [ 83  75  32  79 103]]

chi-squared =  201.2522256170101

p-value =  2.02134193518844e-42

df =  4

Expected <--> Data =

 

[174.64762827 255.00193611 256.64181994 768.28557599 239.42303969]    [130 236 281 858 189]

[ 38.35237173  55.99806389  56.35818006 168.71442401  52.57696031]    [ 83  75  32  79 103]

data - expected =

 

  -44.6       -19.0       24.4        89.7        -50.4    

  44.6        19.0        -24.4       -89.7       50.4     

(data - expected) / expected**0.5 =

 

  -0.3        -0.1        0.1         0.1         -0.2     

  1.2         0.3         -0.4        -0.5        1.0      

FigCrossTable.png was saved.

 

 

 

 

リストB.1 クロス表の分析

 

import pandas as pd

import numpy as np

import scipy.stats as ss

import matplotlib.pyplot as plt

import japanize_matplotlib

 

fin_nm = input('File Name(*.csv) = ')

d_frm = pd.read_csv(fin_nm, encoding='shift-jis')

row_cat = d_frm.values[:,0]

col_cat = d_frm.keys().values[1:]

print('row_cat =\n', row_cat)

print('col_cat =\n', col_cat)

data = np.array(d_frm.values[:,1:], dtype=int)

print('data =\n', data)

chi2, p, df, expt = ss.chi2_contingency(data)

print('chi-squared = ', chi2)

print('p-value = ', p)

print('df = ', df)

print('Expected <--> Data =\n')

for e, d in zip(expt, data):

    print(e, '  ', d)

data = np.array(data)

dif = data - expt

print('data - expected =\n')

for v in dif:

    for v1 in v:

        print('  {0:<10.1f}'.format(v1), end = '')

    print()

dif_r = dif / expt

dif_r0p5 = dif / (expt**0.5)

print('(data - expected) / expected**0.5 =\n')

for v in dif_r:

    for v1 in v:

        print('  {0:<10.1f}'.format(v1), end = '')

    print()

 

for i in range(len(row_cat)):

    plt.plot(col_cat, dif_r0p5[i], label=row_cat[i])

plt.ylabel('$(O-E)/\\sqrt{E}$')

plt.title(f'$\\chi^2$={chi2:.3f},  df={df},  p={p:.5f}')

plt.plot(col_cat, np.zeros(len(col_cat)), '--', c='k', label='O-E=0')

plt.legend()

plt.tight_layout()

plt.savefig('FigCrossTable.png')

plt.show()

print('FigCrossTable.png was saved.')

 

 

 

なお、クロス表作成の簡単なスクリプト例のウェブサイトも用意した。

 

 

 

 

ベイズ分析例

 

B.1のデータに各学部の女子学生の確率を2項分布を適用してベイズ推定してみた。

Stanスクリプトは、以下のようである。

 

data {

    int bun_m;

    int bun_f;

    int hou_m;

    int hou_f;

    int ri_m;

    int ri_f;

    int kou_m;

    int kou_f;

    int nou_m;

    int nou_f;

}

parameters {

    real<lower = 0.0, upper = 1.0> bun_p;

    real<lower = 0.0, upper = 1.0> hou_p;

    real<lower = 0.0, upper = 1.0> ri_p;

    real<lower = 0.0, upper = 1.0> kou_p;

    real<lower = 0.0, upper = 1.0> nou_p;

}

model {

    bun_f ~ binomial(bun_f + bun_m, bun_p);

    hou_f ~ binomial(hou_f + hou_m, hou_p);

    ri_f  ~ binomial(ri_f + ri_m, ri_p);

    kou_f ~ binomial(kou_f + kou_m, kou_p);

    nou_f ~ binomial(nou_f + nou_m, nou_p);

}

 

 

このStanスクリプト(FreqBin.stan)を用いて、以下のPythonスクリプトで分析した。

 

import matplotlib.pyplot as plt

import japanize_matplotlib

import pystan

import seaborn as sb

 

Data = {'bun_m': 130, 'bun_f': 83,

        'hou_m': 236, 'hou_f': 75,

        'ri_m':  281, 'ri_f':  32,

        'kou_m': 858, 'kou_f': 79,

        'nou_m': 189, 'nou_f': 103}

 

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

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

print(fit)

 

sb.kdeplot(fit['bun_p'], label = '文学部')

sb.kdeplot(fit['hou_p'], label = '法学部')

sb.kdeplot(fit['ri_p'], label = '理学部')

sb.kdeplot(fit['kou_p'], label = '工学部')

sb.kdeplot(fit['nou_p'], label = '農学部')

plt.xlabel('P(女子学生)', fontsize = 14)

plt.yticks([])

plt.legend(fontsize = 14)

plt.show()

 

スクリプトファイルは圧縮ファイルFreqBinomial.zipにまとめたので、ダウンロード解凍すれば自由に利用できる。

モジュールjapanize_matplotlibは、グラフ表示で日本語が使えるようにするものである。Anacondacondaコマンドではインストールできなかった(2020.06.14)が、pipコマンドでインストールできた。

上のスクリプトを実行すると下図のグラフが表示される。

文学部と農学部、法学部、工学部と理学部の3グループに分かれていることが表されている。

 

 

*****

度数データのをポアッソン回帰モデルによる分析例を<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>の第12章、第15章で、度数/比率データのロジスティック回帰モデルによる分析例を<岡本安晴「心理学データ分析と測定」勁草書房>の第4章で説明している。分析用プログラムは、それぞれのウェブサイトから自由にダウンロードできる。

 

Up