カグルにこんなんありました『Pokemon with stats』、ポケモンのデータです。800種類のポケモンの
基本的な統計(HP、攻撃、防御、特殊攻撃、特殊防御、速度等)情報がまとまってるとのこと。伝説ポケモンかそうでないか、というデータも入っていますので、どの特徴量が伝説ポケモンたらしめているのか、を見ていこうと思います。こちらも前回『【kaggle】離婚予測データでlightGBMしてみる』使ったlightGBMでやっていこうと思います。
データの確認と準備
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use("ggplot")
from sklearn import metrics
from sklearn.model_selection import train_test_split
import lightgbm as lgb
from sklearn.preprocessing import LabelEncoderいつものやつと、カテゴリデータがあるので、ラベルエンコーダーもインポートしておきます。
df=pd.read_csv("/content/drive/MyDrive/Colab Notebooks/Pokemon.csv")
df.head(10)                 データフレームとして読み込んで表示します。
| # | Name | Type 1 | Type 2 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | Legendary | |
| 0 | 1 | Bulbasaur | Grass | Poison | 318 | 45 | 49 | 49 | 65 | 65 | 45 | 1 | FALSE | 
| 1 | 2 | Ivysaur | Grass | Poison | 405 | 60 | 62 | 63 | 80 | 80 | 60 | 1 | FALSE | 
| 2 | 3 | Venusaur | Grass | Poison | 525 | 80 | 82 | 83 | 100 | 100 | 80 | 1 | FALSE | 
| 3 | 3 | VenusaurMega Venusaur | Grass | Poison | 625 | 80 | 100 | 123 | 122 | 120 | 80 | 1 | FALSE | 
| 4 | 4 | Charmander | Fire | NaN | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | FALSE | 
| 5 | 5 | Charmeleon | Fire | NaN | 405 | 58 | 64 | 58 | 80 | 65 | 80 | 1 | FALSE | 
| 6 | 6 | Charizard | Fire | Flying | 534 | 78 | 84 | 78 | 109 | 85 | 100 | 1 | FALSE | 
| 7 | 6 | CharizardMega Charizard X | Fire | Dragon | 634 | 78 | 130 | 111 | 130 | 85 | 100 | 1 | FALSE | 
| 8 | 6 | CharizardMega Charizard Y | Fire | Flying | 634 | 78 | 104 | 78 | 159 | 115 | 100 | 1 | FALSE | 
| 9 | 7 | Squirtle | Water | NaN | 314 | 44 | 48 | 65 | 50 | 64 | 43 | 1 | FALSE | 
こういう感じです。ほとんど知らないんですが、Type1が大カテゴリ、Type2が中カテゴリみたいなことなんでしょう。Generation、これもおそらく世に出た順番みたいなものでしょう。
で、Legendaryに伝説か、そうでないか、が入ってます。
df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 800 entries, 0 to 799
Data columns (total 13 columns):
 #   Column      Non-Null Count  Dtype 
---  ------      --------------  ----- 
 0   #           800 non-null    int64 
 1   Name        800 non-null    object
 2   Type 1      800 non-null    object
 3   Type 2      414 non-null    object
 4   Total       800 non-null    int64 
 5   HP          800 non-null    int64 
 6   Attack      800 non-null    int64 
 7   Defense     800 non-null    int64 
 8   Sp. Atk     800 non-null    int64 
 9   Sp. Def     800 non-null    int64 
 10  Speed       800 non-null    int64 
 11  Generation  800 non-null    int64 
 12  Legendary   800 non-null    bool  
