0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

初代ポケモン種族値による分類モデル比較:物理型 vs 特殊型

0
Posted at

🔍 概要

本記事では、初代ポケモン151種の「攻撃(Attack)」および「特攻(Sp. Atk)」の種族値を用いて、「物理型(0)」か「特殊型(1)」かを分類するモデルを構築・比較しました。

使用した分類アルゴリズムは以下の8種類です:

  • ロジスティック回帰(Logistic Regression)
  • k近傍法(k-NN)
  • 決定木(Decision Tree)
  • ランダムフォレスト(Random Forest)
  • サポートベクタマシン(SVM)
  • XGBoost
  • ナイーブベイズ(Gaussian Naive Bayes)
  • 多層パーセプトロン(MLP)

🧪 分析の流れ

1. データセット

対象は初代151体から、以下のような**攻撃・特攻・ラベル(0:物理, 1:特殊)**を抜粋したものです:

Name Attack Sp. Atk Label
Charizard 84 109 1
Machamp 130 65 0
Gengar 65 130 1
... ... ... ...

2. 前処理

  • StandardScaler により Attack / Sp. Atk を標準化
  • 訓練・テストに 8:2 で分割

3. 分類器の訓練と評価

各モデルについて以下を実施:

  • 学習(model.fit()
  • 正解率(Accuracy)の算出とプロットタイトルへの表示
  • 決定領域の可視化
  • 混同行列の出力
  • F1スコア・精度・再現率の出力

📊 結果の可視化と評価指標

各モデルごとに以下のような図とともに、テストデータでの性能が評価されます:

  • プロット:決定境界と分類結果
  • Accuracy:テストデータに対する分類正解率
  • Confusion Matrix
  • Classification Report(精度 / 再現率 / F1スコア)

✅ 比較まとめ(一部例)

モデル Accuracy 特徴
Logistic Regression 0.91 境界が直線的で分かりやすい
Random Forest 0.95 非線形で柔軟、安定した性能
XGBoost 0.96 強力な分類性能、微調整可能
k-NN 0.89 パラメータに敏感(kの選定重要)
SVM 0.93 境界がシャープで適合性高い
MLP 0.92 多層による抽象化、過学習注意

コード

# Program Name: pokemon_classifier_comparison.py
# Creation Date: 20250603
# Overview: Compare classification models on Pokémon data using Attack and Sp. Atk
# Usage: Run in Python environment with required libraries installed

# -------------------- Install Required Libraries --------------------
!pip install pandas numpy matplotlib scikit-learn xgboost

# -------------------- Import Libraries --------------------
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from matplotlib.colors import ListedColormap
from xgboost import XGBClassifier

# -------------------- Parameters (一元管理) --------------------
RANDOM_STATE = 42
TEST_SIZE = 0.2
K_NEIGHBORS = 5
RESOLUTION = 0.1

# -------------------- Sample Data --------------------
data = [
    ['Bulbasaur', 49, 65, 1],
    ['Ivysaur', 62, 80, 1],
    ['Venusaur', 82, 100, 1],
    ['Charmander', 52, 60, 1],
    ['Charmeleon', 64, 80, 1],
    ['Charizard', 84, 109, 1],
    ['Squirtle', 48, 50, 1],
    ['Wartortle', 63, 65, 1],
    ['Blastoise', 83, 85, 1],
    ['Caterpie', 30, 20, 0],
    ['Metapod', 20, 25, 0],
    ['Butterfree', 45, 80, 1],
    ['Weedle', 35, 20, 0],
    ['Kakuna', 25, 25, 0],
    ['Beedrill', 90, 45, 0],
    ['Pidgey', 45, 35, 0],
    ['Pidgeotto', 60, 50, 0],
    ['Pidgeot', 80, 70, 0],
    ['Rattata', 56, 25, 0],
    ['Raticate', 81, 50, 0],
    ['Spearow', 60, 31, 0],
    ['Fearow', 90, 61, 0],
    ['Ekans', 60, 40, 0],
    ['Arbok', 85, 65, 0],
    ['Pikachu', 55, 50, 0],
    ['Raichu', 90, 90, 1],
    ['Sandshrew', 75, 20, 0],
    ['Sandslash', 100, 45, 0],
    ['Nidoran♀', 47, 40, 0],
    ['Nidorina', 62, 55, 0],
    ['Nidoqueen', 82, 75, 1],
    ['Nidoran♂', 57, 40, 0],
    ['Nidorino', 72, 55, 0],
    ['Nidoking', 92, 85, 1],
    ['Clefairy', 45, 60, 1],
    ['Clefable', 70, 85, 1],
    ['Vulpix', 41, 50, 1],
    ['Ninetales', 76, 81, 1],
    ['Jigglypuff', 45, 45, 0],
    ['Wigglytuff', 70, 75, 0],
    ['Zubat', 45, 30, 0],
    ['Golbat', 80, 65, 0],
    ['Oddish', 50, 75, 1],
    ['Gloom', 65, 85, 1],
    ['Vileplume', 80, 100, 1],
    ['Paras', 70, 55, 0],
    ['Parasect', 95, 60, 0],
    ['Venonat', 55, 40, 0],
    ['Venomoth', 65, 90, 1],
    ['Diglett', 55, 35, 0],
    ['Dugtrio', 80, 50, 0],
    ['Meowth', 45, 40, 0],
    ['Persian', 70, 65, 0],
    ['Psyduck', 52, 65, 1],
    ['Golduck', 82, 95, 1],
    ['Mankey', 80, 35, 0],
    ['Primeape', 105, 60, 0],
    ['Growlithe', 70, 50, 0],
    ['Arcanine', 110, 80, 0],
    ['Poliwag', 50, 40, 0],
    ['Poliwhirl', 65, 50, 0],
    ['Poliwrath', 85, 70, 0],
    ['Abra', 20, 105, 1],
    ['Kadabra', 35, 120, 1],
    ['Alakazam', 50, 135, 1],
    ['Machop', 80, 35, 0],
    ['Machoke', 100, 50, 0],
    ['Machamp', 130, 65, 0],
    ['Bellsprout', 75, 70, 1],
    ['Weepinbell', 90, 85, 1],
    ['Victreebel', 105, 100, 1],
    ['Tentacool', 40, 50, 1],
    ['Tentacruel', 70, 80, 1],
    ['Geodude', 80, 30, 0],
    ['Graveler', 95, 45, 0],
    ['Golem', 110, 55, 0],
    ['Ponyta', 85, 65, 0],
    ['Rapidash', 100, 80, 0],
    ['Slowpoke', 65, 40, 1],
    ['Slowbro', 75, 100, 1],
    ['Magnemite', 35, 95, 1],
    ['Magneton', 60, 120, 1],
    ['Farfetch’d', 65, 58, 0],
    ['Doduo', 85, 35, 0],
    ['Dodrio', 110, 60, 0],
    ['Seel', 45, 45, 0],
    ['Dewgong', 70, 70, 1],
    ['Grimer', 80, 40, 0],
    ['Muk', 105, 65, 0],
    ['Shellder', 65, 45, 0],
    ['Cloyster', 95, 85, 0],
    ['Gastly', 35, 100, 1],
    ['Haunter', 50, 115, 1],
    ['Gengar', 65, 130, 1],
    ['Onix', 45, 30, 0],
    ['Drowzee', 48, 43, 1],
    ['Hypno', 73, 73, 1],
    ['Krabby', 105, 25, 0],
    ['Kingler', 130, 50, 0],
    ['Voltorb', 30, 55, 1],
    ['Electrode', 50, 80, 1],
    ['Exeggcute', 40, 60, 1],
    ['Exeggutor', 95, 125, 1],
    ['Cubone', 50, 40, 0],
    ['Marowak', 80, 50, 0],
    ['Hitmonlee', 120, 35, 0],
    ['Hitmonchan', 105, 35, 0],
    ['Lickitung', 55, 60, 0],
    ['Koffing', 65, 60, 0],
    ['Weezing', 90, 85, 0],
    ['Rhyhorn', 85, 30, 0],
    ['Rhydon', 130, 45, 0],
    ['Chansey', 5, 35, 1],
    ['Tangela', 55, 100, 1],
    ['Kangaskhan', 95, 40, 0],
    ['Horsea', 40, 70, 1],
    ['Seadra', 65, 95, 1],
    ['Goldeen', 67, 35, 0],
    ['Seaking', 92, 65, 0],
    ['Staryu', 45, 70, 1],
    ['Starmie', 75, 100, 1],
    ['Mr. Mime', 45, 100, 1],
    ['Scyther', 110, 55, 0],
    ['Jynx', 50, 115, 1],
    ['Electabuzz', 83, 95, 1],
    ['Magmar', 95, 100, 1],
    ['Pinsir', 125, 55, 0],
    ['Tauros', 100, 40, 0],
    ['Magikarp', 10, 15, 0],
    ['Gyarados', 125, 60, 0],
    ['Lapras', 85, 85, 1],
    ['Ditto', 48, 48, 0],
    ['Eevee', 55, 45, 0],
    ['Vaporeon', 65, 110, 1],
    ['Jolteon', 65, 110, 1],
    ['Flareon', 130, 95, 0],
    ['Porygon', 60, 85, 1],
    ['Omanyte', 40, 90, 1],
    ['Omastar', 60, 115, 1],
    ['Kabuto', 80, 55, 0],
    ['Kabutops', 115, 65, 0],
    ['Aerodactyl', 105, 60, 0],
    ['Snorlax', 110, 65, 0],
    ['Articuno', 85, 95, 1],
    ['Zapdos', 90, 125, 1],
    ['Moltres', 100, 125, 1],
    ['Dratini', 64, 50, 0],
    ['Dragonair', 84, 70, 0],
    ['Dragonite', 134, 100, 0],
    ['Mewtwo', 110, 154, 1],
    ['Mew', 100, 100, 1]
]

df = pd.DataFrame(data, columns=['Name', 'Attack', 'Sp_Atk', 'Label'])

# -------------------- Feature & Target --------------------
X = df[['Attack', 'Sp_Atk']]
y = df['Label']

# -------------------- Train-Test Split --------------------
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_STATE)

