競走馬、どの子がいちばん強いんでしょうね。
荒れそうな話題です。コンピュータに数字で解決してもらいましょう(さらに荒れそう)
まえがき
競走馬のレーティング
正式なレーティングとしては国際的な基準に基づくロンジンワールドベストレースホースランキングがあります。2023年6月現在、イクイノックスが世界一のレーティングをたたき出していることも話題になりました。
しかしながら国際的なものでもあるため、例えばJRAの午前の3歳未勝利レースなどは算定の対象にはならないようです。
それはそう
TrueSkillについて
さて、ここにMicrosoftさんが開発したTrueSkillなるアルゴリズムがあります。複数人対戦に対応したレーティングアルゴリズムで、XBoxのゲームでのマッチング等に使われているようです。複数人対戦に対応してるというのは競馬にはうってつけです。技術的詳細は下記に詳しいですが、正直わからん
わからないんですが、Python実装があるのでそちらを使います。
幸いというかなんというか、元データとして手元には競馬予想AIをつくろうと収集した2015年からのレース結果データがあります。
これらを使用して、近年の競馬でどの競走馬が「いちばん強かったか」を算出してみようと思います。
先行事例
この話はわたくしが思いついたわけではなく、下記の先行事例を大いに参考にさせてもらいました。というかソースはほぼそのままです。
すみません(ありがとうございます)
やってみよう
Pythonによる実装
今回用いるデータは、2015年1月より2023年6月までのJRA全レースの結果より抽出した下記のようなものです(RaceID、着順、日付によりソートされています)
RaceID | 着順 | HorseID | 馬名 | Date | |
---|---|---|---|---|---|
0 | 201506010101 | 1 | 2012105911 | カイマノア | 2015-01-04 |
1 | 201506010101 | 2 | 2012103557 | ゴールドエッグ | 2015-01-04 |
2 | 201506010101 | 3 | 2012101349 | ジュエルアラモード | 2015-01-04 |
3 | 201506010101 | 4 | 2012100085 | ファインダッシュ | 2015-01-04 |
4 | 201506010101 | 5 | 2012100821 | ハルダヨリ | 2015-01-04 |
… | … | … | … | … | … |
413144 | 202309030812 | 12 | 2019105860 | オースミメッシーナ | 2023-06-25 |
413145 | 202309030812 | 13 | 2018100985 | ペプチドサンライズ | 2023-06-25 |
413146 | 202309030812 | 14 | 2019102491 | プレイテシア | 2023-06-25 |
413147 | 202309030812 | 15 | 2017106270 | ナムラボス | 2023-06-25 |
413148 | 202309030812 | 16 | 2018110017 | スカイナイル | 2023-06-25 |
このデータを使って、実際にTrueSkillによりレーティングを算出してみます。
(上記のデータはデータフレームとしてdf_ratingに格納されているものとします)
import pandas as pd
import numpy as np
import trueskill
mu = 25.
sigma = mu / 3.
beta = sigma / 2.
tau = sigma / 100.
draw_probability = 0.001
backend = None
env = trueskill.TrueSkill(
mu=mu, sigma=sigma, beta=beta, tau=tau,
draw_probability=draw_probability, backend=backend)
race_list = list(df_rating.loc[:,'RaceID'].unique())
horse_list = list(df_rating.loc[:,'HorseID'].unique())
rate_dict = {k:env.create_rating() for k in horse_list}
rate_dict = {k:(v,v) for k,v in rate_dict.items()}
rate_before_ = []
race_after_ = []
for race in race_list:
df_race = df_rating[df_rating.loc[:,'RaceID'] == race]
rate_before = [env.expose(rate_dict[el[2]][0]) for el in list(df_race.values)]
teams = [(rate_dict[el[2]][0],) for el in list(df_race.values)]
teams = env.rate(teams, ranks=list(range(len(df_race))))
rate_after = [env.expose(t[0]) for t in teams]
for i, el in enumerate(list(df_race.values)):
rate_dict[el[2]] = (teams[i][0], max(rate_dict[el[2]][0], teams[i][0]))
rate_before_ += rate_before
race_after_ += rate_after
df_rating.loc[:,'レース前レート'] = rate_before_
df_rating.loc[:,'レース後レート'] = race_after_
display(df_rating)
実行結果
RaceID | 着順 | HorseID | 馬名 | Date | レース前レート | レース後レート | |
---|---|---|---|---|---|---|---|
0 | 201506010101 | 1 | 2012105911 | カイマノア | 2015-01-04 | 0.000000 | 21.718523 |
1 | 201506010101 | 2 | 2012103557 | ゴールドエッグ | 2015-01-04 | 0.000000 | 20.050202 |
2 | 201506010101 | 3 | 2012101349 | ジュエルアラモード | 2015-01-04 | 0.000000 | 18.514712 |
3 | 201506010101 | 4 | 2012100085 | ファインダッシュ | 2015-01-04 | 0.000000 | 17.151930 |
4 | 201506010101 | 5 | 2012100821 | ハルダヨリ | 2015-01-04 | 0.000000 | 15.902128 |
… | … | … | … | … | … | … | … |
413144 | 202309030812 | 12 | 2019105860 | オースミメッシーナ | 2023-06-25 | 24.594729 | 24.837411 |
413145 | 202309030812 | 13 | 2018100985 | ペプチドサンライズ | 2023-06-25 | 29.581136 | 29.195539 |
413146 | 202309030812 | 14 | 2019102491 | プレイテシア | 2023-06-25 | 21.248483 | 21.602119 |
413147 | 202309030812 | 15 | 2017106270 | ナムラボス | 2023-06-25 | 27.285971 | 27.085189 |
413148 | 202309030812 | 16 | 2018110017 | スカイナイル | 2023-06-25 | 24.956057 | 24.551795 |
よさそうな感じです
いちばん「強かった」のは?
表示の簡単のため、HorseID、馬名、Date、レース後レートのみの表示とします。
df_rating=df_rating[['HorseID','馬名','Date','レース後レート']]
(2015以降)ベスト20
上記にて算出したレーティング値を(各馬のキャリアハイとなる)値の高い順に並べてみます(ベスト20を表示)
df_rating.sort_values('レース後レート', ascending=False).drop_duplicates(subset='HorseID').head(20)
結果は下記の通り。なお、Dateはそのレーティング値をたたき出したレースの開催日となります。
HorseID | 馬名 | Date | レース後レート | |
---|---|---|---|---|
289661 | 2015104961 | アーモンドアイ | 2020-11-29 | 47.109769 |
337254 | 2017101835 | コントレイル | 2021-11-28 | 46.878123 |
336721 | 2016104532 | グランアレグリア | 2021-11-21 | 46.512941 |
340968 | 2018105027 | エフフォーリア | 2021-12-26 | 45.891798 |
340970 | 2016104750 | クロノジェネシス | 2021-12-26 | 45.717448 |
413116 | 2019105219 | イクイノックス | 2023-06-25 | 44.538779 |
110732 | 2013106101 | サトノダイヤモンド | 2017-03-19 | 44.171657 |
255900 | 2016104505 | サートゥルナーリア | 2020-03-15 | 44.016735 |
304002 | 2017100720 | デアリングタクト | 2021-03-14 | 43.920770 |
410003 | 2019104462 | セリフォス | 2023-06-04 | 43.912850 |
258720 | 2016102179 | ダノンキングリー | 2020-04-05 | 43.900767 |
293513 | 2015105046 | ラッキーライラック | 2020-12-27 | 43.865839 |
293512 | 2015105075 | フィエールマン | 2020-12-27 | 43.841517 |
329725 | 2017105563 | レシステンシア | 2021-10-03 | 43.822326 |
362472 | 2018110007 | シュネルマイスター | 2022-06-05 | 43.801520 |
197275 | 2014106201 | レイデオロ | 2018-12-23 | 43.742313 |
336724 | 2015104688 | インディチャンプ | 2021-11-21 | 43.705738 |
213411 | 2015102367 | ダノンプレミアム | 2019-04-21 | 43.585643 |
244837 | 2014106220 | リスグラシュー | 2019-12-22 | 43.571278 |
86645 | 2013105906 | シンハライト | 2016-09-18 | 43.092663 |
おお、これは……
というわけで、最もレーティング値が高かったのはアーモンドアイ(2020年ジャパンカップ勝利後)でした!
おお、納得。以下数々の名馬が続きます。わりと実感に沿った順位となってますね。TrueSkillの実力とレーティングの妥当性を感じます。
先日宝塚記念を勝ったイクイノックスは現状6位となりました。これから値を伸ばしていくでしょう。
全体に、JRAのレースにコンスタントに出走し、好走を続けた馬を高く評価するようです(海外成績は考慮していないのでそれはそうですね)。
2023クラシック世代ベスト20
同様に、2023年のクラシックを盛り上げた世代(2020年生まれ)で同様の順位付けを実施してみます。2023クラシック世代は2020年生まれなので、HorseIDが2020から始まることを利用します。
df_rating2020=df_rating.query("HorseID.str.startswith('2020')")
df_rating2020.sort_values('レース後レート', ascending=False).drop_duplicates(subset='HorseID').head(20)
結果は……
HorseID | 馬名 | Date | レース後レート | |
---|---|---|---|---|
408620 | 2020103656 | リバティアイランド | 2023-05-21 | 42.205391 |
409332 | 2020103532 | タスティエーラ | 2023-05-28 | 39.494939 |
409333 | 2020102899 | ソールオリエンス | 2023-05-28 | 39.285717 |
408621 | 2020103458 | ハーパー | 2023-05-21 | 38.674583 |
408626 | 2020103368 | コナコースト | 2023-05-21 | 38.373944 |
409339 | 2020106582 | ファントムシーフ | 2023-05-28 | 37.956769 |
406603 | 2020104243 | オオバンブルマイ | 2023-05-07 | 37.590486 |
408628 | 2020103732 | ドゥアイズ | 2023-05-21 | 37.449103 |
409146 | 2020102764 | モズメイメイ | 2023-05-27 | 37.365129 |
409335 | 2020102781 | ベラジオオペラ | 2023-05-28 | 36.933963 |
408624 | 2020101608 | シンリョクカ | 2023-05-21 | 36.845168 |
411980 | 2020101874 | ペリエール | 2023-06-18 | 36.700559 |
387760 | 2020101533 | ドルチェモア | 2022-12-18 | 36.568839 |
410015 | 2020103075 | シャンパンカラー | 2023-06-04 | 36.389293 |
402712 | 2020103364 | ペリファーニア | 2023-04-09 | 36.313704 |
409148 | 2020100896 | ビッグシーザー | 2023-05-27 | 35.408171 |
406606 | 2020103322 | モリアーナ | 2023-05-07 | 35.331011 |
410830 | 2020103589 | ブトンドール | 2023-06-11 | 35.253020 |
409334 | 2020105681 | ハーツコンチェルト | 2023-05-28 | 35.243370 |
408623 | 2020103390 | ラヴェル | 2023-05-21 | 35.199534 |
リバティアイランド強すぎ!!!
もしリバティアイランドがダービーに出ていたらどうなったんでしょうね。妄想が広がります。
まとめ
Microsoftによるレーティングアルゴリズム、TrueSkillを用いて2015年以降のJRA開催レース結果より、競走馬のレーティングを実施してみました。おおむね実感に沿う結果となって満足です。
TrueSkill、すごいですね