morikomorou’s blog

自分が学んだことなどの備忘録的なやつ

【python】データが正規分布に従うかどうかの確認(後編)


はじめに

前回の記事でデータが正規分布に従っているかどうか判断する方法について説明しました。

今回はシャピロウィルク検定及びコルモゴロフスミルノフ検定について説明します。
統計検定を使った手法なので、正規分布に従うかどうかを目視ではなく定量的に判定が可能です。




データの正規性の確認方法

以下の方法が主に使われます。

  • ヒストグラムを目視
  • 歪度、尖度を確認
  • Q-Qプロットで確認
  • シャピロウィルク検定
  • コルモゴロフスミルノフ検定

今回はシャピロウィルク検定、コルモゴロフスミルノフ検定について説明します。

サンプルデータは前回同様下記の4つのデータを使います。

  • ①正規分布に従うデータ
  • ②ログ正規分布に従うデータ
  • ③正規分布に従う2つの母集団からサンプリングしたデータ
  • ④一様分布に従うデータ

データは以下コードで準備します、ヒストグラムも作っておきます。

import numpy as np
from scipy.stats import norm, lognorm, uniform
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

# ①正規分布に従うサンプル
data1 = norm.rvs(loc=7.5, scale=1, size=100)

# ②ログ正規分布に従うサンプル
data2 = lognorm.rvs(0.95, loc=5, scale=1, size=100)

# ③2つの母集団からのサンプル
data31 = norm.rvs(loc=10, scale=1, size=70)
data32 = norm.rvs(loc=6, scale=1.5, size=30)
data3 = np.concatenate([data31, data32])

# ④一様分布に従うサンプル
data4 = uniform.rvs(loc=5,scale=8,size=100)
fig, ax = plt.subplots(2, 2)
ax[0, 0].hist(data1, density=True)
ax[0, 0].set_xlabel('x')
ax[0, 0].set_ylabel('density')
ax[0, 0].set_title('1. Normal')
ax[0, 0].set_xlim(0, 15)

ax[0, 1].hist(data2, density=True)
ax[0, 1].set_xlabel('x')
ax[0, 1].set_ylabel('density')
ax[0, 1].set_title('2. Log Normal')
ax[0, 1].set_xlim(0, 15)

ax[1, 0].hist(data3, density=True)
ax[1, 0].set_xlabel('x')
ax[1, 0].set_ylabel('density')
ax[1, 0].set_title('3. Sample from two population')
ax[1, 0].set_xlim(0, 15)

ax[1, 1].hist(data4, density=True)
ax[1, 1].set_xlabel('x')
ax[1, 1].set_ylabel('density')
ax[1, 1].set_title('4. Uniform')
ax[1, 1].set_xlim(0, 15)

plt.tight_layout()
plt.show()

結果は以下の通り。




シャピロウィルク検定を実施

シャピロウィルク検定とは、データが正規分布に従う(正規分布からサンプリングされたものである)という帰無仮説を検定する手法です。
この検定で得られるp値は、正規分布と仮定した際にこのサンプルデータを取りうる確率を示しており、
p値が低いほどありえないことが起きた⇒正規分布しているとは言えない
という結果が得られます。
p値の閾値は優位水準と呼ばれ5%がよく使われます。
実際にやってみましょう。
scipy.statsに関数が用意されてますので1行実装可能です。

from scipy.stats import shapiro
print('data1: ', shapiro(data1))
print('data2: ', shapiro(data2))
print('data3: ', shapiro(data3))
print('data4: ', shapiro(data4))

結果は以下の通りです。

data1:  ShapiroResult(statistic=0.991529107093811, pvalue=0.7861605882644653)
data2:  ShapiroResult(statistic=0.7282731533050537, pvalue=2.59088995917256e-12)
data3:  ShapiroResult(statistic=0.8764220476150513, pvalue=1.2845382002524275e-07)
data4:  ShapiroResult(statistic=0.9559363126754761, pvalue=0.002085518790408969)

正規分布に従う①のデータのp値は0.79であり、0.05より大きいので正規分布に従うと言えそうです。
逆に、それ以外の②~④では、p値が0.05を下回っているため、正規分布ではないと言えます。

コルモゴロフスミルノフ検定を実施

コルモゴロフスミルノフ検定では、二つの母集団の確率分布が異なるものであるかどうかを検定する手法です。
こちらも同様に、p値が低いほどありえないことが起きた⇒正規分布しているとは言えない
という結果が得られます。

コルモゴロフスミルノフ検定では任意の分布との検定が可能です。
実際にやってみましょう。

こちらもscipy.statsに関数が用意されてますので1行実装可能です。
引数に'norm'と入れると正規分布との検定を行えます。

from scipy.stats import kstest
print('data1: ', kstest(data1, 'norm'))
print('data2: ', kstest(data2, 'norm'))
print('data3: ', kstest(data3, 'norm'))
print('data4: ', kstest(data4, 'norm'))

結果は以下の通り

data1:  KstestResult(statistic=0.999998936705918, pvalue=0.0)
data2:  KstestResult(statistic=0.9999998028216364, pvalue=0.0)
data3:  KstestResult(statistic=0.998216801955008, pvalue=2.6362581116987133e-275)
data4:  KstestResult(statistic=0.9999997175116764, pvalue=0.0)

なぜかよくわかりませんが、p値がすべて0になってしまいました。
①のデータは正規分布に従うデータのはずなのに優位水準を超えません。

比較したのが標準正規分布だからなのでしょうか?
scipy.stats.kstestでは下記のようにして平均、標準偏差を指定できるのでサンプルデータの平均と、標準偏差を入れて結果を見てみましょう。

print('data1: ', kstest(data1, norm(loc=data1.mean(), scale=data1.std()).cdf))
print('data2: ', kstest(data2, norm(loc=data2.mean(), scale=data2.std()).cdf))
print('data3: ', kstest(data3, norm(loc=data3.mean(), scale=data3.std()).cdf))
print('data4: ', kstest(data4, norm(loc=data4.mean(), scale=data4.std()).cdf))

結果は以下。

data1:  KstestResult(statistic=0.07743865803251226, pvalue=0.5598181810551678)
data2:  KstestResult(statistic=0.29396415882567184, pvalue=3.750826867998532e-08)
data3:  KstestResult(statistic=0.13634225058651606, pvalue=0.04401657777972734)
data4:  KstestResult(statistic=0.08291193371858241, pvalue=0.4725522370259)

④の一様分布のデータもp値が0.05を超え、正規分布という判定になってしまいましたが、①の正規分布に従うデータはちゃんと正規分布であると判断できそうですね。
あまりよくわかりませんでしたが、コルモゴロフスミルノフ検定を行う場合は平均と標準偏差を指定したほうがいいのかもしれません。
もう少しこちらに関しては調査、勉強進めます。

終わりに

統計検定を用いて、定量的に正規分布に従うかどうか判断する方法について説明しました。
今回のデータの場合はシャピロウィルク検定のほうがよさそうですね。
データの処理をやる前にまずは、正規分布かどうか調べてみてはどうでしょうか?

pythonと統計学の勉強について

PyQというオンライン学習サービスがおすすめです。

pythonや統計学の入門から、Webアプリの作成、機械学習までたくさんのコースがあり、月3000円ちょいで全コース学べます。
ブラウザ上でコードを書いて学習ができるので面倒な環境設定も不要です。
学べることが非常に多いので一度見てみてはいかがでしょうか?