pythonのprastクラスによる集計(5) t検定

2018/10/13

 今回は t検定を取り上げます。

 2つのグループ(数値データ)について、その平均値に
有意な差があるかどうかを検証します。

 たとえば、男女間で身長に差があるかどうかなどを調べます。

 処理するcsvファイルは第1回〜第4回と同じ data01.csv です。

 当Webページで紹介するスクリプトや素材データ一式は、
prast05.zip という圧縮ファイルに同梱しておきます。

    


《このページの目次》


    

1. scipyライブラリのt検定用メソッド

 pythonのscipyライブラリには t検定を行うメソッドが2種類あります。

 男女間(異なる2つの標本)について、その身長や体重を比較するのは
対応のないケースです。

 「対応のある」は、1つの標本に対して2種類の調査結果を比較する場合です。

 同じ10人に対して2種類の睡眠剤を投与して、
薬を用いない場合と比べて睡眠時間がどれだけ増減したかを調べます。

 2種類の薬についてその効果を比較するわけです。

 次のように記述します。

import scipy.stats as stats
t, p = stats.ttest_ind(a, b, equal_var=True)  # 対応のない場合
t, p = stats.ttest_rel(a, b)  # 対応のある場合

 引数 a, b は array(配列)です。

 配列に準ずる Series などでも大丈夫みたいです。

 equal_varオプションは、a, b の分散が等しいといえるなら True、
不等分散なら False にします。デフォルトは True。

 ttest_rel() の方にはequal_varオプションはありません。

 戻り値の t は統計量、p は有意確率。

 p<0.05 の場合、「2群の平均値に有意差がある」と判断されます。

 このpは両側検定の値です。

 「aとbの平均値が等しいか否か」の判断材料です。

 「aがbより大きいか(または小さいか)」を見ればいいだけなら片側検定ですが、
ttest_ind(), ttest_rel() には片側検定を指定するオプションがありません。

 両側検定で有意差があると分かれば、2群の平均値を見て
どちらが有意に大きいか(小さいか)を判断できます。

 でも、片側検定と意味合いが必ずしも同じとはいえません。

目次に戻る


2. Prastクラスの t_test()

 Prastクラスの t_test() は t検定を行うメソッドです。

 片側検定にも対応しました。

 また、pairedオプションを True にすれば対応のある場合の検定を行えます。

Prast.t_test(cname, gkey, alternative="two-sided",
    paired=False, equal_var=False)
dod = psx.t_test("height", "gender", equal_var=True)

 戻り値は OrderedDict で、t, p_value などが盛り込まれます。

 引数の意味は次のとおり。

目次に戻る


3. 対応のない t検定のサンプルスクリプト

 男女間で身長の平均値に差があるかどうかをみます。

 平均値が集団の代表値として最も妥当性を持つのは正規分布の場合です。

 なので、数値データが正規分布かどうかを事前にチェックするのが本来ですが、
今回は省略します。

 また、F検定で等分散か否かをチェックするのも省略して
不等分散を仮定して検定を行います。

 スクリプトは次のとおり。

 1# t_test01.py (coding: cp932)
 2import pandas as pd
 3from prast import Prast
 4
 5dtf = pd.read_csv("data01.csv")
 6psx = Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")
 7dod = psx.t_test("height", "gender", equal_var=False)
 8for key in dod.keys():
 9    print(key)
10    print(dod[key])
11    print('')

    

 戻り値には下のものがあります。

 p_value が非常に小さい値なので、有意水準を1%としても
有意な差があると判断されます。

目次に戻る


4. 対応のある t検定のサンプルスクリプト

 data02.csv は、10人に対して2種類の睡眠剤を投与し、
睡眠時間がどれだけ増加したかを記録したものです。

 列ラベルには次の3つがあります。

 csvの行数は 20行です。

 1〜10行目は睡眠剤1の結果、11〜20行目が睡眠剤2の結果。

 IDは、10行目までが 1〜10 で、11〜20行目も 1〜10 です。

 実は統計解析ソフトRに備えられているデータをそのまま使っています。

 extra(増減時間)をgroup(薬の種類)で分類した上で検定します。

 サンプルスクリプトは下のとおり。

 1# t_test02.py (coding: cp932)
 2import pandas as pd
 3from prast import Prast
 4
 5dtf = pd.read_csv("data02.csv")
 6psx = Prast(dtf, encoding="cp932")
 7dod = psx.t_test("extra", "group", paired=True)
 8for key in dod.keys():
 9    print(key)
10    print(dod[key])
11    print('')

 実行結果は次のとおり。

 p_value<0.05 なので、睡眠剤 1, 2 の間に有意な差があるといえます。

目次に戻る


5. クラスの外で定義されている t_test()

 Prastクラスに属さない t_test() もあります。

 第1引数と第2引数に2群の数値配列を指定します。

 こちらの方が素直な感じで使いやすいかもしれません。

 男女間の体重を比較するスクリプトを掲げておきます。

 1# t_test03.py (coding: cp932)
 2import pandas as pd
 3import prast as ps
 4ps.set_encoding("cp932")  # python2用にencodingを設定
 5
 6dtf = pd.read_csv("data01.csv")
 7male = dtf[dtf["gender"] == "m"]  # 男性のみのDataFrame
 8female = dtf[dtf["gender"] == "f"]  # 女性のみのDataFrame
 9a = male["weight"]  # 男性の体重
10b = female["weight"]  # 女性の体重
11dod = ps.t_test(a, b, alternative="greater")
12for key in dod.keys():
13    print(key)
14    print(dod[key])
15    print('')

 t_test() に与える引数 a, b には欠損値 NaN が含まれていてもかまいません。

 内部処理で NaN を除去した上で検定します。

目次に戻る


6. 等分散か否かを調べる F検定

 分散が等しいといえるかなどをチェックする F検定は
var_test() で行います。

 これも Prastクラスに属するメソッドと
クラスの外で定義されているものとがあります。

Prast.var_test(cname, gkey, alternative="two-sided")  # クラス内定義
var_test(a, b, alternative="two-sided")  # クラス外定義

 引数の意味合いは t_test() と同じです。

dod = psx.var_test("height", "gender")

 上記のようにクラス内メソッドを呼び出したときの戻り値は下のとおり。

 女性の分散の方が少し大きいですが、
p_value>0.05なので、「分散が等しい」(帰無仮説)を棄却できません。

 つまり「分散が等しくない」(対立仮設)を採択できません。

 以下に F検定で等分散か否かを調べた後で t検定を行うスクリプトを掲げます。

 1# t_test04.py (coding: cp932)
 2import pandas as pd
 3from prast import Prast
 4
 5dtf = pd.read_csv("data01.csv")
 6psx = Prast(dtf, "data01_c.txt", "data01_i.txt", "cp932")
 7fod = psx.var_test("height", "gender")  # F検定
 8flag = fod["p_value"] > 0.05  # True or False
 9print(u"F検定: F比=%f" % fod["f"])
10if flag:
11    print(u"等分散: p=%f" % fod["p_value"])
12else:
13    print(u"不等分散: p=%f" % fod["p_value"])
14print('')
15dod = psx.t_test("height", "gender", equal_var=flag)
16print(u"t検定の結果:")
17for key in dod.keys():
18    print(key)
19    print(dod[key])
20    print('')

〜 以上 〜

Copyright (C) T. Yoshiizumi, 2018 All rights reserved.