ポケモンの種族値で学ぶ統計学：t検定・分散分析・回帰・カイ二乗・ロジスティック回帰


## 概要

ポケモンの種族値は、攻撃、特攻、素早さ、HPなどの6つの能力値で構成されています。これらのデータは統計学の教材として非常に有用です。本記事では、以下の6つの統計的分析を用いて、ポケモン種族値の違いや傾向を明らかにします。

---

## 1. **t検定：平均値の差を比較する**

### 例題：

「伝説ポケモン」と「一般ポケモン」の攻撃種族値の平均に有意差はあるか？

### 方法：

* 群1: 伝説ポケモンの攻撃種族値
* 群2: 一般ポケモンの攻撃種族値
* 検定：2標本t検定（Welchのt検定）

### 解釈：

p値 < 0.05 なら「伝説ポケモンの方が平均攻撃値が高い」と有意に言える。

---

```python
# Program Name: anova_pokemon_speed.py  
# Creation Date: 20250603  
# Overview: One-way ANOVA comparing Speed across evolutionary stages (basic, middle, final)  
# Usage: Run in Python environment with pandas, scipy, matplotlib installed  

# -------------------- Install Required Libraries --------------------

# -------------------- Import Libraries --------------------
import pandas as pd
import scipy.stats as stats
import seaborn as sns
import matplotlib.pyplot as plt

# -------------------- Parameter: Sample Pokémon Data --------------------
# データの定義（進化段階と素早さ） / Define sample data (evolution stage and speed)
data = {
    'Name': ['Pichu', 'Pikachu', 'Raichu',
             'Charmander', 'Charmeleon', 'Charizard',
             'Bulbasaur', 'Ivysaur', 'Venusaur',
             'Gastly', 'Haunter', 'Gengar'],
    'Stage': ['Basic', 'Middle', 'Final'] * 4,  # 進化段階 / Evolutionary stage
    'Speed': [60, 90, 110, 65, 80, 100, 45, 60, 80, 80, 95, 110]  # 素早さ / Speed stat
}

df = pd.DataFrame(data)

# -------------------- Group by Stage --------------------
# 進化段階ごとに素早さを抽出 / Extract speed data by evolution stage
group_basic = df[df['Stage'] == 'Basic']['Speed']
group_middle = df[df['Stage'] == 'Middle']['Speed']
group_final = df[df['Stage'] == 'Final']['Speed']

# -------------------- Perform One-Way ANOVA --------------------
# 一元配置分散分析の実行 / Run one-way ANOVA
f_stat, p_val = stats.f_oneway(group_basic, group_middle, group_final)

# -------------------- Display Result --------------------
print("=== One-Way ANOVA Result ===")
print(f"F-statistic: {f_stat:.4f}")
print(f"p-value: {p_val:.4f}")

# -------------------- Interpretation --------------------
# 結果の解釈表示 / Interpretation of result
if p_val < 0.05:
    print("=> Significant difference in Speed across evolution stages.")
    print("=> 進化段階ごとに素早さに有意な差があります。")
else:
    print("=> No significant difference in Speed across evolution stages.")
    print("=> 素早さに進化段階ごとの有意差は見られません。")

# -------------------- Visualization --------------------
# 箱ひげ図で可視化 / Visualize using boxplot
plt.figure(figsize=(8, 6))
sns.boxplot(x='Stage', y='Speed', data=df)
plt.title("Speed Distribution by Evolutionary Stage")
plt.xlabel("Evolutionary Stage")
plt.ylabel("Speed Stat")
plt.show()


```


## 2. **分散分析（ANOVA）：3群以上の平均差の検定**

### 例題：

「進化段階（未進化・中間・最終進化）」によって素早さに違いはあるか？

### 方法：

* 説明変数：進化段階（カテゴリ変数）
* 目的変数：素早さ種族値（連続変数）
* 検定：一元配置分散分析（One-way ANOVA）

### 解釈：

有意差あり → 「どの進化段階が他と違うか」は事後検定（Tukey等）で確認。