dtypes: bool(1), int64(9), object(3)
memory usage: 75.9+ KBこんな感じ。これも見ておきます。
df.nunique()#             721
Name          800
Type 1         18
Type 2         18
Total         200
HP             94
Attack        111
Defense       103
Sp. Atk       105
Sp. Def        92
Speed         108
Generation      6
Legendary       2
dtype: int64なるほど、Type1は18種もありますね。Type2はないポケモンもいるようなので、これ以外でやっていきます。
df_=df.drop(["#","Name","Type 2","Legendary"],axis=1)
df_.head(10)| Type 1 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | |
| 0 | Grass | 318 | 45 | 49 | 49 | 65 | 65 | 45 | 1 | 
| 1 | Grass | 405 | 60 | 62 | 63 | 80 | 80 | 60 | 1 | 
| 2 | Grass | 525 | 80 | 82 | 83 | 100 | 100 | 80 | 1 | 
| 3 | Grass | 625 | 80 | 100 | 123 | 122 | 120 | 80 | 1 | 
| 4 | Fire | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | 
| 5 | Fire | 405 | 58 | 64 | 58 | 80 | 65 | 80 | 1 | 
| 6 | Fire | 534 | 78 | 84 | 78 | 109 | 85 | 100 | 1 | 
| 7 | Fire | 634 | 78 | 130 | 111 | 130 | 85 | 100 | 1 | 
| 8 | Fire | 634 | 78 | 104 | 78 | 159 | 115 | 100 | 1 | 
| 9 | Water | 314 | 44 | 48 | 65 | 50 | 64 | 43 | 1 | 
Type1を数値化していきます。決定木ベースのlightGBMを使うので、そのままラベルエンコーディングします。
x=df_
type1_le=LabelEncoder()
x["Type 1"]=type1_le.fit_transform(x["Type 1"])
x.head(10)| Type 1 | Total | HP | Attack | Defense | Sp. Atk | Sp. Def | Speed | Generation | |
| 0 | 9 | 318 | 45 | 49 | 49 | 65 | 65 | 45 | 1 | 
| 1 | 9 | 405 | 60 | 62 | 63 | 80 | 80 | 60 | 1 | 
| 2 | 9 | 525 | 80 | 82 | 83 | 100 | 100 | 80 | 1 | 
| 3 | 9 | 625 | 80 | 100 | 123 | 122 | 120 | 80 | 1 | 
| 4 | 6 | 309 | 39 | 52 | 43 | 60 | 50 | 65 | 1 | 
| 5 | 6 | 405 | 58 | 64 | 58 | 80 | 65 | 80 | 1 | 
| 6 | 6 | 534 | 78 | 84 | 78 | 109 | 85 | 100 | 1 | 
| 7 | 6 | 634 | 78 | 130 | 111 | 130 | 85 | 100 | 1 | 
| 8 | 6 | 634 | 78 | 104 | 78 | 159 | 115 | 100 | 1 | 
| 9 | 17 | 314 | 44 | 48 | 65 | 50 | 64 | 43 | 1 | 
これで特徴量は用意できました。次にターゲットの方ですね。
t=df.iloc[:,-1]
t=t.astype(np.int)簡単です。true/falseは型を変えるだけで1,0になってくれます。あとは、いつもの通り、スプリットします。
x_train,x_test,t_train,t_test=train_test_split(x,t,test_size=0.3,random_state=0)
print(x_train.shape,x_test.shape,t_train.shape,t_test.shape)(560, 9) (240, 9) (560,) (240,)これにて、データの準備が整いました。
lightGBMで学習
lgb_train=lgb.Dataset(x_train,t_train)
lgb_eval=lgb.Dataset(x_test,t_test)
params = {"metric":"auc",
          "objective":"binary", 
          "max_depth":7}
lgbm = lgb.train(params,
                lgb_train,
                valid_sets=lgb_eval,
                num_boost_round=1000,
                early_stopping_rounds=50,
                verbose_eval=50)Training until validation scores don't improve for 50 rounds.
[50]	valid_0's auc: 0.986375
Early stopping, best iteration is:
[45]	valid_0's auc: 0.988179いつもの通り、一瞬で終わります。正解率は、
print(metrics.accuracy_score(t_train.values,np.round(lgbm.predict(x_train))))
print(metrics.accuracy_score(t_test.values,np.round(lgbm.predict(x_test))))0.9946428571428572
0.9625やはり、素晴らしい数値をたたき出します。
伝説ポケモンたらしめる特徴量は何か?
imp=pd.DataFrame(np.round(lgbm.feature_importance(importance_type="gain"),2), index=x.columns, columns=["importance"])
imp=imp.sort_values("importance",ascending=False)
imp| importance | |
| Total | 1051.98 | 
| Generation | 80.33 | 
| Attack | 53.57 | 
| Sp. Atk | 42.22 | 
| HP | 38.13 | 
| Speed | 35.83 | 
| Type 1 | 25.57 | 
| Sp. Def | 21.89 | 
| Defense | 17.92 | 
HPからSpeedまでの合計がTotalなんですが、これが大きいとだいたい伝説ポケモン、というシンプルで一貫性のある結果が得られました。どうもありがとうございました。
 
 
