【プログラマーのための統計学】 度数分布とヒストグラム

## 目次
[プログラマーのための統計学 - 目次](http://qiita.com/Esfahan/items/bd9e52837bbaeb168b9f)

## 概要
統計学で使用する、度数分布とヒストグラムについてです。
pythonでヒストグラムを作成もします。

## 度数分布
度数分布とは
> 収集したデータをいくつかの階級（区間）に分けたときの、それぞれの階級に所属するデータの分布状況のこと

引用元：[コトバンク](https://kotobank.jp/word/%E5%BA%A6%E6%95%B0%E5%88%86%E5%B8%83-6683)

らしいです。言葉の定義だけでは難しいので、実際に例を挙げて説明していきます。

## 度数分布表
ある組織の年齢の分布を調べるとします。
組織の人数は全部で100人いるとします。

調査した結果、下記のような結果になりました。
非常に見づらいですが、100人全員分の年齢をlistにしています。

```py
ages = [23, 22, 23, 22, 24, 20, 22, 24,
29, 28, 25, 25, 26, 27, 28, 27, 25, 25, 27, 25, 25,
32, 32, 32, 33, 33, 32, 33, 32, 30, 33, 32,
35, 39, 38, 38, 37, 35, 38, 35, 35, 38, 35, 37,
43, 44, 40, 41, 44, 41, 40, 43, 44, 41, 41, 44, 43, 42, 40, 44, 42, 41, 42,
47, 49, 49, 46, 48, 45, 49, 49, 49, 49, 49, 48, 46, 49, 45, 48, 49, 48,
54, 52, 54, 53, 53, 54, 50, 51, 52, 54,
58, 56, 58, 58, 55, 57, 56, 56, 55]
```

このデータを元に、年齢層別の分布表を作ります。
このような表のことを、度数分布表と言います。

| 階級 | 階級値 | 度数 | 相対度数 | 累積相対度数 |
| --- | --: | --: | --: | --: |
| 20歳以上 - 25歳未満 | 22.5 | 8人 | 0.08 | 0.08 |
| 25歳以上 - 30歳未満 | 27.5 | 13人 | 0.13 | 0.21 |
| 30歳以上 - 35歳未満 | 32.5 | 11人 | 0.11 | 0.32 |
| 35歳以上 - 40歳未満 | 37.5 | 12人 | 0.12 | 0.44 |
| 40歳以上 - 45歳未満 | 42.5 | 19人 | 0.19 | 0.63 |
| 45歳以上 - 50歳未満 | 47.5 | 18人 | 0.18 | 0.81 |
| 50歳以上 - 55歳未満 | 52.5 | 10人 | 0.10 | 0.91 |
| 55歳以上 - 60歳未満 | 57.5 | 9人 | 0.09 | 1.00 |

以下、各項目の説明です。

#### [階級]
度数を集計するためのグループ分けした区間。この例では年齢。

#### [階級値]
階級の真ん中の値のこと。
階級がa以上b未満のとき、階級値は以下で求められます。

```math
階級値 = \frac{a+b}{2}
```

上記の表の20歳以上25歳未満の場合の階級値は、22.5になります。

```math
22.5 = \frac{20+25}{2}
```

#### [度数]
該当の階級のデータ数。この例では、人数。

#### [相対度数]
各階級ごとの度数の全体に占める割合で、`度数 / 合計` 。

```math
相対度数 = \frac{度数}{合計}
```

この例では、 `各階級の人数 / 100人` となるので、上記の表の20歳以上25歳未満の場合の相対度数は `0.08` になります。

```math
0.08 = \frac{8}{100}
```

#### [累積相対度数]
その階級までの相対度数の全ての和（累積和）のことです。
上記の表の場合は、以下のようになります。

```
20歳以上 - 25歳未満 = 0.08
25歳以上 - 30歳未満 = 0.08 + 0.13
30歳以上 - 35歳未満 = 0.08 + 0.13 + 0.11
35歳以上 - 40歳未満 = 0.08 + 0.13 + 0.11 + 0.12
︙
55歳以上 - 60歳未満 = 0.08 + 0.13 + 0.11 + 0.12 + 0.19 + 0.18 + 0.1 + 0.09 = 1
```

一番最後の階級は必ず `1` になります。

## ヒストグラム
ヒストグラムとは度数分布表をグラフにしたもののことです。
前述の度数分布表を、pythonのmatplotlibを使って、ヒストグラムにします。
（正しくは、ヒストグラムに見せかけた棒グラフを作ります。詳細は後述します。）

matplotlibの使い方はこちらを参照
[jupyter(ipython notebook) + matplotlib + vagrantでグラフ描画](http://qiita.com/Esfahan/items/bbb2e9d593faa8b45861)

```py
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

# 横座標
left = np.array([0, 1, 2, 3, 4, 5, 6, 7])
# 縦座標
height = np.array([8, 13, 11, 12, 19, 18, 10, 9])
ax.bar(left, height, width=1.0)
# グラフのタイトル
plt.title('Ages')
# X軸のタイトル
plt.xlabel('Age group')
# Y軸のタイトル
plt.ylabel('Numbers')

# X軸のラベル
label = [
    '20 - 24',
    '25 - 29',
    '30 - 34',
    '35 - 39',
    '40 - 44',
    '45 - 49',
    '50 - 54',
    '55 - 59'
]

# ラベルを打つX軸の場所
ax.set_xticks([0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5])
# X軸にラベルをセット、90度回転させる
ax.set_xticklabels(label, rotation = 90)
# 描画
plt.show()
```

すると、このようなグラフが出来上がります。
これが、ヒストグラムです。

<img width="578" alt="40fada156fcb51e856fa6533ffc12ae7.png" src="https://qiita-image-store.s3.amazonaws.com/0/105936/49d2ba2d-74c4-d03b-90ed-a691e2856117.png">

X軸が階級（年齢層）、Y軸は度数（人数）を表しています。
40歳以上 - 45歳未満が一番多いことがわかります。

## ヒストグラムと棒グラフ
ヒストグラムは棒グラフに見えますが、厳密には、ヒストグラムと棒グラフは違います。

> ■棒グラフ
　棒：それぞれが独立している
　軸：お好きに、どうぞ
　使う目的：棒同士を比較する

>■ヒストグラム
　棒：全部でひとつ
　軸：横軸が「階級」で縦軸が「度数」
　使う目的：分布を見る

らしいです。
詳細は下記を参照してください。
[「棒グラフ」と「ヒストグラム」の違い](http://wa3.i-3-i.info/diff317graph.html)

## pythonのmatplotlibを使ってヒストグラムを作る
先ほど、「ヒストグラムに見せかけた棒グラフを作ります。」と言いましたが、
今度は、matplotlibを使って、本当のヒストグラムを作成します。

前述の例では、集計結果を度数分布表にしてから、ヒストグラム（実際は棒グラフ）を作りました。
しかし、pythonのmatplotlibを使えば、集計結果からそのままヒストグラムを作ることができます。


```py
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

# 100人分の年齢
ages = [
23, 22, 23, 22, 24, 20, 22, 24,
29, 28, 25, 25, 26, 27, 28, 27, 25, 25, 27, 25, 25,
32, 32, 32, 33, 33, 32, 33, 32, 30, 33, 32,
35, 39, 38, 38, 37, 35, 38, 35, 35, 38, 35, 37,
43, 44, 40, 41, 44, 41, 40, 43, 44, 41, 41, 44, 43, 42, 40, 44, 42, 41, 42,
47, 49, 49, 46, 48, 45, 49, 49, 49, 49, 49, 48, 46, 49, 45, 48, 49, 48,
54, 52, 54, 53, 53, 54, 50, 51, 52, 54,
58, 56, 58, 58, 55, 57, 56, 56, 55]

fig, ax = plt.subplots()

# 8個の階級でヒストグラムを作成します。binsの最小値と最大値をrangeで指定します。
# 戻り値について　n => 各階級における度数、bins => 階級のリスト
n, bins, patches  = ax.hist(ages, bins=8, range=(20, 60))


# グラフのタイトル
ax.set_title('Ages')
# X軸のタイトル
ax.set_xlabel('Age group')
# Y軸のタイトル
ax.set_ylabel('Numbers')

# X軸のラベル
label = [
    '20 - 24',
    '25 - 29',
    '30 - 34',
    '35 - 39',
    '40 - 44',
    '45 - 49',
    '50 - 54',
    '55 - 59'
]

# ラベルを打つX軸の場所。階級値をセットする。
ax.set_xticks([22.5, 27.5, 32.5, 37.5, 42.5, 47.5, 52.5, 57.5])
# X軸にラベルをセット、90度回転させる
ax.set_xticklabels(label, rotation = 90)

# 描画
plt.show()
```

これを実行すると、先ほど棒グラフとして作成したものと、同じ形のヒストグラムが出来上がりました！

<img width="567" alt="89597524777552a81eb5f29d4437cb94 (1).png" src="https://qiita-image-store.s3.amazonaws.com/0/105936/95af98b9-54c9-0654-a341-2a5fdb9adf7e.png">

度数分布表を作成しなくてよいので、とても便利ですね！

## 階級幅の決め方
階級の幅の決め方ですが、特にルールがあるわけではありませんが、
スタージェスの公式を使って算出するということもできます。

```math
k=1+log_2N
```

`n` はデータ数、`k` は階級数になります。
前述の例だと、`k` は100になり、以下のようになります。

```math
k=1+log_2100
```

`log2` というのは自然対数です。
参考：[2の自然対数](https://ja.wikipedia.org/wiki/2%E3%81%AE%E8%87%AA%E7%84%B6%E5%AF%BE%E6%95%B0)

なんか難しそうですが、
pythonであれば、以下の関数で求めることができます。

python2系の場合

```py
import math
k = 1 + math.log(n, 2)
```

python3系の場合

```py3
import math
k = 1 + math.log2(n)
```

ただし、スタージェスの公式で求められる値は、あくまで目安なので、階級数が明確な場合は、わざわざ公式を使う必要はありません。

次項：[ローレンツ曲線とジニ係数](http://qiita.com/Esfahan/items/b92ad4468b9e6b3ef0e6)

## 参考
- [統計WEB -  度数分布と累積度数分布](https://bellcurve.jp/statistics/course/)
- [JSciencer - 度数分布とヒストグラム](http://jsciencer.com/unimath/toukeigaku/4439/)
- [度数分布表とは](http://wa3.i-3-i.info/word15619.html)
- [ヒストグラム (histogram)とは](http://wa3.i-3-i.info/word15620.html)
- [matplotlib で棒グラフを描く](http://pythondatascience.plavox.info/matplotlib/%E6%A3%92%E3%82%B0%E3%83%A9%E3%83%95)
- [matplotlib入門 ヒストグラム編](http://www.python.ambitious-engineer.com/archives/899)
- [Python でデータサイエンス](http://pythondatascience.plavox.info/)
- [スタージェスの公式を用いて階級数を決める](http://qiita.com/sugar020855/items/d64dd0bb8c55f4d3723f)