データ分析: CSVデータに対して記述統計学や推計統計学をPythonで手軽に適用

## はじめに

データ分析を行う上で、記述統計学や推計統計学が機械学習モデルの構築前のデータの理解の段階で使われることがしばしばあります。
今回、データを理解する上で、CSVデータをpandasのdataframeで読み込み、記述統計学や推計統計学を手軽に適用するソースコードを作成しました。


## 準備データ

inputとなる　CSVデータを作成します。
ソースコードを作成する上で使ったデータを以下に示します。

```
x1,x2,x3,x4,x5
1,11,1,1,1
2,12,1,1,2
3,13,1,1,3
4,14,1,1,4
5,150,1,1,5
5,150,1,1,5
4,160,1,1,4
3,180,1,1,3
2,180,1,1,2
1,190,2,2,2
```

### ソースコード

モジュール化したソースコードを以下に示します。  

```
class StatisticalTests():
    def __init__(self):
        pass

    @classmethod
    def basic_info(cls, df):
        print('基本統計量------------------start')
        print('df.head(3)-------------')
        print(df.head(3))
        print('df.dtypes-------------')
        print(df.dtypes)
        print('df.describe(include=\'all\')-------------')
        print(df.describe(include='all'))

    @classmethod
    def t_interval(cls, df):
        print('母平均の95%信頼区間-------------------start')
        for column_name, s in df.iteritems():
            u2 = s.var(ddof=1)  # 母集団の分散推定値（不偏分散）
            m = s.mean()  # 標本平均
            n = len(s)-1  # 自由度
            se = math.sqrt(u2/len(s))  # 標準誤差

            ci1, ci2 = st.t.interval(alpha=0.95, loc=m, scale=se, df=n)
            print(f'カラム名 = {column_name} // 母平均の95%信頼区間CI = '
                  f'[{ci1:.2f} , {ci2:.2f}] // 標本平均[{m}]')

    @classmethod
    def shapiro(cls, df):
        print('シャピロ・ウィルク検定(正規性の検定)------------------start')
        for column_name, s in df.iteritems():
            _, p = st.shapiro(s)
            if p >= 0.05:
                print(f'カラム名 = {column_name} // p値 = {p:.3f} '
                      f'// 検定結果: 帰無仮説を採択して、正規性なしとは言えない')
            else:
                print(f'カラム名 = {column_name} // p値 = {p:.3f} '
                      f'// 検定結果: 帰無仮説を棄却して、正規性なし')

    @classmethod
    def levene(cls, xa, xb):
        print('2群間: 母平均の95%ルビーン検定による等分散性の検定-------------------start')
        _, p = st.levene(xa, xb, center='mean')
        if p >= 0.05:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を採択して、2つの標本には等分散性なしとは言えない')
        else:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を棄却して、2つの標本には等分散性なし')

    @classmethod
    def ttest_rel(cls, xa, xb):
        print('2群間: 対応ありt検定-------------------start')
        # 2つの標本の平均値に有意差がないことを帰無仮説とする
        # 対応ありでは、Ａさん、Ｂさんのように薬の投与前後で同じ人を調べる
        t, p = st.ttest_rel(xa, xb)
        if np.sign(t) == -1:
            a = xa
            xa = xb
            xb = a

        t, p = st.ttest_rel(xa, xb)
        mu = abs(xa.mean()-xb.mean())
        se = mu/t
        n = len(xa)+len(xb)-2
        ci1, ci2 = st.t.interval(alpha=0.95, loc=mu, scale=se, df=n)
        if p >= 0.05:
            print(f'p値={p:.3f} // t値 = {t:.2f}')
            print(f'// 平均値の差 = {mu:.2f} // 差の標準誤差 = {se:.2f}')
            print(f'// 平均値の差の95%信頼区間CI = [{ci1:.2f} , {ci2:.2f}]')
            print('// 検定結果: 帰無仮説を採択して、2つの標本の平均値に有意差があるとは言えない')
        else:
            print(f'p値={p:.3f} // t値 = {t:.2f}')
            print(f'// 平均値の差 = {mu:.2f} // 差の標準誤差 = {se:.2f}')
            print(f'// 平均値の差の95%信頼区間CI = [{ci1:.2f} , {ci2:.2f}]')
            print(f'// 検定結果: 帰無仮説を棄却して、2つの標本の平均値に有意差あり')

    @classmethod
    def ttest_ind_equal_var_true(cls, xa, xb):
        print('2群間: 対応なし(2群間に等分散性あり)t検定-------------------start')
        # 2つの標本の平均値に有意差がないことを帰無仮説とする
        # 対応なしでは、Ａさん、Ｂさんのように薬の投与前後で同じ人を調べない
        t, p = st.ttest_ind(xa, xb, equal_var=True)
        if np.sign(t) == -1:
            a = xa
            xa = xb
            xb = a

        t, p = st.ttest_ind(xa, xb, equal_var=True)
        cls._ttest_ind(t, p, xa, xb)

    @classmethod
    def ttest_ind_equal_var_false(cls, xa, xb):
        print('2群間: 対応なし(2群間に等分散性なし)t検定-------------------start')
        # 2つの標本の平均値に有意差がないことを帰無仮説とする
        # 対応なしでは、Ａさん、Ｂさんのように薬の投与前後で同じ人を調べない
        t, p = st.ttest_ind(xa, xb, equal_var=False)
        if np.sign(t) == -1:
            a = xa
            xa = xb
            xb = a

        t, p = st.ttest_ind(xa, xb, equal_var=False)
        cls._ttest_ind(t, p, xa, xb)

    @classmethod
    def _ttest_ind(cls, t, p, xa, xb):
        mu = abs(xa.mean()-xb.mean())
        se = mu/t
        n = len(xa)+len(xb)-2
        ci1, ci2 = st.t.interval(alpha=0.95, loc=mu, scale=se, df=n)
        if p >= 0.05:
            print(f'p値={p:.3f} // t値 = {t:.2f}')
            print(f'// 平均値の差 = {mu:.2f} // 差の標準誤差 = {se:.2f}')
            print(f'// 平均値の差の95%信頼区間CI = [{ci1:.2f} , {ci2:.2f}]')
            print('// 検定結果: 帰無仮説を採択して、2つの標本の平均値に有意差があるとは言えない')
        else:
            print(f'p値={p:.3f} // t値 = {t:.2f}')
            print(f'// 平均値の差 = {mu:.2f} // 差の標準誤差 = {se:.2f}')
            print(f'// 平均値の差の95%信頼区間CI = [{ci1:.2f} , {ci2:.2f}]')
            print(f'// 検定結果: 帰無仮説を棄却して、2つの標本の平均値に有意差あり')

    @classmethod
    def chisquare(cls, sample, answer):
        print('適合度の検定-------------------start')
        # 対立仮説：得られたデータは理論上の分布に適合しない。
        sample = sample.tolist()
        answer = answer.tolist()

        p = st.chisquare(sample, f_exp=answer)[1]
        if p >= 0.05:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を採択して、理論上の分布に適合しないと結論づけられない。')
        else:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を棄却して、理論上の分布に適合しないと結論づけられる。')

    @classmethod
    def chi2_contingency(cls, df):
        print('独立性の検定-------------------start')
        # Usage)
        #       発ガン人数	非発ガン人数
        # 喫煙群	     30	          70
        # 非喫煙群	    20	         80
        # print(st.chi2_contingency(x))
        p = st.chi2_contingency(df.values)[1]
        if p >= 0.05:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を採択して、2つの変数は独立していないと結論付けられない。')
        else:
            print(f'p値 = {p:.3f} // 検定結果: 帰無仮説を棄却して、2つの変数は独立していないと結論付けられる。')

    @classmethod
    def pearsonr(cls, xa, xb):
        print('相関係数の検定-------------------start')
        # 帰無仮説と対立仮説をたてる: 帰無仮説は ρ=0 、つまり母相関 =0
        # 対立仮説は「 ρ≠0 」、つまり母相関 ≠0
        x1 = xa.values
        x2 = xb.values
        s = st.pearsonr(x1, x2)
        if s[1] >= 0.05:
            print(f'相関係数 = {s[0]:.3f} // p値 = {s[1]:.3f} // 検定結果: 帰無仮説を採択する。相関ありとは言えない。')
        else:
            print(f'相関係数 = {s[0]:.3f} // p値 = {s[1]:.3f} // 検定結果: 帰無仮説を棄却する。相関あり。')

```

