分布の散らばりを評価するための指標と方法


## はじめに

データ分析では、データの中心だけでなく、データの散らばり（ばらつき）も理解することが重要です。本記事では、データの散らばりを評価するためのさまざまな指標と方法について、具体的な例を交えながら解説します。

## 分散と標準偏差

### 分散

分散はデータの散らばりの大きさを表す指標です。データポイントが平均値からどれだけ離れているかを測定します。例えば、テストの点数が平均点からどれだけばらついているかを知りたい場合に使います。

分散の計算式は次の通りです：

$$
\text{分散} = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2
$$

ここで、$n$ はデータポイントの数、$x_i$ は各データポイント、$\bar{x}$ はデータの平均です。

### 計算方法

例えば、5人の学生のテストの点数が [50, 60, 70, 80, 90] だとします。このデータの分散を計算する手順は次の通りです：

1. 平均を計算します：
$$
\bar{x} = \frac{50 + 60 + 70 + 80 + 90}{5} = 70
$$

2. 各データポイントと平均の差を二乗します：
$$
(50 - 70)^2 = 400, (60 - 70)^2 = 100, (70 - 70)^2 = 0, (80 - 70)^2 = 100, (90 - 70)^2 = 400
$$

3. 差の二乗の合計をデータポイントの数-1で割ります：
$$
\text{分散} = \frac{400 + 100 + 0 + 100 + 400}{5 - 1} = \frac{1000}{4} = 250
$$

### 標準偏差

標準偏差は分散の平方根で、データの散らばりを直感的に理解しやすくします。

$$
\text{標準偏差} = \sqrt{\text{分散}} = \sqrt{250} \approx 15.81
$$

### Pythonでの実装

次に、Pythonを使って分散と標準偏差を計算します。

```python
import numpy as np

data = [50, 60, 70, 80, 90]
variance = np.var(data, ddof=1)
std_dev = np.std(data, ddof=1)
print(f'分散: {variance}, 標準偏差: {std_dev}')
```
![スクリーンショット 2024-07-01 17.02.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/4781ffd0-8f6f-d08d-a429-7a1e2cf26c11.png)

## 範囲（最小値、最大値）

範囲はデータセットの最大値と最小値の差を示す単純な指標です。範囲が大きいほどデータの散らばりが大きいことを意味します。

$$
\text{範囲} = \text{最大値} - \text{最小値}
$$

### 計算方法

先ほどのテストの点数 [50, 60, 70, 80, 90] の範囲を計算します。最大値は90、最小値は50なので、

$$
\text{範囲} = 90 - 50 = 40
$$

### Pythonでの実装

次に、Pythonを使って範囲を計算します。

```python
data_range = max(data) - min(data)
print(f'範囲: {data_range}')
```
![スクリーンショット 2024-07-01 17.03.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/4f7dd968-2e70-520f-9f05-2119c771bc26.png)

## 四分位範囲（IQR）

四分位範囲（Interquartile Range, IQR）はデータの中央50%の散らばりを測定します。第1四分位数（Q1）と第3四分位数（Q3）の差を計算します。

$$
\text{IQR} = Q3 - Q1
$$

### 計算方法

テストの点数 [50, 60, 70, 80, 90] の四分位範囲を計算します。

1. データを昇順に並べ替えます（既に並んでいます）。
2. 第1四分位数（Q1）は25パーセンタイルの値で、60です。
3. 第3四分位数（Q3）は75パーセンタイルの値で、80です。

したがって、

$$
\text{IQR} = 80 - 60 = 20
$$

### Pythonでの実装

次に、Pythonを使って四分位範囲を計算します。

```python
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
print(f'第1四分位数 (Q1): {Q1}, 第3四分位数 (Q3): {Q3}, 四分位範囲 (IQR): {IQR}')
```
![スクリーンショット 2024-07-01 17.03.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/6d9b0fd0-a988-d42e-8e86-e5023817dc96.png)

## 箱ひげ図

箱ひげ図はデータの分布を視覚的に表現する方法です。データの最小値、Q1、中央値（Q2）、Q3、最大値を表示します。外れ値も視覚的に確認できます。

### 例

