LoginSignup
0
0

More than 1 year has passed since last update.

scipy.stats: 適合度の検定 chisquare

Posted at

scipy.stats: 適合度の検定 chisquare

Help では "Calculates a one-way chi square test" と書いているが,一般的には適合度の検定である。1

カテゴリーデータの集計の結果,度数分布が特定の分布にしたがっているかどうかの検定である。

特定の分布としては,

  • 一様分布
    帰無仮説:各カテゴリーの頻度は同じである $H_0: p_i = 1/k,\ \ i=1,2,\dots,k$
  • その他の分布
    帰無仮説:各カテゴリーの頻度は特定の割合(母比率 $\pi_i$)である $H_0: p_i = \pi_i,\ \ \sum \pi_i = 1,\ \ i=1,2,\dots,k$
from scipy.stats import chisquare

chisquare(f_obs, f_exp=None, ddof=0, axis=0)

  • f_obs は観察度数
  • f_exp は期待度数。サンプルサイズ=$n$,カテゴリー数=$k$ のとき,デフォルトで,すべてのカテゴリーの期待度数は $n/k$ に等しい。
  • ddof 自由度は $k-1$ から更に余分に減少すべき自由度。デフォルトは 0。

1. 一様分布に従うか?

1.1. カテゴリー数が 3 以上の場合

よくある例で,サイコロを振ったときに 1〜6 の目が同じ頻度で出るかの検定である。

普通のサイコロのデータではありふれているので,ここでは現実世界には存在しない正 10 面体のサイコロを使ってみる。

例題: 正 10 面体のサイコロを 500 回振って,出た目を記録した。1 〜 10 の目は均等に出ているといえるか?

import numpy as np
np.random.seed(123)
x = np.random.randint(1, 11, 500) # 1 ~ 10 の整数乱数を 500 個生成
print(*x)
3 3 7 2 4 10 7 2 1 2 10 1 1 10 4 5 1 1 5 2 8 4 3 5 8 3 5 9 1 8 10 4 5 7 2 6 7 3 2 9 4 6 1 3 7 3 5 5 7 4 1 7 5 8 7 8 2 6 8 10 3 5 9 2 3 2 2 4 6 10 1 9 2 7 4 4 6 10 8 10 3 4 4 4 9 7 10 8 7 4 10 7 7 7 2 4 5 4 2 1 6 9 7 9 10 2 1 4 2 4 5 8 7 2 5 4 4 8 7 9 7 5 5 8 1 1 10 9 9 5 9 7 2 7 9 8 10 2 8 2 8 10 9 8 2 4 2 9 8 6 2 3 6 3 3 10 4 3 7 8 10 2 4 9 4 8 10 10 4 4 6 7 1 9 8 8 5 5 6 1 9 10 3 6 2 6 10 3 5 4 1 4 8 8 3 6 2 8 6 10 2 3 9 6 1 10 4 4 4 2 8 7 4 2 8 3 7 6 10 9 10 5 8 9 6 10 3 5 4 4 2 8 9 6 8 8 8 6 1 1 9 2 1 5 6 5 6 1 8 2 3 3 8 8 4 7 2 9 5 2 7 6 10 1 4 2 4 8 8 8 6 4 7 4 6 2 6 10 2 4 10 2 6 7 2 6 1 8 1 4 1 7 3 8 6 2 1 1 3 7 6 10 2 6 4 7 5 8 2 7 1 4 2 4 1 9 6 2 5 4 3 3 1 3 1 3 8 10 1 4 10 8 1 6 5 7 1 8 9 9 2 1 2 8 6 4 1 5 9 5 10 4 6 2 9 1 10 2 6 9 9 5 7 9 8 10 3 1 1 10 10 6 4 6 6 3 3 8 4 2 4 10 4 7 3 4 2 10 9 1 3 4 8 10 3 8 10 8 2 5 8 5 8 9 1 3 8 5 4 8 4 8 1 8 7 9 7 9 1 6 4 3 5 4 4 10 8 2 9 1 2 5 8 3 5 9 4 5 10 7 3 8 8 8 5 8 9 5 1 8 7 4 5 2 2 8 3 8 9 10 5 5 5 8 8 10 10 5 2 6 6 9 10 6 2 1 7 1 10 5 7 5 6 4 5 7 3 6 3 5 4 3 5 9 1 7 9 9 6 7

