Pythonによる仮説検定の実行方法
Pythonで仮説検定を行う方法です。
t検定、ウィルコクソンの符号順位検定、ウィルコクソンの順位和検定(マン・ホイットニーのU検定)、カイ二乗検定についてご紹介しています。
どういう時にどの手法を使えば良いのかについては、「仮説検定の使い分け方」の記事に詳しく整理しています。
使用するデータの読み込み
以下の、とあるクラスの体力測定の結果を使い、様々な手法で「有意差」があるか調べていきます。
「反復横跳び」と「腕立て伏せ」は2回づつ。
「バク転」は、できるかできないかだけ調べた形になっています。
※普通、体力測定でバク転なんてやりませんが、例題なのでご容赦を・・・
また、今回は、すべてscipyライブラリのstatsクラスを使用していきますので、それも以下のようにインポートしておきます。
1 2 3 4 5 |
import pandas as pd import numpy as np from scipy import stats df = pd.read_csv("体力測定.csv",index_col="名前") |
t検定
まずは反復横跳びの結果に着目してみましょう。
一般的に、多くの体力測定データは正規分布に従っているようなので、t検定を適用します。
(より厳密にやるなら、正規分布かどうかの検定を実施して、t検定が相応しいかどうかを確認してから実行する方が良いでしょう。)
t検定はExcelでも出来ますので、そちらは別の記事に整理しています。
1変数のt検定
このクラスの反復横跳びの回数が、一般的な世間の平均回数と異なっているかどうか調べてみましょう。
一般的な反復横跳びの回数が55回であるとした時、このクラスは「一般的な回数」と比べて有意差があるでしょうか。
このような、1変数のt検定にはttest_1samp関数が使えます。
1 |
stats.ttest_1samp(df["反復横跳び(1回目)"], popmean=55) |
1 |
Ttest_1sampResult(statistic=0.3676073110469039, pvalue=0.7172290978369188) |
p値は約0.72ということで、2つの変数に「有意差があるとはいえない」という結果になりました。
ちなみに、この関数で得られるp値は両側検定によるものとなっています。
片側検定によるp値が欲しければ、この値を半分にすれば得られます。
有意差が欲しいからといって勝手に片側検定にしてはいけません。例えば、「このクラスは一般平均より高い事は分かっている」というように大小関係が決まっている場合は片側検定でOKですが、「どちらが大きいか分からない」なら両側検定を使います。
対応する2変数のt検定
続いては、反復横跳びは1回目と2回目でできる回数は変わるのか調べてみましょう。
2回目の方が疲れて回数が減るのか、むしろ身体が慣れて増えるのか、それとも2回くらいでは変わらないのでしょうか。
このような対応する2変数のt検定にはttest_rel関数を使います。
1 |
stats.ttest_rel(df["反復横跳び(1回目)"], df["反復横跳び(2回目)"]) |
1 |
Ttest_relResult(statistic=3.0455242459636036, pvalue=0.006654790139035558) |
p値は約0.001ということで、こちらは有意差があると言えそうです。
対応しない2変数のt検定
お次は、できる回数が男女で異なるのか調べてみましょう。
人数が違うので、対応無しのt検定を使う必要があります。
対応なしのt検定にはttest_ind関数を使います。
数値はとりあえず1回目のものを使ってみます。
1 |
stats.ttest_ind(df[df["性別"]=="男性"]["反復横跳び(1回目)"], df[df["性別"]=="女性"]["反復横跳び(1回目)"]) |
1 |
Ttest_indResult(statistic=1.4684577698871681, pvalue=0.15923896868245274) |
また、対応のないt検定では2つの変数の分散が同程度か、異なるのかによって適切な方法が異なります。
同程度の場合はそのまま使えばOKですが、異なる場合は、引数に「equal_var=False」を追加します。
分散が同程度と見なせるかどうかには次に紹介するF検定を使用します。
F検定
2つの変数の分散が同程度か、異なるのかを統計的に調べるにはF検定をします。
これにはbartlett関数を使います。
1 |
stats.bartlett(df[df["性別"]=="男性"]["反復横跳び(1回目)"], df[df["性別"]=="女性"]["反復横跳び(1回目)"]) |
1 |
BartlettResult(statistic=1.5881897459271868, pvalue=0.2075849593836162) |
p値は約0.208ということで、2つの変数の分散に「有意差があるとはいえない」・・・つまり分散は同程度と考えられると出ました。
よって、先ほどのt検定の引数にequal_var=Falseは不要だったという事になります。
ウィルコクソンの順位符号検定
さて、お次は腕立て伏せについて、1回目と2回目で回数は変わるのか調べてみましょう。
腕立て伏せは反復横跳びとは少し傾向が違います。
実際にデータを見てわかるように、1回も出来ない人も多く、できる人はめちゃくちゃできたりします。
よって、これは正規分布とみなせそうにありません。
このように、「対応するデータ」で、かつ正規分布を仮定できない時にはノンパラメトリック検定の「ウィルコクソンの順位符号検定」を使用します。
こちらはstatsライブラリに用意されているwilcoxon関数を使います。
1 |
stats.wilcoxon(df["腕立て伏せ(1回目)"], df["腕立て伏せ(2回目)"]) |
1 |
WilcoxonResult(statistic=5.0, pvalue=0.020889731442320328) |
p値は約0.021・・・つまり有意差があるようです。
wilcoxonメソッドは、引数alternative=’greater’を指定することにより片側検定の結果が得られます。
ウィルコクソンの順位和検定
では、腕立て伏せのできる回数が男女で異なるのか調べてみましょう。
データ数が異なるのでウィルコクソンの順位符号検定は使えません。
こういった対応のない場合は、ウィルコクソンの順位和検定(マン・ホイットニーのU検定)を用います。
statsライブラリに用意されているmannwhitneyu関数を使います。
1 |
stats.mannwhitneyu(df[df["性別"]=="男性"]["腕立て伏せ(1回目)"], df[df["性別"]=="女性"]["腕立て伏せ(1回目)"]) |
1 |
MannwhitneyuResult(statistic=76.0, pvalue=0.015249132623633525) |
同様に、片側検定にしたい場合は引数alternative=’greater’を指定します。
カイ二乗検定
バク転ができるかどうか、男女で異なるのか調べましょう。
このように、数値ではなく「できる/できない」のようなカテゴリデータの時はこれまでの検定は適用できないので、代わりにカイ二乗検定を使います。
1 2 |
crosstab = pd.crosstab(df["性別"],df["バク転"]) stats.chi2_contingency(crosstab) |
1 |
(0.27777777777777773, 0.5981614526835279, 1, array([[6., 2.],[9., 3.]])) |
カイ二乗検定は、今までのやり方とは少し違います。
まず、pandasのcrosstabメソッドでクロス集計を行います。
そのクロス集計表に対してchi2_contingencyメソッドを適用します。
結果はカイ二乗値、p値、自由度、期待度数・・・の順番に取得されます。
つまり、p値は約0.598なので、男女間でバク転が出来るor出来ないには有意差があるとはいえない、という結果になりました。
さいごに
仮説検定で有意差を出したいといっても、データにより適切な方法は違ってきます。
きちんと適切な手法を選択して、そしてどの方法を使うことになっても迷わずに実行できるように準備しておきましょう。