## 実行結果

上記モジュール化したソースコードの実行例を以下に示します。  
CSVデータを一通り理解できることがわかります。

```
基本統計量------------------start
df.head(3)-------------
   x1  x2  x3  x4  x5
0   1  11   1   1   1
1   2  12   1   1   2
2   3  13   1   1   3
df.dtypes-------------
x1    int64
x2    int64
x3    int64
x4    int64
x5    int64
dtype: object
df.describe(include='all')-------------
              x1          x2         x3         x4        x5
count  10.000000   10.000000  10.000000  10.000000  10.00000
mean    3.000000  106.000000   1.100000   1.100000   3.10000
std     1.490712   81.493013   0.316228   0.316228   1.37032
min     1.000000   11.000000   1.000000   1.000000   1.00000
25%     2.000000   13.250000   1.000000   1.000000   2.00000
50%     3.000000  150.000000   1.000000   1.000000   3.00000
75%     4.000000  175.000000   1.000000   1.000000   4.00000
max     5.000000  190.000000   2.000000   2.000000   5.00000
母平均の95%信頼区間-------------------start
カラム名 = x1 // 母平均の95%信頼区間CI = [1.93 , 4.07] // 標本平均[3.0]
カラム名 = x2 // 母平均の95%信頼区間CI = [47.70 , 164.30] // 標本平均[106.0]
カラム名 = x3 // 母平均の95%信頼区間CI = [0.87 , 1.33] // 標本平均[1.1]
カラム名 = x4 // 母平均の95%信頼区間CI = [0.87 , 1.33] // 標本平均[1.1]
カラム名 = x5 // 母平均の95%信頼区間CI = [2.12 , 4.08] // 標本平均[3.1]
シャピロ・ウィルク検定(正規性の検定)------------------start
カラム名 = x1 // p値 = 0.341 // 検定結果: 帰無仮説を採択して、正規性なしとは言えない
カラム名 = x2 // p値 = 0.004 // 検定結果: 帰無仮説を棄却して、正規性なし
カラム名 = x3 // p値 = 0.000 // 検定結果: 帰無仮説を棄却して、正規性なし
カラム名 = x4 // p値 = 0.000 // 検定結果: 帰無仮説を棄却して、正規性なし
カラム名 = x5 // p値 = 0.410 // 検定結果: 帰無仮説を採択して、正規性なしとは言えない
2群間: 母平均の95%ルビーン検定による等分散性の検定-------------------start
p値 = 0.000 // 検定結果: 帰無仮説を棄却して、2つの標本には等分散性なし
2群間: 母平均の95%ルビーン検定による等分散性の検定-------------------start
p値 = 0.813 // 検定結果: 帰無仮説を採択して、2つの標本には等分散性なしとは言えない
2群間: 対応ありt検定-------------------start
p値=0.003 // t値 = 4.01
// 平均値の差 = 103.00 // 差の標準誤差 = 25.70
// 平均値の差の95%信頼区間CI = [49.01 , 156.99]
// 検定結果: 帰無仮説を棄却して、2つの標本の平均値に有意差あり
2群間: 対応ありt検定-------------------start
p値=0.343 // t値 = 1.00
// 平均値の差 = 0.10 // 差の標準誤差 = 0.10
// 平均値の差の95%信頼区間CI = [-0.11 , 0.31]
// 検定結果: 帰無仮説を採択して、2つの標本の平均値に有意差があるとは言えない
2群間: 対応なし(2群間に等分散性あり)t検定-------------------start
p値=0.001 // t値 = 4.00
// 平均値の差 = 103.00 // 差の標準誤差 = 25.77
// 平均値の差の95%信頼区間CI = [48.85 , 157.15]
// 検定結果: 帰無仮説を棄却して、2つの標本の平均値に有意差あり
2群間: 対応なし(2群間に等分散性あり)t検定-------------------start
p値=0.878 // t値 = 0.16
// 平均値の差 = 0.10 // 差の標準誤差 = 0.64
// 平均値の差の95%信頼区間CI = [-1.25 , 1.45]
// 検定結果: 帰無仮説を採択して、2つの標本の平均値に有意差があるとは言えない
2群間: 対応なし(2群間に等分散性なし)t検定-------------------start
p値=0.003 // t値 = 4.00
// 平均値の差 = 103.00 // 差の標準誤差 = 25.77
// 平均値の差の95%信頼区間CI = [48.85 , 157.15]
// 検定結果: 帰無仮説を棄却して、2つの標本の平均値に有意差あり
2群間: 対応なし(2群間に等分散性なし)t検定-------------------start
p値=0.878 // t値 = 0.16
// 平均値の差 = 0.10 // 差の標準誤差 = 0.64
// 平均値の差の95%信頼区間CI = [-1.25 , 1.45]
// 検定結果: 帰無仮説を採択して、2つの標本の平均値に有意差があるとは言えない
適合度の検定-------------------start
p値 = 0.000 // 検定結果: 帰無仮説を棄却して、理論上の分布に適合しないと結論づけられる。
適合度の検定-------------------start
p値 = 1.000 // 検定結果: 帰無仮説を採択して、理論上の分布に適合しないと結論づけられない。
独立性の検定-------------------start
p値 = 0.142 // 検定結果: 帰無仮説を採択して、2つの変数は独立していないと結論付けられない。
独立性の検定-------------------start
p値 = 0.000 // 検定結果: 帰無仮説を棄却して、2つの変数は独立していないと結論付けられる。
独立性の検定-------------------start
p値 = 1.000 // 検定結果: 帰無仮説を採択して、2つの変数は独立していないと結論付けられない。
相関係数の検定-------------------start
相関係数 = 0.165 // p値 = 0.649 // 検定結果: 帰無仮説を採択する。相関ありとは言えない。
相関係数の検定-------------------start
相関係数 = 0.979 // p値 = 0.000 // 検定結果: 帰無仮説を棄却する。相関あり。
```

## まとめ
- CSVデータをpandasのdataframeに読み込み記述統計学や推計統計学を手軽に適用できることを確認できた。
- CSVデータを記述統計学や推計統計学の適用により一通り理解できることがわかった。
 

## 参考
- [Pythonでt検定　2クラスの試験成績の比較](https://qiita.com/code0327/items/a96dd2fbd8a491d2eeaa)  
- [pythonを用いた統計的仮説検定、信頼区間推定方法まとめ](https://qiita.com/Wotipati/items/4f5e893fa39ad4cb9957)