集計結果は以下のようになる。

counts = np.unique(x, return_counts=True)
counts
(array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]),
 array([48, 56, 42, 62, 48, 45, 44, 65, 43, 47]))
f = counts[1] # 観察度数
f
array([48, 56, 42, 62, 48, 45, 44, 65, 43, 47])
from scipy.stats import chisquare
chisquare(f)
Power_divergenceResult(statistic=11.92, pvalue=0.21785699690374188)

帰無仮説は $H_0: p_i = 1/k,\ \ i=1,2,\dots,k$ である。
すなわち「1〜10 の出方は同じである」ということ。

有意水準 5% で検定するとき,$p$ 値が 0.21786 なので,帰無仮説を棄却できない。すなわち,「出た目の頻度は一様でないとはいえない」ということである。

間違っても「出た目の頻度は等しい」といってはいけない

なお,分析結果の名前が Power_divergenceResult になっていることからも判るが,実際は  power_divergence が使われている。

from scipy.stats import power_divergence
power_divergence(f)
Power_divergenceResult(statistic=11.92, pvalue=0.21785699690374188)

1.2. カテゴリー数が 2 の場合

例えば,コイントスで表と裏が等確率(すなわち 0.5 ずつ)であるかのような場合にも,chisquare を使うことはできるが,二項検定 binom_test によれば,正確な検定(exact test)を行うことができる。

例題: コインを 60 回投げて,表が 38 回,裏が 22 回出た。このコインの表・裏が出る確率はそれぞれ 0.5 であるといえるか?

from scipy.stats import chisquare
chisquare([38, 22], [30, 30])
Power_divergenceResult(statistic=4.266666666666667, pvalue=0.03886710381241731)
from scipy.stats import binom_test
binom_test(38, 60)
0.051893895928921435

漸近検定である chisquare では,有意水準 5% で帰無仮説は棄却された($p = 0.0389$)。しかし,正確な検定である binom_test では帰無仮説は棄却されなかった($p = 0.519$)。

なお,カテゴリー数が 2 の場合には二項分布に基づく正確な二項検定があるが,カテゴリー数が 3 以上の場合にも多項分布に基づく正確な検定がある(scipy.stats には用意されていない)。

2. 特定の理論分布に従うか。

これもよくある例で,交配実験結果がメンデルの法則にしたがうかというのがある。メンデルの法則を知っている必要はない。

例題: 4 つのカテゴリーの頻度が,9:3:3:1 になるはずのところ,観察度数が 91, 25, 34, 7 であった。これは理論分布にしたがっているといえるだろうか?

サンプルサイズは sum([91, 25, 34, 7])=157,期待度数は 157 * np.array([9, 3, 3, 1])/16) である。

f = [91, 25, 34, 7]
e = 157 * np.array([9, 3, 3, 1])/16
print(*e)
88.3125 29.4375 29.4375 9.8125
chisquare(f, e)
Power_divergenceResult(statistic=2.2639773531493277, pvalue=0.5194569931782831)

帰無仮説は確率でいえば $H_0: p_1 = 9/16,p_2=3/16, p_3=3/16, p_4=1/16$ である。
あるいは頻度でいえば $H_0: f_1 = 157\cdot9/16,f_2=157\cdot3/16, f_3=157\cdot3/16, f_4=157\cdot1/16$ である。
有意水準 5% で検定するとき,$p$ 値が 0.51946 なので,帰無仮説を棄却できない。すなわち,「頻度は理論分布にしたがっていないとはいえない」ということである。

間違っても「理論分布にしたがっている」といってはいけない

3. データから理論分布のパラメータを推定して適合度の検定を行う

たとえばポアソン分布にしたがうと推定されるが,ポアソン定数はデータから推定する必要がある場合である。

例題: 観察度数は,階級 0 〜 10 に対して, [27, 61, 77,71,54, 35, 20, 11, 6, 2, 1] であった。ポアソン分布にしたがっているといえるだろうか?

ポアソン定数 $\lambda$ は,データの平均値に一致する。

import numpy as np
i = np.arange(0, 11)
frequency = np.array([27, 61, 77,71,54, 35, 20, 11, 6, 2, 1])
λ = sum(i * frequency) / sum(frequency)
print(λ)
2.9917808219178084

