ぶな記

主に食べ物、みじんこ程度にプラグラミング

ぶな記

MENU

ポケモンのタイプと個体値を主成分分析してみた

東工大情報工学系200番台の講義に「機械学習」が新設された。

 

1年の時にPythonではなくRubyで根菜を学習した人間なのでPython初学者になるわけだが、とても身になったと思う。

受講前までPythonはみんな使ってるからやだとか言ってた自分を張っ倒したい。Pythonはなんでもできる。

 

この講義の最後の課題で、「任意のデータを用いて分析して良い」という課題が出たので思い切ってポケモンを題材に分析してやった。今日はその話。

 


 

主成分分析とは

間違ったことを言って刺されたくないのでサイトを引用します。


qiita.com


 

たくさんの次元で表されたものを、少ない次元に圧縮して表す手法の一つ、でいいのかな。

上記のサイトの例だと、ワインを表す13次元のデータから、2次元に落とし込んでプロットしたら3品種が区別できそう、と言う話をしている。

 

使用したデータセット

今回はポケモン個体値とタイプを用いて2次元プロットにしたら、タイプごとの相関が見られるんじゃないかと思って試してみたい。

 

データセットはKaggleというデータ解析コンペのサイトから引っ張ってきた。このために会員登録までした(データをダウンロードするのに無料の会員登録と利用規約への同意が必要)。

 

www.kaggle.com



企業から賞金付きのコンペティションも開催されているらしい。40万人以上のデータサイエンティストが集まると銘打っている通りめちゃくちゃデータセットがたくさんある。すごい。

 

で、今回用いたのはこれ。T7 - Hackathon Challenge さんが作成した

www.kaggle.com

の、pokemon.csv

 

割と適当に探してしまったのでこのデータセット、第6世代(X,Yまで)のメガ進化を含めたポケモンしかいない。ちょっと少ないけどまあいいか。

 

なので、現状ポケモンが890種類いる内、721種類を網羅している。

  • 図鑑番号
  • 英語名
  • タイプ1
  • タイプ2
  • 個体値(HP, 攻撃,防御,特攻,特防,素早さ)
  • 登場世代
  • 伝説のポケモンかどうか

がデータの要素。

 

今回使うのはラベル用の名前と、タイプ1、2。そしてメインになる個体値

 

ポケモンのフォルム違いや、メガ進化も別個の事例として捉えているため、事例数はn=1214である。

結果

ポケモンのタイプごとに色分けした点が以下のように散布された。

タイプカラーは上から順に

  • みず
  • はがね
  • いわ
  • エスパー
  • どく
  • ノーマル
  • こおり
  • じめん
  • くさ
  • ゴースト
  • ひこう
  • ほのお
  • かくとう
  • フェアリー
  • でんき
  • ドラゴン
  • あく
  • むし
f:id:buna-simeji:20210224192311p:plain
散布図

800匹もいるとだいぶごちゃついてるし、そもそもタイプが16もあるのが悪い。という感じの図になった。

 

ポケモンのタイプのカラーコードはこちらを参考にした。


yuki-falling-fall.blogspot.com



これ、どうみるのかわからないので、どこにあると何が高くなっている、という観測変数の寄与度もプロットしてみた。

f:id:buna-simeji:20210224192526p:plain
観測変数の寄与度

 真右だと攻撃の値が大きい、とかそんな感じだ。

大まかに右に行くほど攻撃
上に行くほど防御
下だと(左よりならより)素早さ
に特化してる、と見える。せっかくなので二つの図を並べてみる。

f:id:buna-simeji:20210224192526p:plainf:id:buna-simeji:20210224192311p:plain
観測変数の寄与度と散布図

 


全体的に左に寄ってるポケモンはしょぼいと考えて良い。

ドラゴンタイプ(紺色)やエスパータイプがやや右寄りに存在しているため,やはり「強いポケモン」が多いタイプなりの特徴を持っているとわかる.

フェアリータイプ(図中桃色の点)は最も後発となるタイプだからか,バランスよく様々な場所で確認される.

むしタイプ(薄緑)は逆に図の左側により固まっている様子が見て取れる.これはむしタイプに大器晩成なキャラクターが非常に多いため,このように固まってしまっているのかもしれない.