テストの点数 [50, 60, 70, 80, 90] の箱ひげ図を作成します。

```python
!pip install japanize-matplotlib
import matplotlib.pyplot as plt
import japanize_matplotlib

plt.boxplot(data)
plt.title('箱ひげ図')
plt.show()
```
![スクリーンショット 2024-07-01 17.07.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/be146990-04e5-063c-3a26-2702cc1a7a2d.png)


## ローレンツ曲線とジニ係数

### ローレンツ曲線

ローレンツ曲線は所得分布の不平等を視覚的に示す方法です。横軸に人口の累積比率、縦軸に所得の累積比率を取ります。

### 計算方法

例えば、ランダムに生成した所得データ [5, 10, 15, 20, 25] のローレンツ曲線を計算する手順は次の通りです：

1. データを昇順に並べ替えます（既に並んでいます）。
2. 所得の累積合計を計算します：
$$
5, 5+10=15, 15+15=30, 30+20=50, 50+25=75
$$

3. 所得の累積合計を全体の合計で割ります：
$$
\frac{5}{75}, \frac{15}{75}, \frac{30}{75}, \frac{50}{75}, \frac{75}{75}
$$

4. 横軸に人口の累積比率、縦軸に所得の累積比率を取ります。

### ジニ係数

ジニ係数はローレンツ曲線の不平等の程度を数値化したもので、0から1の範囲で値を取り、0は完全な平等、1は完全な不平等を示します。

### 計算方法

ローレンツ曲線の下の面積を $A$ とし、面積の合計を $B$ とすると、ジニ係数 $G$ は次のように計算されます：

$$
G = 1 - 2A
$$

### Pythonでの実装

次に、Pythonを使ってローレンツ曲線とジニ係数を計算します。

```python
import numpy as np
import matplotlib.pyplot as plt

def lorenz_curve(data):
    sorted_data = np.sort(data)
    cumulative_data = np.cumsum(sorted_data) / np.sum(sorted_data)
    cumulative_data = np.insert(cumulative_data, 0, 0)
    return cumulative_data

data = np.random.exponential(scale=1, size=1000)
lorenz = lorenz_curve(data)
plt.plot(np.linspace(0, 1, len(lorenz)), lorenz)
plt.plot([0,1], [0,1], linestyle='--', color='k')
plt.title('ローレンツ曲線')
plt.xlabel('累積人口比率')
plt.ylabel('累積所得比率')
plt.show()

def gini_coefficient(data):
    lorenz = lorenz_curve(data)
    area_under_curve = np.trapz(lorenz, dx=1/len(lorenz))
    return 1 - 2 * area_under_curve

gini = gini

_coefficient(data)
print(f'ジニ係数：{gini:.2f}')
```
![スクリーンショット 2024-07-01 17.08.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/ac9bdbaa-5a07-3137-0c96-779a898c8a7b.png)



## 2つのグラフの視覚的比較

異なるデータセットの分布を比較するために、ヒストグラムやカーネル密度推定（KDE）を用います。

### 例

2つの異なるデータセットの分布を比較します。

```python
import seaborn as sns

data1 = np.random.normal(loc=0, scale=1, size=100)
data2 = np.random.normal(loc=1, scale=2, size=100)

sns.histplot(data1, kde=True, color='blue', label='データ1')
sns.histplot(data2, kde=True, color='orange', label='データ2')
plt.legend()
plt.title('ヒストグラムとKDEによる分布の比較')
plt.show()
```
![スクリーンショット 2024-07-01 17.11.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/5254825b-7d89-a2c0-c109-f66b03522027.png)


## カイ二乗値

カイ二乗値は、観測されたデータが期待される頻度分布からどれだけずれているかを測定する指標です。

### 計算方法

例えば、観測データ [10, 12, 20, 18] と期待されるデータ [15, 15, 15, 15] のカイ二乗値を計算する手順は次の通りです：

1. 観測データと期待されるデータの差の二乗を計算します：
$$
(10 - 15)^2 = 25, (12 - 15)^2 = 9, (20 - 15)^2 = 25, (18 - 15)^2 = 9
$$

