統計検定2級　中心極限定理

## はじめに
統計検定2級の中心極限定理に関して学んでいきます。

## ✅ 定理の内容

> **「母集団の分布が何であっても、十分なサイズの標本の平均は正規分布に従う」**

---

## 💡 もっと具体的に言うと：

- どんな形の分布（例：偏った分布、歪んだ分布）であっても…
- サイズ $n$ の標本をたくさん取ってその **平均** を集めると…
- その平均値の分布は、**正規分布に近づいていく**。

---

## 🧮 数式で表すと

母平均 $\mu$、母標準偏差 $\sigma$ のとき、標本平均 $\bar{X}$ の分布は：

$$
\bar{X} \sim N\left(\mu, \frac{\sigma^2}{n}\right)
$$

ただし、$n$ が大きいほどこの正規分布に近くなる。

---

## 🎲 例：サイコロで考えてみる

サイコロの出目（1〜6）は一様分布です。でも…

- サイコロを **30回** 振って **平均値を記録**  
- それを **1000回** 繰り返してヒストグラムにすると…

→ その分布は **正規分布にそっくりになります！**


```python:
# 日本語フォントのインストール（Google Colab用）
!apt-get -y install fonts-ipafont-gothic > /dev/null

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.font_manager import FontProperties

# 日本語フォントを設定
jp_font = FontProperties(fname="/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf")
plt.rcParams["font.family"] = jp_font.get_name()

# 乱数シード（再現性のため）
np.random.seed(0)

# サイコロを30回振って平均をとる操作を1000回繰り返す
sample_means = []
for _ in range(1000):
    sample = np.random.randint(1, 7, size=30)  # サイコロの出目（1〜6）
    sample_means.append(np.mean(sample))

# 可視化
plt.figure(figsize=(10, 5))
sns.histplot(sample_means, kde=True, color="skyblue")
plt.title("サイコロ30回の平均値の分布（中心極限定理）", fontproperties=jp_font)
plt.xlabel("平均値", fontproperties=jp_font)
plt.ylabel("度数", fontproperties=jp_font)
plt.grid(True)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/9659f191-6411-43e7-8f42-a19265cab696.png)

:::note info
それはそうだろう。サイコロの値の平均をとったら3.5に近づくでしょうよ。
一様分布はこれで納得。
:::

## 指数分布
お客さんが次に来るまでの時間」「機械が壊れるまでの時間

```py:
# フォントインストール（Colab用）
!apt-get -y install fonts-ipafont-gothic > /dev/null

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.font_manager import FontProperties

# 日本語フォント設定
jp_font = FontProperties(fname="/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf")
plt.rcParams["font.family"] = jp_font.get_name()

# 乱数シード
np.random.seed(0)

# 指数分布（平均=1）からの標本平均（n=30）
sample_means = []
for _ in range(1000):
    sample = np.random.exponential(scale=1.0, size=30)
    sample_means.append(np.mean(sample))

# 可視化
plt.figure(figsize=(10, 5))
sns.histplot(sample_means, kde=True, color="orange")
plt.title("指数分布から得た標本平均の分布（中心極限定理）", fontproperties=jp_font)
plt.xlabel("平均値", fontproperties=jp_font)
plt.ylabel("度数", fontproperties=jp_font)
plt.grid(True)
plt.show()
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/8d12a07e-2ba4-430e-b995-3262d9dd2035.png)


📘 練習問題：年収データの分析（対数正規分布）

ある業界で働く人たちの年収（万円）は対数正規分布に従っている。調査の結果、年収の対数値（log年収）が以下のような正規分布に従うことがわかった：
	•	対数平均 $\mu = 5.3$
	•	対数標準偏差 $\sigma = 0.5$

この分布に従う1,000人分の年収データを生成して、以下の問いに答えなさい：

・問1
年収の 中央値、平均、歪度（skewness） を求めよ。

```shell:
【統計量】
中央値: 194.61 万円
平均: 221.32 万円
歪度: 1.50

【年収分布表】
| 年収区間          |   人数（人） |
|:------------------|-------------:|
| 〜100万円未満     |           84 |
| 100〜200万円未満  |          439 |
| 200〜300万円未満  |          281 |
| 300〜500万円未満  |          160 |
| 500〜1000万円未満 |           36 |
| 1000万円以上      |            0 |
```


```python:
# 📌 Google Colab専用：日本語フォントのインストール
!apt-get -y install fonts-ipafont-gothic > /dev/null

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.font_manager import FontProperties, findfont, FontManager
from scipy.stats import skew

# 日本語フォント設定
jp_font = FontProperties(fname="/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf")
plt.rcParams['font.family'] = jp_font.get_name()

# データ生成
mu = 5.3
sigma = 0.5
n = 1000
np.random.seed(0)
incomes = np.random.lognormal(mean=mu, sigma=sigma, size=n)

# 統計量
median_income = np.median(incomes)
mean_income = np.mean(incomes)
skew_income = skew(incomes)

print("【統計量】")
print(f"中央値: {median_income:.2f} 万円")
print(f"平均: {mean_income:.2f} 万円")
print(f"歪度: {skew_income:.2f}")

# 年収区間表
bins = [0, 100, 200, 300, 500, 1000, np.inf]
labels = ['〜100万円未満', '100〜200万円未満', '200〜300万円未満', '300〜500万円未満', '500〜1000万円未満', '1000万円以上']
bin_counts = pd.cut(incomes, bins=bins, labels=labels).value_counts().sort_index()
df_table = pd.DataFrame({'年収区間': labels, '人数（人）': bin_counts.values})
display(df_table)

# 🎯 中心極限定理を使って標本平均を取得
sample_size = 30
sample_means = [np.mean(np.random.choice(incomes, size=sample_size)) for _ in range(1000)]

# 🎨 グラフ描画（並列で2つ）
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 対数正規分布の生データ
sns.histplot(incomes, bins=40, kde=True, color='teal', ax=axes[0])
axes[0].set_title("対数正規分布に従う年収データ", fontproperties=jp_font)
axes[0].set_xlabel("年収（万円）", fontproperties=jp_font)
axes[0].set_ylabel("人数", fontproperties=jp_font)
axes[0].grid(True)

# 標本平均の分布（中心極限定理）
sns.histplot(sample_means, bins=40, kde=True, color='skyblue', ax=axes[1])
axes[1].set_title("標本平均の分布（中心極限定理の適用）", fontproperties=jp_font)
axes[1].set_xlabel("標本平均（万円）", fontproperties=jp_font)
axes[1].set_ylabel("度数", fontproperties=jp_font)
axes[1].grid(True)

plt.tight_layout()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/a0e5ecc5-3e2a-4d80-bc2d-7cd320c5646a.png)

:::note info
一瞬、あれ？って思うんですが、正しそうですね。中心極限定理をかけると、どうして左の図から右の図になるの？？左の図だと再頻出値は140〜180くらい。でも右の図だと、220くらいになっている。
あれ、間違ってないか？と思いますが、あってます。
単純無作為抽出で30個取り平均年収を算出、これを1000回繰り返すと、母平均の値と一致するよねって考え方です。これは不偏推定量の考え方ですね！
:::