```python
# Program Name: anova_pokemon_speed_posthoc.py  
# Creation Date: 20250603  
# Overview: One-way ANOVA and post hoc Tukey HSD test for Speed across evolution stages  
# Usage: Run in Python environment with pandas, statsmodels, matplotlib, seaborn installed  

# -------------------- Install Required Libraries --------------------


# -------------------- Import Libraries --------------------
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from statsmodels.formula.api import ols
import statsmodels.api as sm
from statsmodels.stats.multicomp import pairwise_tukeyhsd

# -------------------- Parameter: Sample Pokémon Data --------------------
# データの定義 / Define Pokémon speed data by evolutionary stage
data = {
    'Name': ['Pichu', 'Pikachu', 'Raichu',
             'Charmander', 'Charmeleon', 'Charizard',
             'Bulbasaur', 'Ivysaur', 'Venusaur',
             'Gastly', 'Haunter', 'Gengar'],
    'Stage': ['Basic', 'Middle', 'Final'] * 4,  # 進化段階 / Evolutionary Stage
    'Speed': [60, 90, 110, 65, 80, 100, 45, 60, 80, 80, 95, 110]  # 素早さ / Speed Stat
}
df = pd.DataFrame(data)

# -------------------- One-Way ANOVA --------------------
# 一元配置分散分析 / One-way ANOVA
model = ols('Speed ~ C(Stage)', data=df).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
print("=== One-Way ANOVA Result ===")
print(anova_table)

# -------------------- Post Hoc Test (Tukey HSD) --------------------
# 有意差があればTukey検定を実施 / Perform Tukey HSD if p < 0.05
if anova_table["PR(>F)"][0] < 0.05:
    print("\n=> Significant difference found. Performing Tukey HSD test...")
    tukey_result = pairwise_tukeyhsd(df['Speed'], df['Stage'])
    print(tukey_result)
else:
    print("\n=> No significant differences detected between evolution stages.")

# -------------------- Visualization --------------------
# 箱ひげ図で視覚化 / Visualize using boxplot
plt.figure(figsize=(8, 6))
sns.boxplot(x='Stage', y='Speed', data=df)
plt.title("Speed Distribution by Evolutionary Stage")
plt.xlabel("Evolutionary Stage")
plt.ylabel("Speed Stat")
plt.show()


```

---

## 3. **単回帰分析：1変数で予測する**

### 例題：

「素早さ」から「合計種族値」をどれくらい予測できるか？

### 方法：

* 説明変数：素早さ
* 目的変数：合計種族値
* 手法：単回帰分析（線形回帰）

### 解釈：

回帰係数が正 → 素早さが高いほど、合計種族値も高くなる傾向。
```python

# Program Name: simple_regression_speed_total.py  
# Creation Date: 20250603  
# Overview: Simple linear regression to predict total base stat from speed  
# Usage: Run in Python environment with pandas, sklearn, matplotlib installed  

# -------------------- Install Required Libraries --------------------


# -------------------- Import Libraries --------------------
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# -------------------- Parameter: Sample Pokémon Data --------------------
# データ定義（素早さと合計種族値） / Define sample data: Speed and Total Stats
data = {
    'Name': ['Pikachu', 'Charizard', 'Blastoise', 'Venusaur', 'Gengar', 'Machamp', 'Dragonite', 'Snorlax'],
    'Speed': [90, 100, 78, 80, 110, 55, 80, 30],  # 素早さ / Speed stat
    'Total': [320, 534, 530, 525, 500, 505, 600, 540]  # 合計種族値 / Total base stat
}
df = pd.DataFrame(data)

# -------------------- Reshape Data --------------------
# 説明変数と目的変数の定義 / Define explanatory and target variables
X = df[['Speed']]  # 説明変数 / Speed
y = df['Total']    # 目的変数 / Total base stat

# -------------------- Fit Simple Linear Regression --------------------
# 単回帰モデルの学習 / Fit simple regression model
model = LinearRegression()
model.fit(X, y)

# -------------------- Show Coefficients --------------------
print("=== Simple Linear Regression Result ===")
print(f"Intercept (切片): {model.intercept_:.2f}")
print(f"Coefficient (回帰係数): {model.coef_[0]:.2f}")

# -------------------- Interpretation --------------------
# 解釈 / Interpretation
if model.coef_[0] > 0:
    print("=> Positive correlation: Speed increases → Total stat tends to increase.")
    print("=> 素早さが高いほど、合計種族値も高くなる傾向があります。")
else:
    print("=> No positive correlation detected.")
    print("=> 素早さと合計種族値に正の相関は見られません。")

# -------------------- Visualization --------------------
# 散布図と回帰直線の描画 / Plot scatter and regression line
plt.figure(figsize=(8, 6))
plt.scatter(df['Speed'], df['Total'], color='blue', label='Data Points')
plt.plot(df['Speed'], model.predict(X), color='red', label='Regression Line')
plt.title("Regression: Total Base Stat vs. Speed")
plt.xlabel("Speed")
plt.ylabel("Total Base Stat")
plt.legend()
plt.show()

```