2. それぞれの差の二乗を期待されるデータで割ります：
$$
\frac{25}{15}, \frac{9}{15}, \frac{25}{15}, \frac{9}{15}
$$

3. 上記の値を合計します：
$$
\chi^2 = \frac{25}{15} + \frac{9}{15} + \frac{25}{15} + \frac{9}{15} = \frac{68}{15} \approx 4.53
$$

### Pythonでの実装

次に、Pythonを使ってカイ二乗値を計算します。

```python
from scipy.stats import chisquare

observed = [10, 12, 20, 18]
expected = [15, 15, 15, 15]
chi2, p = chisquare(observed, expected)
print(f'カイ二乗値：{chi2:.2f}, p値：{p:.2f}')
```
![スクリーンショット 2024-07-01 17.11.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/d6233ef2-b70d-7684-b040-7b5ded8e23e4.png)

## 歪度と尖度について

### 歪度

**歪度**は、データ分布の対称性を測る指標です。具体的には、データの平均値に対する対称性を表します。

- **正の歪度**：データの右側に長い尾があり、多くのデータが左側に偏っています。
- **負の歪度**：データの左側に長い尾があり、多くのデータが右側に偏っています。
- **歪度が0**：データが左右対称に分布していることを示します。

### 計算方法

例えば、データセット [2, 3, 5, 7, 9, 11] の歪度を計算する手順は次の通りです：

1. 平均を計算します：
$$
\bar{x} = \frac{2 + 3 + 5 + 7 + 9 + 11}{6} = 6.17
$$

2. 各データポイントと平均の差を三乗します：
$$
(2 - 6.17)^3, (3 - 6.17)^3, (5 - 6.17)^3, (7 - 6.17)^3, (9 - 6.17)^3, (11 - 6.17)^3
$$

3. 差の三乗の合計をデータポイントの数で割り、標準偏差の三乗で割ります：
$$
\text{歪度} = \frac{1}{n} \sum_{i=1}^{n} \frac{(x_i - \bar{x})^3}{\text{標準偏差}^3}
$$

### 尖度

**尖度**は、データ分布の峰（ピーク）の鋭さを測る指標です。分布が平均値周辺にどれだけ集中しているかを示します。

- **正の尖度**：鋭い峰を持ち、データが平均値周辺に集中していることを示します。
- **負の尖度**：平らな峰を持ち、データが平均値周辺にあまり集中していないことを示します。
- **尖度が0**：正規分布に似た分布を示します。

### 計算方法

データセット [2, 3, 5, 7, 9, 11] の尖度を計算する手順は次の通りです：

1. 平均を計算します：
$$
\bar{x} = \frac{2 + 3 + 5 + 7 + 9 + 11}{6} = 6.17
$$

2. 各データポイントと平均の差を四乗します：
$$
(2 - 6.17)^4, (3 - 6.17)^4, (5 - 6.17)^4, (7 - 6.17)^4, (9 - 6.17)^4, (11 - 6.17)^4
$$

3. 差の四乗の合計をデータポイントの数で割り、標準偏差の四乗で割ります：
$$
\text{尖度} = \frac{1}{n} \sum_{i=1}^{n} \frac{(x_i - \bar{x})^4}{\text{標準偏差}^4} - 3
$$

### Pythonでの実装

次に、Pythonを使って歪度と尖度を計算します。

```python
from scipy.stats import skew, kurtosis
import numpy as np

data = np.random.normal(loc=0, scale=1, size=1000)  # 正規分布に従うランダムデータ
skewness = skew(data)
kurt = kurtosis(data)
print(f'歪度：{skewness:.2f}, 尖度：{kurt:.2f}')
```
![スクリーンショット 2024-07-01 17.12.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/87b9fdc9-0c5a-964a-4ee2-33823deef78f.png)

## まとめ

この記事では、分布の散らばりを評価するためのさまざまな指標と方法を紹介しました。分散や標準偏差、範囲、四分位範囲、箱ひげ図、ローレンツ曲線、ジニ係数、2つのグラフの視覚的比較、カイ二乗値、歪度、尖度などを使ってデータのばらつきや特性を理解することができます。

これらの指標を活用して、データの特徴をより深く理解し、分析に役立ててください。