電気タイプ(黄色)が下側に寄っている. これは素早いポケモンが多く,どちらかというと攻撃ではなく特攻よりのポケモンが多いことを表している.

防御,特防が高いポケモンとして上側に存在するのが岩タイプ,地面タイプ,鋼タイプであることがわかる.

みたいなことがわかりそう。全体的にタイプごとの塊が激しくできてないところをみると、これこそがゲームバランスの成果なのか...という気持ちになる。

 

ポケモンガチ勢ではないので違った考察してても怒らないで。

 

英語ラベルにはなるけど800匹分のラベルもはっつけてみた。拡大推奨。結構メガ進化が外れ値みたいになってるのも面白いね。

f:id:buna-simeji:20210224194238p:plain
ラベル月散布図


 ここからは使ったコードだけつらつら書いておく。Pythonのひよこ並初心者なのでどっかおかしいコードがあっても怒らないでください。

使ったコード

これはポケモンの名前とタイプと個体値を取得している。

import csv
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# ポケモンのタイプごとにデータを作成する
# 複合タイプなら2回カウントする

poke_type = []
poke_name = []
data = np.empty((0,6))

with open('/content/drive/MyDrive/pokemon.csv') as fi:
  reader = csv.reader(fi)
  header = next(reader) # ヘッダー
  for row in reader:
      poke_name.append(row[1])
      poke_type.append(row[2])
      data = np.append(data, [row[4:-2]], axis=0)
      if row[3] is not '': # 第2タイプがあれば追加
        poke_name.append(row[1])
        poke_type.append(row[3])
        data = np.append(data, [row[4:-2]], axis=0)

タイプは第1タイプと第2タイプがあるので、それぞれカウントしている。そのため同じ名前のポケモンはダブルカウントすることになる。

# 二次元マッピングをする際のColormapをタイプごとに作成している.
typename = ['Bug', 'Dark', 'Dragon', 'Electric', 'Fairy', 'Fighting', 'Fire', 'Flying', 'Ghost', 'Grass', 'Ground', 'Ice', 'Normal', 'Poison', 'Psychic', 'Rock', 'Steel', 'Water']
typecolor = ['#9acd32', '#656777', '#0966bb', '#f7da5b', '#ffaef0', '#d5425f', '#ffa152', '#9bb3e1', '#626dbd' ,'#5dbe67', '#d68e5e', '#7dd2c7', '#919aa1', '#b962cf', '#fb8684', '#cbbd8d', '#5497a3', '#5faadd']

type_num = []
for str in poke_type:
  type_num.append(typename.index(str))

# カラーマップの自作
from matplotlib.colors import ListedColormap
cmap = ListedColormap(typecolor, name="custom")

カラーマップは散布図のドットの色とカラーバーの色になる。

pca2 = PCA(2)
P2 = pca2.fit_transform(data)

これで2次元に主成分分析して次元を削減して、

plt.figure(figsize=(6, 6))
for x, y, name in zip(pca.components_[0], pca.components_[1], header[4:]):
    plt.text(x, y, name)
plt.scatter(pca.components_[0], pca.components_[1], alpha=0.8)
plt.grid()
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.show()

f:id:buna-simeji:20210224192526p:plain

これで観測変数の寄与度を出した。

#二次元プロット#
fig = plt.figure(dpi=100, figsize=(60,30))
ax = fig.add_subplot(1,1,1)
ax.set_xlabel('PC 1')
ax.set_ylabel('PC 2')
ax.set_aspect('equal')
m = ax.scatter(P2[:, 0], P2[:, 1], c=type_num, marker='.', cmap=cmap)

# 各要素に名前をラベルとして付ける
i=0
for txt in poke_name:
  plt.text(P2[i,0], P2[i,1], txt)
  i=i+1

fig.colorbar(m,ax=ax)
fig.show()

これがラベル付き二次元プロット。各要素に~i=i+1の部分をコメントアウトするとラベルなしの二次元プロットになる。

i=i+1の書き方、かっこ悪いと思うだけどpythonはこれでしかできないのかな。それが最近の悩み。