---

## 4. **重回帰分析：複数変数で予測する**

### 例題：

「攻撃」「特攻」「素早さ」から合計種族値を予測できるか？

### 方法：

* 説明変数：攻撃、特攻、素早さ
* 目的変数：合計種族値
* 手法：重回帰分析

### 解釈：

各係数の大きさ・有意性から、どの能力が合計種族値に最も寄与しているかが分かる。
```python

# Program Name: multiple_regression_stats_total.py  
# Creation Date: 20250603  
# Overview: Multiple linear regression to predict total base stat from Attack, Sp. Atk, and Speed  
# Usage: Run in Python environment with pandas, sklearn, matplotlib installed  

# -------------------- Install Required Libraries --------------------


# -------------------- Import Libraries --------------------
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
import numpy as np

# -------------------- Parameter: Sample Pokémon Data --------------------
# サンプルデータ定義（攻撃・特攻・素早さ・合計種族値） / Define sample Pokémon stats
data = {
    'Name': ['Pikachu', 'Charizard', 'Blastoise', 'Venusaur', 'Gengar', 'Machamp', 'Dragonite', 'Snorlax'],
    'Attack': [55, 84, 83, 82, 65, 130, 134, 110],        # 攻撃 / Attack
    'SpAtk':  [50, 109, 85, 100, 130, 65, 100, 65],       # 特攻 / Sp. Atk
    'Speed':  [90, 100, 78, 80, 110, 55, 80, 30],         # 素早さ / Speed
    'Total':  [320, 534, 530, 525, 500, 505, 600, 540]    # 合計種族値 / Total base stat
}
df = pd.DataFrame(data)

# -------------------- Define Features and Target --------------------
# 説明変数と目的変数の設定 / Define explanatory variables and target
X = df[['Attack', 'SpAtk', 'Speed']]  # 説明変数 / Features
y = df['Total']                       # 目的変数 / Target

# -------------------- Fit Multiple Regression Model --------------------
# 重回帰モデルの学習 / Fit multiple regression model
model = LinearRegression()
model.fit(X, y)

# -------------------- Show Coefficients --------------------
print("=== Multiple Linear Regression Result ===")
print(f"Intercept (切片): {model.intercept_:.2f}")
for feature, coef in zip(X.columns, model.coef_):
    print(f"Coefficient for {feature}: {coef:.2f}")

# -------------------- Model Evaluation --------------------
r_squared = model.score(X, y)
print(f"\nR-squared (決定係数): {r_squared:.4f}")

# -------------------- Interpretation --------------------
# 解釈 / Interpretation
print("\n=> 各係数の大きさから、合計種族値に対する寄与度が分かります。")
print("=> R-squaredが高ければ、高精度で予測できていることを意味します。")

```

---

## 5. **クロス集計とカイ二乗検定：カテゴリ変数の関係**

### 例題：

「ポケモンのタイプ」と「伝説ポケモンか否か」は関係があるか？

### 方法：

* 2つのカテゴリ変数でクロス集計表を作成
* カイ二乗検定を実施

### 解釈：

