ウマ娘のリセマラランキングを機械学習で予想してみる

# この記事について

ウマ娘プリティーダービーが明日の2/24から公開されることになり、ゲーム攻略サイトの「元の競走馬の勝率をもとにしたリセマラランキング」が話題になりました。

どうせなら機械学習を使ってリセマラランキングを予想してみたくなりました。
考え方、予想の手順、予想の結果を技術ブログとして残します。

# 予想の方法

実際の競走馬の成績と性質をもとに、機械学習でクラスタリングをします。

強くならない特定のキャラクター（例：ハルウララ）の所属するクラスタを低く、アニメで人気のあるキャラクタが集まったクラスタを高く評価して、ランキングを作ります。

実行環境：
　Windows 10, Python 3.8.3

ライブラリ：
　numpy==1.20.1
　sklearn==0.24.1
　pandas==1.2.2

※データを作る時に、javaのjsoapを使っています。

## 機械学習の結果：できあがったリセマラランキング

SSランク（大当たり）

- ゴールドシップ
- ウオッカ
- スペシャルウィーク
- オグリキャップ
- グラスワンダー

Sランク（当たり）

- ダイワスカーレット
- シンボリルドルフ
- タイキシャトル
- トウカイテイオー
- サクラバクシンオー
- サイレンススズカ
- メジロマックイーン
- マヤノトップガン
- ライスシャワー
- スーパークリーク
- エルコンドルパサー
- マルゼンスキー

Aランク（やや冷遇されそうなウマ娘）

- エアグルーヴ
- ナイスネイチャ
- キングヘイロー
- メジロライアン
- ウイニングチケット
- マチカネフクキタル
- アグネスタキオン

Bランク（ハルウララ）

- ハルウララ

## 予想の手順：方針

手順を説明していきます。まず3点の仮定を立てます。

**1点目：「似た脚質、似た戦績のウマは、おそらくゲーム内の性能も似ている」**

長距離が得意なメジロマックイーン、長距離が得意なライスシャワー、どちらも得意なレースは同じレースになるはずです。距離適性を含めて、以下のことを考えます。

- 芝が得意、ダート（砂地）が得意
- 長距離に強い、短距離に強い
- 重馬場への適正（雨が降っていても強い、晴れていると強い）
- 脚質（最初から速いペースで飛び出すウマ、後から抜いていくウマ）

それぞれの属性が似通ったウマは、ゲームでも似たような性能になると考えます。

**2点目：「勝てないことが個性のウマがいる」**

勝てないことが話題になり、個性になった競走馬がいます。
そういった競走馬がモデルのウマは、他のウマよりも低い性能になるはずです。

- 113戦で0勝、一度も勝てないことで話題になったハルウララ
- 有馬記念で3年連続3着、3着の多いブロンズコレクターとして人気のあるナイスネイチャ

ハルウララやナイスネイチャが最強、というのは考えにくく、ゲーム内の性能にも反映されるはずです。

**3点目：「アニメで人気のあるウマほど初期は強い」**

たくさんの人が「使いたい！」と思っているキャラクターは、他のキャラクターよりも良い設定になるはずです。もし機動戦士ガンダムのゲームで、ジーン（※第一話でやられるジオン兵）がアムロ・レイよりも強かったら炎上します。

また、イベントに特効があるとすれば、人気のあるキャラほど早く回ってきます。

## 計算方法

この3つの仮定をふまえて計算方法を検討すると、以下のようになります。

少し単純にして説明するために、パラメータを抜粋して図解します。

**1. データを正規化する**

単位も大きさも違うと比較できないため、全て割合に直します。
賞金額は円ではなく%で、距離適性、出走数、勝利数も、全て%で考えます。

![graph_vertical.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/35dbd34a-e461-1c6f-f227-41d62ea42909.png)

**2. 似たウマ同士をクラスタリングする**

賞金額と距離適性で2次元のグラフを作ると、以下のようになります。
グラフの上で近い場所にいるウマは、良く似たウマです。

![cluster_uma.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/3a24adab-cdc9-1865-aff6-4614c89634a4.png)

もちろん比較するデータが賞金額と距離適性だけだと、それ以外の情報でギャップが出てきます。
今回は以下の情報を比較して、よく似たウマ同士をグループ分けします。

- 賞金額（賞金の総額です。今回は物価の変動は考慮しません）
- G1勝数（最もグレードの高いレースに勝利した数です）
- 勝数（レースで1位を取った数です）
- 着内（レースで3位よりも高い順位を取った数です）
- 出走数（レースに出た数です）
- 芝適正（netkeibaで「ダートよりも芝に向いている」と投票された割合です）
- 距離適正（netkeibaで「長距離よりも短距離に向いている」と投票された割合です）
- 重馬場（netkeibaで「雨で状態の悪くなったコースでも強い」と投票された割合です）
- 脚質（netkeibaで「最初からペースを上げるウマだ」と投票された割合です）

