Pythonによるハンバーガー統計学 #1

ハンバーガー統計学にようこそ!の第1章をPythonでやってみたもの。

準備

# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

1.1 ポテトの長さの平均は

# ワクワクバーガー
waku = pd.Series(
         [3.5, 4.2, 4.9, 4.6, 2.8, 5.6, 4.2, 4.9, 4.4, 3.7,
          3.8, 4.0, 5.2, 3.9, 5.6, 5.3, 5.0, 4.7, 4.0, 3.1,
          5.8, 3.6, 6.0, 4.2, 5.7, 3.9, 4.7, 5.3, 5.5, 4.7,
          6.4, 3.8, 3.9, 4.2, 5.1, 5.1, 4.1, 3.6, 4.2, 5.0,
          4.2, 5.2, 5.3, 6.4, 4.4, 3.6, 3.7, 4.2, 4.8]
          )

# モグモグバーガー
mogu = pd.Series(
        [4.5, 4.2, 3.9, 6.6, 0.8, 5.6, 3.2, 6.9, 4.4, 4.7,
        3.8, 3.0, 3.2, 4.9, 7.6, 3.3, 7.0, 3.7, 3.0, 4.1,
        5.8, 4.6, 4.0, 2.2, 7.7, 3.9, 6.7, 3.3, 7.5, 2.7,
        5.4, 5.8, 5.9, 3.2, 5.1, 3.1, 6.1, 4.6, 2.2, 4.0,
        6.4, 5.2, 3.3, 6.4, 6.4, 2.6, 2.6, 5.2, 5.8]
        )

# 集計
# 総和と平均を求める
waku.sum() # 224.0
waku.mean() # 4.5714285714285712
mogu.sum() # 226.10000000000002
mogu.mean() # 4.6142857142857148

箱ひげ図はDataFrameにしてから描画すると簡単。

df = pd.DataFrame({"waku":waku, "mogu":mogu})
df.boxplot(grid=False)
plt.show()

1.2 度数分布

度数分布はpandas.cutで分割し、これに基づいてpandas.groupbyで元データを分割、グループごとにcountを適用するという手順をとることで得られる(cf. pandas で年齢階級をつくる - Qiita)。

waku.groupby(pd.cut(waku, np.arange(0, 9, 1))).count()
(0, 1]     0
(1, 2]     0
(2, 3]     1
(3, 4]    14
(4, 5]    19
(5, 6]    13
(6, 7]     2
(7, 8]     0
dtype: int64
mogu.groupby(pd.cut(mogu, np.arange(0, 9, 1))).count()
(0, 1]     1
(1, 2]     0
(2, 3]     7
(3, 4]    13
(4, 5]     8
(5, 6]     9
(6, 7]     8
(7, 8]     3
dtype: int64

ヒストグラムの方が作るのは簡単な印象。

df.hist(bins=np.arange(0,9,1),
        sharey=True,  # y軸を揃える
        grid=False,   # グリッド非表示
        layout=(2,1)) # 2行1列に並べる
plt.show()

1.4 分散と標準偏差

df.var(ddof=0) # 分散
mogu    2.584898
waku    0.680816
dtype: float64
df.std(ddof=0) # 標準偏差
mogu    1.607762
waku    0.825116
dtype: float64

ddofDelta Degrees of Freedomで、自由度はサンプルサイズをNとしてN - ddofとして指定される。Pandasの場合はデフォルトが1なので、何も指定しないと不偏分散およびそれに基づく標準偏差が計算される。今回はテキストに合わせるためにあえて0を指定している。

なお、numpy.varおよびnumpy.stdも分散および標準偏差を計算するメソッドであり、ddofという同名の引数が用意されているが、なんとこちらはデフォルトが0である。

1.9 通過テスト

df = pd.DataFrame({
  "桜組" : [78, 62, 81, 59, 72, 68, 75, 65, 80, 60, 78, 62, 70],
  "桃組" : [70, 72, 68, 75, 65, 71, 69, 76, 64, 80, 60, 73, 67],
  "柳組" : [57, 59, 55, 62, 52, 58, 56, 63, 51, 67, 47, 60, 54]
  })

平均、分散、標準偏差df.mean(), df.var(), df.std()でそれぞれ計算できるが、df.describe()を使うとデータ数、平均、標準偏差といわゆる五数要約(最大値、75%点、中央値、25%点、最小値)を確認できる。

df.describe()
柳組   桃組  桜組
count   13.000000   13.000000   13.000000
mean    57.000000   70.000000   70.000000
std 5.400617    5.400617    7.937254
min 47.000000   60.000000   59.000000
25% 54.000000   67.000000   62.000000
50% 57.000000   70.000000   70.000000
75% 60.000000   73.000000   78.000000
max 67.000000   80.000000   81.000000

また、scipy.stats.describe()ではデータ数、最大値、最小値、平均、分散、歪度、尖度を確認でき、ddofで自由度を指定できる。

import scipy as sp
sp.stats.describe(df)
DescribeResult(nobs=13, minmax=(array([47, 60, 59]), array([67, 80, 81])), mean=array([ 57.,  70.,  70.]), variance=array([ 29.16666667,  29.16666667,  63.        ]), skewness=array([ 0.,  0.,  0.]), kurtosis=array([-0.44902857, -0.44902857, -1.47721928]))

ついでに日本語ラベル付きのヒストグラムでも描いてみようと思って日本語表示に結構手間取った。seabornの設定でフォントを指定するとmatplotlibの設定もまとめて上書きされるので楽っぽい(cf.matplotlib と Seaborn の軸の日本語設定 - Qiita)。

import seaborn as sns
sns.set(font='Ricty') # Rictyは別途インストールしたフォント

df.hist()
plt.show()

seabornでも描いてみよう。

for i in df.columns:
  ax = sns.distplot(df[i],
                    label=i,
                    bins = 4)

ax.legend()
plt.show()

適当にやっても綺麗に出力される印象(cf.python - Two seaborn distplots one same axis - Stack Overflow)。