p値 < 0.05 → 「タイプと伝説の関係には偏りがある」と判断
```python
# Program Name: chi2_test_type_vs_legendary.py  
# Creation Date: 20250603  
# Overview: Chi-square test of independence between Pokémon Type and Legendary status  
# Usage: Run in Python environment with pandas and scipy installed  

# -------------------- Install Required Libraries --------------------


# -------------------- Import Libraries --------------------
import pandas as pd
from scipy.stats import chi2_contingency

# -------------------- Parameter: Sample Pokémon Data --------------------
# データ定義（タイプと伝説か否か） / Define sample data (Type and Legendary status)
data = {
    'Name': ['Articuno', 'Zapdos', 'Moltres', 'Pikachu', 'Charizard', 'Machamp', 'Gyarados', 'Dragonite'],
    'Type': ['Ice', 'Electric', 'Fire', 'Electric', 'Fire', 'Fighting', 'Water', 'Dragon'],  # タイプ / Type
    'Legendary': ['Yes', 'Yes', 'Yes', 'No', 'No', 'No', 'No', 'Yes']  # 伝説か否か / Legendary or not
}
df = pd.DataFrame(data)

# -------------------- Create Contingency Table --------------------
# クロス集計表の作成 / Create a crosstab of Type and Legendary
contingency_table = pd.crosstab(df['Type'], df['Legendary'])

# -------------------- Perform Chi-Square Test --------------------
# カイ二乗検定の実行 / Perform chi-square test
chi2, p, dof, expected = chi2_contingency(contingency_table)

# -------------------- Display Results --------------------
print("=== Chi-Square Test: Type vs Legendary ===")
print("Contingency Table:")
print(contingency_table)
print(f"\nChi2 Statistic: {chi2:.4f}")
print(f"p-value: {p:.4f}")
print(f"Degrees of Freedom: {dof}")
print("Expected Frequencies:")
print(pd.DataFrame(expected, index=contingency_table.index, columns=contingency_table.columns))

# -------------------- Interpretation --------------------
# 解釈 / Interpretation
if p < 0.05:
    print("\n=> Significant association: Type and Legendary status are not independent.")
    print("=> タイプと伝説の関係には有意な偏りがあります。")
else:
    print("\n=> No significant association: Type and Legendary status are independent.")
    print("=> タイプと伝説の関係に有意な偏りは見られません。")


```

---

## 6. **ロジスティック回帰分析：確率を予測する**

### 例題：

「攻撃」「素早さ」「特攻」から、ポケモンが伝説かどうかを予測できるか？

### 方法：

* 説明変数：連続変数（攻撃、特攻、素早さ）
* 目的変数：2値（伝説:1, 一般:0）
* 手法：ロジスティック回帰

### 解釈：

係数が正 → 該当能力が高いほど伝説である確率が高くなる。
```python
# Program Name: logistic_regression_legendary_prediction.py  
# Creation Date: 20250603  
# Overview: Logistic regression to predict Legendary status from Attack, Sp. Atk, and Speed  
# Usage: Run in Python environment with pandas, sklearn, matplotlib installed  

# -------------------- Install Required Libraries --------------------


# -------------------- Import Libraries --------------------
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt

# -------------------- Parameter: Sample Pokémon Data --------------------
# サンプルデータ（攻撃、特攻、素早さ、伝説かどうか） / Sample data
data = {
    'Name': ['Articuno', 'Zapdos', 'Moltres', 'Pikachu', 'Charizard', 'Machamp', 'Gyarados', 'Dragonite'],
    'Attack': [85, 90, 100, 55, 84, 130, 125, 134],       # 攻撃 / Attack
    'SpAtk':  [95, 125, 125, 50, 109, 65, 60, 100],       # 特攻 / Special Attack
    'Speed':  [85, 100, 90, 90, 100, 55, 81, 80],         # 素早さ / Speed
    'Legendary': [1, 1, 1, 0, 0, 0, 0, 1]                # 伝説:1 一般:0 / Legendary status
}
df = pd.DataFrame(data)

# -------------------- Define Features and Target --------------------
# 説明変数と目的変数の定義 / Define X (features) and y (target)
X = df[['Attack', 'SpAtk', 'Speed']]
y = df['Legendary']

# -------------------- Fit Logistic Regression Model --------------------
# ロジスティック回帰モデルの学習 / Fit model
model = LogisticRegression()
model.fit(X, y)

# -------------------- Show Coefficients --------------------
print("=== Logistic Regression Coefficients ===")
for feature, coef in zip(X.columns, model.coef_[0]):
    print(f"{feature}: {coef:.4f}")
print(f"Intercept (切片): {model.intercept_[0]:.4f}")

# -------------------- Prediction and Evaluation --------------------
# 予測と評価 / Predict and evaluate
y_pred = model.predict(X)
print("\n=== Classification Report ===")
print(classification_report(y, y_pred))
print("=== Confusion Matrix ===")
print(confusion_matrix(y, y_pred))

# -------------------- Interpretation --------------------
# 解釈 / Interpretation
print("\n=> 各係数が正であれば、その能力値が高いほど伝説ポケモンである確率が高いことを意味します。")

```

---

## 終わりに

ポケモンの種族値を使った統計分析は、親しみやすい題材でありながら本格的な統計手法を実践的に学べる絶好の教材です。本記事の内容は、統計検定2級〜準1級レベルに相当し、データサイエンス初学者の導入としても優れています。