グループ分けをしてくれる仕組みが、機械学習のクラスタリングです。
今回はAffinityPropagationを使います。

**3. ハルウララ、ナイスネイチャなど、特定のウマのいるクラスタを除外する**

同じクラスタのウマが似た性能になるのなら、ナイスネイチャやハルウララの得意な分野は低く評価されます。

**4. それぞれのクラスタのウマの平均人気順でランキングをつける**

人気のあるウマが勝ちやすいようにゲームバランスを調整しているはずです。
だとすれば、人気のあるウマと同じクラスタにいるウマがリセマラランキング上位になります。

## 対象データ

学習に使うデータを用意します。

ゲームの初期実装から外れそうなキャラクターは除外します。具体的には、公式サイトのキャラクターページを見て、勝負服の3D画像が出ているキャラクターだけを対象にしています。

**学習に使うデータは、以下の通りとします。**

netkeibaに掲載されている、モデルになった競走馬の性質、成績を利用しました。

|名前|賞金額|G1勝数|勝数|着内|出走数|芝適正|距離適性|重馬場|脚質|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|ゴールドシップ|13億9776万円|6|13|18|28|96%|14%|88%|8%|
|ウオッカ|13億0487万円|7|10|18|26|94%|53%|41%|28%|
|スペシャルウィーク|10億9262万円|4|10|16|17|96%|18%|67%|8%|
|メジロマックイーン|9億9810万円|4|12|19|21|92%|7%|89%|91%|
|オグリキャップ|8億8830万円|4|22|29|32|69%|52%|82%|23%|
|エアグルーヴ|8億2196万円|2|9|17|19|97%|47%|50%|43%|
|マヤノトップガン|8億1039万円|4|8|17|21|92%|8%|44%|53%|
|ダイワスカーレット|7億8668万円|4|8|12|12|89%|51%|63%|95%|
|グラスワンダー|6億9164万円|4|9|10|15|93%|50%|60%|17%|
|シンボリルドルフ|6億8482万円|7|13|15|16|95%|27%|75%|82%|
|ライスシャワー|6億6686万円|3|6|13|25|98%|4%|65%|66%|
|ナイスネイチャ|6億1918万円|0|7|21|41|95%|38%|54%|24%|
|タイキシャトル|6億1548万円|5|11|13|13|69%|92%|92%|90%|
|トウカイテイオー|6億0470万円|4|9|9|12|98%|44%|60%|65%|
|スーパークリーク|5億5610万円|3|8|12|16|96%|7%|75%|78%|
|サクラバクシンオー|5億1549万円|2|11|14|21|93%|95%|46%|94%|
|キングヘイロー|5億0026万円|1|6|14|27|89%|65%|50%|17%|
|メジロライアン|4億8693万円|1|7|14|19|94%|43%|77%|19%|
|サイレンススズカ|4億5598万円|1|9|10|16|96%|50%|57%|99%|
|エルコンドルパサー|3億7607万円|3|8|11|11|62%|45%|91%|82%|
|ウイニングチケット|3億7177万円|1|6|9|14|97%|44%|49%|10%|
|マチカネフクキタル|3億7024万円|1|6|11|22|91%|36%|41%|10%|
|アグネスタキオン|2億2208万円|1|4|4|4|94%|50%|71%|48%|
|マルゼンスキー|7660万円|0|8|8|8|80%|50%|79%|94%|
|ハルウララ|112万円|0|0|12|113|19%|81%|35%|34%|

※マルゼンスキーは半世紀前のウマですので、物価が大きく異なります。

ウマ娘攻略まとめ速報で集計された、アニメ第一期終了時点の人気投票の結果を利用しました。