期待値 expected は ポアソン分布の確率関数
$\displaystyle p(x) = \frac{\lambda^x\ e^{-\lambda}}{x!}$
で求める。

from math import exp, factorial
k = len(frequency)
probability = np.array([λ**z * exp(-λ) / factorial(z) for z in range(k)])
probability[k-1] = 1 - sum(probability[0:k-1])
n = sum(frequency)
expected = n * probability

 ここまでを表にしてみよう。

print("number of class =", k, ",  total sample size =", n)
print('\n i  frequency  probability    expected')
for i in range(k):
    print(f"{i:2d}  {frequency[i]:9d}  {probability[i]:11.7f}  {expected[i]:10.7f}")
number of class = 11 ,  total sample size = 365

 i  frequency  probability    expected
 0         27    0.0501980  18.3222567
 1         61    0.1501813  54.8161761
 2         77    0.2246548  81.9989922
 3         71    0.2240393  81.7743374
 4         54    0.1675691  61.1627236
 5         35    0.1002660  36.5970927
 6         20    0.0499957  18.2484133
 7         11    0.0213680   7.7993219
 8          6    0.0079910   2.9167327
 9          2    0.0026564   0.9695805
10          1    0.0010805   0.3943730

適合度検定の際に,下側,上側のいくつかの階級の期待値が 1 未満の場合にはそれぞれ下側,上側から期待値が1以上になるように階級を合併する。

i = 9, 10 の階級の期待度数が 1 未満なので合併する。

frequency[k-2] += frequency[k-1]
probability[k-2] += probability[k-1]
expected[k-2] += expected[k-1]
k = k-1
frequency = frequency[:k]
probability = probability[:k]
expected = expected[:k]

ここまでを表にしてみよう。

print(' i  frequency  probability    expected')
for i in range(k):
    print(f"{i:2d}  {frequency[i]:9d}  {probability[i]:11.7f}  {expected[i]:10.7f}")
 i  frequency  probability    expected
 0         27    0.0501980  18.3222567
 1         61    0.1501813  54.8161761
 2         77    0.2246548  81.9989922
 3         71    0.2240393  81.7743374
 4         54    0.1675691  61.1627236
 5         35    0.1002660  36.5970927
 6         20    0.0499957  18.2484133
 7         11    0.0213680   7.7993219
 8          6    0.0079910   2.9167327
 9          3    0.0037369   1.3639535

どの階級も,期待値が 1 以上になったので,検定を実行できるようになった。

自由度の設定において,データから $\lambda$ を推定したので,自由度は余分に 1 少なくなるので ddof=1 を指定する。

from scipy.stats import chisquare
chisquare(frequency, expected, ddof=1)
Power_divergenceResult(statistic=14.143749368122329, pvalue=0.0780947329266041)

$p > 0.05$ なので,帰無仮説は棄却できない($p = 0.078$)

4. 無理やり応用して,クロス集計表の独立性の検定

例題: 以下のようなクロス集計表で,独立性の検定を行う。

tbl = np.array([[24, 13, 7], [5, 30, 19]])
普通は `scipy.stats.chi2_contingency` を使う
from scipy.stats import chi2_contingency
chi2_contingency(tbl)
(23.93649410229202,
 6.342439710555313e-06,
 2,
 array([[13.02040816, 19.30612245, 11.67346939],
        [15.97959184, 23.69387755, 14.32653061]]))

chisquare で行う場合は,tbl の期待値を scipy.stats.contingency.expected_freq で求めて,

from scipy.stats.contingency import expected_freq
e = expected_freq(tbl)
e
array([13.02040816, 19.30612245, 11.67346939, 15.97959184, 23.69387755,
       14.32653061])
`ddof` を適切に指定すれば`chi2_contingency` と同じ結果を得ることができる

`tbl`  `e`  `np.ravel()` で一次元配列ベクトルにして使っているところに注目 
k, l = tbl.shape
chisquare(np.ravel(tbl), np.ravel(e), ddof=k+l-2)
Power_divergenceResult(statistic=23.93649410229202, pvalue=6.342439710555313e-06)
  1. scipy.stats.binom_test, scipy.stats.power_divergence, scipy.stats.chi2_test との関連についても述べる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0