Python初心者の備忘録 #12 ~統計学入門編02~

# はじめに
今回私は最近はやりのchatGPTに興味を持ち、深層学習について学んでみたいと思い立ちました！  
深層学習といえばPythonということなので、最終的にはPythonを使って深層学習ができるとこまでコツコツと学習していくことにしました。  
ただ、勉強するだけではなく少しでもアウトプットをしようということで、備忘録として学習した内容をまとめていこうと思います。  
この記事が少しでも誰かの糧になることを願っております！
**※投稿主の環境はWindowsなのでMacの方は多少違う部分が出てくると思いますが、ご了承ください。**
最初の記事：[Python初心者の備忘録 #01](https://qiita.com/Yushin-Tati/items/961dc47d6163f944f7e9)
前の記事：[Python初心者の備忘録 #11 ~統計学入門編01~](https://qiita.com/Yushin-Tati/items/4fab2dbedb5a3211f0ed)
次の記事：[Python初心者の備忘録 #13 ~統計学入門編03~](https://qiita.com/Yushin-Tati/items/883b4ce13e186ae9fa06)

本記事は**確率、正規分布、標準化**についてまとめてあります。
## ■学習に使用している資料
Udemy：[米国データサイエンティストが教える統計学超入門講座【Pythonで実践】
](https://www.udemy.com/course/python-stats/)
## ■確率
### ▶確率(probability)とは
ある試行を同じ条件で何度も繰り返したときにある事象が起こる相対頻度の極限値。
※極限値とは無限回試行したとき、ということ
### ▶確率変数(random variable)
- 値が確率的に変動する変数ことで、通常$X$で表す
- それぞれの値に確率$P(X)$が対応している

サイコロを例に出すと、サイコロを振った際に出る「**値**」はそれぞれ$\frac{1}{6}$の「**確率**」で1～6の数字に変化する「**変数**」といえる。
![stats_random_variable.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/868d5b73-6c5c-5244-418c-5177c0ffce97.jpeg)
### ▶確率分布(probability distribution)
- 確率がどのように分布しているかを表したもの
- 今まで扱ってきたヒストグラムも捉え方によっては確率分布といえる
- 確率変数は、確率分布から生成された値だと捉えると分かりやすいかも?
- サイコロのようにすべての値の確率が同じものを一様分布という
- 確率分布は「**データ生成装置**」であり、この分布に従って変数の値を出力すると考える

Pythonのstatsモジュールには様々な確率分布のメソッドが用意されており、`stats.<確率分布>.<メソッド>`で扱うことができる。
<確率変数>：`norm`,`uniform`,`randint`,`binorm`,`poisson`,`expon`など
<メソッド>：`rvs()`,`pdf()`,`cdf()`,`sf()`,`mean()`,`var()`,`std()`など
`rvs()`：ランダムな値を返す
`pdf(x)`：PDFにおけるxの時の値を返す
`cdf(x)`：CDFにおけるxの時の値を返す
`sf(x)`：1-CDFにおけるxの時の値を返す
`mean()`：平均を返す
`var()`：分散を返す
`std()`：標準偏差を返す

`stats.randint.rvs(low, high)`：`low`以上、`high`未満の整数をランダムに生成
`stats.uniform.rvs(loc, scale)`：`loc`以上、`loc + scale`以下の数をランダムに生成
```python
# 一様分布からランダムにデータを生成
# ランダムな整数を生成
stats.randint.rvs(1, 7)  # -> 2
# stats.randint(1, 7).rvs()でもOK

# ランダムな数を生成
stats.uniform.rvs(0, 1)  # -> 0.4472931614668868
# stats.uniform(0, 1).rvs()でもOK
```
### ▶離散型と連続型
- とびとびの値をとる確率変数：離散型確率変数(例：サイコロの目)
- 連続の値をとる確率変数：連続確率変数(例：ランダム抽出した成人男性の身長)
- 離散型の関数$f_X(x)$を確率質量関数(PMF:Probability Mass Function)という
- 連続型の関数$f_X(x)$を確率密度関数(PDF:Probability Density Function)という

![stats_PMF_PDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/c3246a70-d7e1-6502-04db-2353d9a933d7.jpeg)
一様分布を描画するには下記のようにする。
- x軸
離散型：`np.arange()`などで、整数xの値を用意
連続型：`np.linspace()`などで、xの値を用意
- y軸
離散型：`stats.randint(low, high).pmf(x)`(整数xの時の確率を返す)
連続型：`stats.uniform(loc, scale).pdf(x)`(xの時の確率を返す)

`plt.plot(x, y)`で描画
```python
# 離散型確率分布の描画
x = np.arange(1, 7)
y = stats.randint(1, 7).pmf(x)
# 'o'はプロットの形を指定している(今回は点)
plt.plot(x, y, 'o')
# .vlines(plot, y_min, y_max)でplotに対する縦線がy_min～y_maxの範囲で描画される
plt.vlines(x, 0, y)
```
![stats_plot(PMF).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/0ffe8b92-ba8a-300f-d9ec-8e96fe022a43.jpeg)
```python
# 連続型確率分布の描画
x = np.linspace(-2, 2, 100)
y = stats.uniform(loc=0, scale=1).pdf(x)
plt.plot(x, y)
```
![stats_plot(PDF).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/26ed086a-dbc7-97a9-4f74-8db6bd06b58a.jpeg)
※loc~loc+scaleの範囲で値xが出力される確率を表している。
### ▶確率密度と確率
- 確率密度関数が描くグラフの面積が確率となる
- グラフ全体の面積は1となるように描画されており、PMFはy軸の値がそのまま確率になっていたが、PDFの場合はy軸の値は重要ではなく面積が必要になってくる
- 曲線グラフの面積を求めるには積分を使用する必要がある

例：ランダム抽出した成人男性の身長
![stats_PDF_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/64c2b194-ef66-86fc-0f2e-1dda6ec8e9c1.jpeg)
※厳密に170.0000...㎝ちょうど人などいないので、0%になる。
### ▶累積分布関数(CDF:Cumulative Distribution Function)
- PDFの負の無限大からある値xまでの確率の合計を返す関数
- PDFでは面積を計算する必要があったが、CDFではy軸が確率の累積なので、$y_{x_1}-y_{x_2}$とすることで簡単に確率を求めることができる

![stats_CDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/493b5d3e-eafd-0a02-9610-72b949372095.jpeg)
- CDFを使用することで、複数のPDFの差異を比べることができる

![stats_CDF_2PDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/aad6fdc2-e8c6-0a9a-4737-32f1a58babe9.jpeg)
```python
# pdfを描画(正規分布)
x = np.linspace(-3, 3, 100)
y = stats.norm.pdf(x)
plt.plot(x, y)
```
![stats_norm_PDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/7dbe6cd5-2804-f233-409d-c7dd0935d4fb.jpeg)
```python
# 正規分布のCDF
y = stats.norm.cdf(x)
plt.plot(x, y)
```
![stats_norm_CDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/0604eec9-c785-be98-899a-c9625e9aebed.jpeg)
```python
# CDFの右側の値を求める survival function(sf) =  1 - cdf
y = stats.norm.sf(x)
plt.plot(x, y)
```
![stats_sf_CDF.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/98f7d726-e87a-7a27-3d9b-c2d79cc683fc.jpeg)
### ▶正規分布(normal distribution)
- 最もよく使われる分布で、統計学の理論でもよく出てくる
- 自然界にも多く見られる(身長、誤差)
- 平均$μ$と分散$σ^2$で形状が決まる$N(μ,σ^2)$
- 分散が大きいとグラフの傾きが緩やかになり、小さいと急になる
- 平均1、標準偏差1(分散1)の正規分布を標準正規分布という

以下の式で表される
$\frac{1}{\sqrt{2\piσ^2}}exp(-\frac{(x-μ)^2}{2σ^2})$

`stats.norm.pdf(x, loc=μ, scale=σ)`もしくは`stats.norm(loc=μ, scale=σ).pdf(x)`で平均$μ$、分散$σ^2$の正規分布におけるxの値を返す。
```python
# さまざまな正規分布を描画
x = np.linspace(-5, 15, 100)
y = stats.norm(loc=10, scale=3).pdf(x)
y2 = stats.norm(loc=8, scale=3).pdf(x)
y3 = stats.norm(loc=8, scale=1).pdf(x)
plt.plot(x, y)
plt.plot(x, y2)
plt.plot(x, y3)
```
![stats_norm.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/0128c27b-e344-18ef-5652-96d688c8478f.jpeg)
### ▶カーネル密度推定(KDE:Kernel Density Estimation)
- 観測した分布から確率密度関数(確率密度分布)を推定する
- `sns.distplot()`でプロットされるグラフにデフォルトで描かれる確率密度関数がまさにKDE
- 逆に考えると観測されて分布はある確率密度関数から出力された結果と捉えることもできる
- 推定方法は観測された値それぞれにカーネルを割り当て、そのカーネルを足し合わせることで推定を作成している

![stats_KDE.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/9a450400-668d-3ae1-c8dd-b9a2eaac8cde.jpeg)
`stats.gaussian_kde(df).pdf(x)`であるdataset(df)のKDEを推定し、xの時の値を返す。
`stats.gaussian_kde(df).resample()`でKDEから新たにdatasetを生成する。

KDEはあくまで推定なので、欲しい境界外の値が生成されることがある。(例えば負の値)
その時は`2 * bound - x`(bound:境界値)でミラーリングすることで、境界外の値を使用できるようになる。
```python
# tipカラムの確率分布をKDEで推定する
x = np.linspace(-3, 10, 100)
y = stats.gaussian_kde(df['tip']).pdf(x)
plt.plot(x, y)
```
![stats_KDE_plot().jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/d8eaf21f-6298-5815-9f44-3140b237c7c4.jpeg)
```python
# KDEした結果の確率分布からresample(再度データを抽出)することができる
resample_data = stats.gaussian_kde(df['tip']).resample()

# 境界外(今回であれば0)の値をミラーリング
bound = 0
resample_data = np.where(resample_data < bound, # Condition
                         2 * bound - resample_data, # True
                         resample_data) # False
```
## ■正規分布と標準化
### ▶68-95-99.7ルール
- 平均$μ$、分散$σ^2$の正規分布におけるデータの分布のルール

![stats_68-95-99.7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/a32e1b83-2a7c-82c2-2408-7ec23d4e596e.jpeg)
#### Challenge
実際に平均4、標準偏差3の正規分布から値をランダムに1000件生成し、68-95-99.5ルールに従うのか確認する。
<details><summary>解答例</summary>

```python
# 68-95-99.7ルールを確認
group1 = []
group2 = []
group3 = []
samples = []
loc = 4
scale = 3

for i in range(1000):
    sample = stats.norm(loc=loc, scale=scale).rvs()
    if loc-scale < sample < loc+scale:
        group1.append(sample)
    elif loc-2*scale < sample < loc+2*scale:
        group2.append(sample)
    elif loc-3*scale < sample < loc+3*scale:
        group3.append(sample)
    samples.append(sample)

# 68%
print(len(group1)/len(samples))

# 95%
print((len(group1) + len(group2))/len(samples))

# 99.5%
print((len(group1) + len(group2) + len(group3))/len(samples))
```
</details>

### ▶標準化(standardize)
- 平均を0、分散を0にすること(z得点)
- 標準化することで、尺度をそろえることができ、複数のデータを比較することができる
- 標準化する際には各値から平均を引いて、標準偏差で割ることで可能

下記のような式になる
$z=\frac{x-\bar{x}}{s}$ ($\bar{x}$：平均、s：標準偏差)
### ▶偏差値(T-score)
- 平均を50、標準偏差を10にすること(T得点)
- 標準化した値に「×10 + 50」をすることで偏差値を算出できる
- もちろん「68-95-99.7ルール」を適用可能
### ▶標準正規分布(standard normal distribution)
- 正規分布を標準化したもの(平均μが0、分散σが1の正規分布)

下記のような式になる
$\frac{1}{\sqrt{2\pi}}(-\frac{x^2}{2})$

標準化は機械学習の前処理でよく使用され、`sklearn.preprocessing.StandardScaler`というライブラリを使用することで、簡単に標準化することができる。

`StandardScaler()`でインスタンスを生成し、そのインスタンスに対して`.fit_transform()`というメソッドを読んであげればいい。

`.fit_transform()`に渡すdataは2次元である必要がある(そもそもが複数データを一度に標準化するのが目的のメソッドのため)ので、1次元(1種類のデータ)の場合は`.reshape(-1, 1)`で2次元にするか、`.append(list)`でリストの中に格納する必要がある。
```python
# インポート
from sklearn.preprocessing import StandardScaler

# DataFrameのひとつのデータを標準化
scaler = StandardScaler()
scaled = scaler.fit_transform(df['tip_rate'].values.reshape(-1, 1))

# 複数データの場合はそのままdataを渡せばいい
scaled = scaler.fit_transform(df['tip_rate', 'total_bill', 'tip'])

# 偏差値
t-score = scaled * 10 + 50
```
### ▶二項分布(Biomial distribution)
- 様々な確率分布の基礎となる分布
- ある観察や試行を複数回行った際に、ある事象aが起こる回数が従う確率分布

例：サイコロを三回振って、2が回出でる確率
![stats_binomial_sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/69e2a176-a677-d5b9-ee48-c421ced44bc5.jpeg)

2が1回出るパターンは3回の試行の内、「1回目に出る、2回目に出る、3回目に出る」の3パターンがある。2が2回出るパターンも同様に3パターンある。
なので、$_3C_1$や$_3C_2$といった場合分けが必要となる。

一般化すると下記のような式になる。
$P(x)=_nC_xp^xq^{n-x}=\frac{n!}{x!(n-x)!}p^xq^{n-x}$ ※ただし、$q=1-p$

`stats.binom(n, p)`で二項分布の確率分布を求めることができる。(n：試行回数、p：確率)
`stats.binom(n, p).pmf(x)`である値xの時の確率yを算出することができる。
```python
# 二項分布の描画
n = 3
x = np.arange(n+1)
y = stats.binom(n=n, p=1/6).pmf(x)
plt.plot(x, y, 'o')
plt.vlines(x, 0, y)
```
![stats_binomial.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/bc1ca35c-b61a-fb97-d6d2-3e0ed66f49cf.jpeg)
### ▶二項分布と正規分布
- 二項分布のnを無限大にした分布は正規分布に近似する
(npを一定(nが大きくなるにつれてpを小さくする)にしてnを無限大にした場合はポアソン分布に近似するので注意)
※詳しくは[こちら](https://datawokagaku.com/poisson_dist/)
- nを無限大にした際の正規分布は、平均はnp、分散はnpqに近似する
#### Challenge
実際にnを大きくした際に二項分布が正規分布(N(np, npq))に近似していく様を確認する。
<details><summary>解答例</summary>

```python
# 二項分布
n = 100
p = 1/6
x = np.arange(n+1)
y = stats.binom(n, p).pmf(x)
plt.plot(x, y, 'o')
```
![stats_binomial_challenge.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/55e66818-9d05-ac12-8e47-7c3a5b88d685.jpeg)
```python
# 正規分布
y2 = stats.norm(n*p, np.sqrt(n*p*(1-p))).pdf(x)
plt.plot(x, y2)
plt.plot(x, y, 'o')
```
![stats_normal_challenge.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3448232/77033f7c-9a93-c152-3100-8a02d91d762b.jpeg)
```python
# 正規分布の平均=np
stats.binom(n, p).mean()  # -> 16.666666666666664

# 正規分布の分散=np(1-p)
stats.binom(n, p).var()  # -> 13.888888888888888

# 二項分布の平均の近似
n*p  # -> 16.666666666666664

# 二項分布の分散の近似
n*p*(1-p)  # -> 13.888888888888888
```
</details>

#### 次の記事
[Python初心者の備忘録 #13 ~統計学入門編03~](https://qiita.com/Yushin-Tati/items/883b4ce13e186ae9fa06)