|名前|得票数|順位|
|:--|:--|:--|
|ライスシャワー|374|1位|
|サイレンススズカ|366|2位|
|ゴールドシップ|358|3位|
|グラスワンダー|343|4位|
|キングヘイロー|342|5位|
|セイウンスカイ|304|6位|
|トウカイテイオー|302|7位|
|ダイワスカーレット|249|8位|
|オグリキャップ|247|9位|
|テイエムオペラオー|246|10位|
|タマモクロス|236|11位|
|メジロマックイーン|217|12位|
|スペシャルウィーク|208|13位|
|ウオッカ|188|14位|
|ミホノブルボン|183|15位|
|マチカネフクキタル|169|16位|
|マルゼンスキー|157|17位|
|ナイスネイチャ|157|18位|
|エルコンドルパサー|152|19位|
|メイショウドトウ|135|20位|
|ナリタブライアン|128|21位|
|アグネスタキオン|107|22位|
|シンボリルドルフ|106|23位|
|マンハッタンカフェ|100|24位|
|エアグルーヴ|89|25位|
|マヤノトップガン|89|26位|
|ヒシアマゾン|78|27位|
|アグネスデジタル|78|28位|
|タイキシャトル|74|29位|
|メジロドーベル|73|30位|
|フジキセキ|63|31位|
|サクラバクシンオー|63|32位|
|エイシンフラッシュ|60|33位|
|スーパークリーク|53|34位|
|ビワハヤヒデ|45|35位|
|ファインモーション|43|36位|
|カレンチャン|41|37位|
|ナカヤマフェスタ|40|38位|
|ハルウララ|39|39位|
|ゼンノロブロイ|37|40位|
|アドマイヤベガ|37|41位|
|カワカミプリンセス|33|42位|
|アイネスフウジン|28|43位|
|ウイニングチケット|25|44位|

※人気順のうち、上位44キャラクターを掲載しています。

## 実際に計算していく

ソースに書くと、処理はシンプルです。

```python
import numpy as np
import pandas as pd
from sklearn.cluster import AffinityPropagation
from sklearn.preprocessing import MinMaxScaler

def main(file_name):
    # CSVからデータを読み込む
    df = pd.read_csv(file_name, index_col=0)

    # 正規化のためのノーマライザを取得
    normalizer = MinMaxScaler()

    # AffinityPropagationでクラスタリングする（初期化乱数は固定する）
    clustering = AffinityPropagation(random_state=5)
    cluster = clustering.fit_predict(normalizer.fit_transform(df))

    # クラスタリングした結果を格納
    # クラスターごとに所属するキャラクタの名前を一覧する
    cluster_list = {}
    for i, c in enumerate(cluster):
        key = str(c)
        if not (key in cluster_list):
            cluster_list[key] = []
        cluster_list[key].append(df.index[i])

    print(cluster_list)
```

実行すると、以下の出力結果を得ました。

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/dd8cfb0f-da44-277f-240c-7d8d98c13057.png)

クラスタごとに表にすると次の通りです。

|クラスタ番号|ウマの名前|ファン投票順位|
|:--|:--|:--|
|0|ゴールドシップ|3位|
|0|ウオッカ|14位|
|0|スペシャルウィーク|13位|
|0|オグリキャップ|9位|
|0|グラスワンダー|4位|

|クラスタ番号|ウマの名前|ファン投票順位|
|:--|:--|:--|
|1|ダイワスカーレット|8位|
|1|シンボリルドルフ|23位|
|1|タイキシャトル|29位|
|1|トウカイテイオー|7位|
|1|サクラバクシンオー|32位|
|1|サイレンススズカ|2位|

|クラスタ番号|ウマの名前|ファン投票順位|
|:--|:--|:--|
|2|メジロマックイーン|12位|
|2|マヤノトップガン|26位|
|2|ライスシャワー|1位|
|2|スーパークリーク|34位|
|2|エルコンドルパサー|19位|
|2|マルゼンスキー|17位|

|クラスタ番号|ウマの名前|ファン投票順位|
|:--|:--|:--|
|3|エアグルーヴ|25位|
|3|ナイスネイチャ|18位|
|3|キングヘイロー|5位|
|3|メジロライアン|51位|
|3|ウイニングチケット|44位|
|3|マチカネフクキタル|16位|
|3|アグネスタキオン|22位|

|クラスタ番号|ウマの名前|ファン投票順位|
|:--|:--|:--|
|4|ハルウララ|39位|

まとめると、以下の通りになりました。

|クラスタ番号|人気の平均順位|所属数|備考|
|:--|:--|:--|:--|
|0|8位|5人||
|1|16位|6人||
|2|18位|6人||
|3|25位|7人|ナイスネイチャが所属|
|4|39位|1人|ハルウララが所属|

この結果を見て、以下のようにクラスタとランキングを対応させました。

- クラスタ0：人気が高く、性能も優遇されやすいウマ＝ランクSS
- クラスタ1：性能が優遇されやすいクラスタ＝ランクS
- クラスタ2：性能が優遇されやすいクラスタ＝ランクS
- クラスタ3：性能が冷遇されそうなクラスタ＝ランクA
- クラスタ4：ハルウララだけのクラスタ＝ランクB