# -------------------- Scaling --------------------
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# -------------------- Classifiers --------------------
models = {
    'Logistic Regression': LogisticRegression(),
    'k-NN': KNeighborsClassifier(n_neighbors=K_NEIGHBORS),
    'Decision Tree': DecisionTreeClassifier(random_state=RANDOM_STATE),
    'Random Forest': RandomForestClassifier(random_state=RANDOM_STATE),
    'SVM': SVC(probability=True),
    'XGBoost': XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=RANDOM_STATE),
    'Naive Bayes': GaussianNB(),
    'MLP': MLPClassifier(random_state=RANDOM_STATE, max_iter=1000)
}

# -------------------- Train & Plot --------------------
xx, yy = np.meshgrid(
    np.arange(X_train_scaled[:, 0].min() - 1, X_train_scaled[:, 0].max() + 1, RESOLUTION),
    np.arange(X_train_scaled[:, 1].min() - 1, X_train_scaled[:, 1].max() + 1, RESOLUTION)
)

fig, axes = plt.subplots(3, 3, figsize=(20, 15))
axes = axes.flatten()
cmap = ListedColormap(['#FFAAAA', '#AAAAFF'])

for idx, (name, model) in enumerate(models.items()):
    model.fit(X_train_scaled, y_train)
    y_pred = model.predict(X_test_scaled)
    accuracy = accuracy_score(y_test, y_pred)
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)

    ax = axes[idx]
    ax.contourf(xx, yy, Z, alpha=0.3, cmap=cmap)
    ax.scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=y_train, s=40, edgecolor='k', cmap=cmap)
    ax.set_title(f"{name}\nAccuracy: {accuracy:.2f}")
    ax.set_xlabel('Attack (Standardized)')
    ax.set_ylabel('Sp. Atk (Standardized)')

    # -------------------- Console Output --------------------
    print(f"===== {name} =====")
    print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
    print("Classification Report:\n", classification_report(y_test, y_pred))

# -------------------- Hide Extra Axes --------------------
for ax in axes[len(models):]:
    ax.axis('off')

plt.tight_layout()
plt.show()

結果
image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?