競馬のレーティングをしてみた(Elo Rating)
前回はGlicko2 Rating Systemを使用してレーティングを行いましたが、今回はElo Ratingを使用してみました。今回は競走馬だけではなくジョッキーやトレイナーについてもレーティングを行っています。Elo Ratingは通常チェス等の1対1の対戦において使用されるレーティングシステムですが、今回はそれを複数人対戦用に拡張したライブラリを使用してみました。英語ですが解説はこちらです。
1.レーティング結果
2015年以降の全レース結果を対象にイロレーティングを適用しました。結果は以下の通りでした。上位5位までを掲載します。6位以下についても気になる人がいるようであれば開示します。
2.ソースコード
from multielo import MultiElo import pickle import pandas as pd pd.set_option('display.max_columns', 100) import numpy as np import datetime from tqdm.notebook import tqdm as tqdm # 事前に取得した競馬レースデータをロードする。競馬データの取得は以下を参考にした。 # http://houdoukyokucho.com/2020/08/31/post-1617/ with open('df.pickle', 'rb') as f: df = pickle.load(f) # ジョッキーの名前から減量記号を削除 df['jockey_name'] = df['jockey_name'].str.replace("▲","").replace("△","").replace("☆","").replace("★","").replace("◇","") # 中止レース等を除外 df = df[(df["rank"]!="中止") & (df["rank"]!="除外") & (df["rank"]!="取消") & (df["rank"]!="失格")] # レートを格納する列を追加 df["horse_rate"]=np.nan df["jockey_rate"]=np.nan df["trainer_rate"]=np.nan # レーティング結果を格納するデータフレームを作成 def createRatingTable(df, colName): df = df[[colName]].copy() df = df.drop_duplicates() df["rating"] = 1500 return df df_horse_rate = createRatingTable(df, "horse_name") df_jockey_rate = createRatingTable(df, "jockey_name") df_trainer_rate = createRatingTable(df, "trainer") # レーティング処理を高速化するためにPandasをリストに変換 horse_rates = df_horse_rate.values.tolist() jockey_rates = df_jockey_rate.values.tolist() trainer_rates = df_trainer_rate.values.tolist() race_datas = df.values.tolist() # 繰り返し計算用に辞書定義 list_dict = { "horse_name":horse_rates, "jockey_name":jockey_rates, "trainer":trainer_rates } rating_col_dict = { "horse_name":"horse_rate", "jockey_name":"jockey_rate", "trainer":"trainer_rate" } # レース単位に繰り返し計算を行う elo = MultiElo(k_value=32, d_value=400, score_function_base=2) finished = [] for i,race_data in enumerate(tqdm(race_datas)): target_race_id = race_data[12] if target_race_id not in finished: finished.append(target_race_id) single_race = [rd for rd in race_datas if rd[12]==target_race_id] ranks = [int(horse[15]) for horse in single_race] # 馬・ジョッキー・トレイナーのレートを計算 for rating_target in ["horse_name","jockey_name","trainer"]: # 馬・ジョッキー・トレイナーの名前のリストを作成 name_col_no = df.columns.get_loc(rating_target) names = [single_record[name_col_no] for single_record in single_race] # 最新レートを格納するリストを取得 rating_list=list_dict[rating_target] # レートを格納する列番号を取得 rate_col_no = df.columns.get_loc(rating_col_dict[rating_target]) # 将来的に機械学習等を行うときの説明変数とするために、レース前のレートを記録しておく players=[] for j, name in enumerate(names): # 過去レートを取得 rate = [rate for rate in rating_list if rate[0]==name][0][1] players.append(rate) # レースデータのリストに登録 race_datas[i+j][rate_col_no] = rate # レートを計算する new_player_rates = elo.get_new_ratings(players, ranks) # 最新のレート情報を記録 for name, new_player_rate in zip(names,new_player_rates): for j,rate in enumerate(rating_list): if rate[0] == name: rating_list[j][1]=new_player_rate break # 見やすいようにDataFrameに戻す def createDataFrame(rated_list, original_df): columns=original_df.columns df = pd.DataFrame(rated_list, columns=columns) return df df = createDataFrame(race_datas, df) df_horse_rate = createDataFrame(horse_rates, df_horse_rate) df_jockey_rate = createDataFrame(jockey_rates, df_jockey_rate) df_trainer_rate = createDataFrame(trainer_rates, df_trainer_rate) # 結果表示 display(df_horse_rate.sort_values("rating",ascending=False).head(5)) display(df_jockey_rate.sort_values("rating",ascending=False).head(5)) display(df_trainer_rate.sort_values("rating",ascending=False).head(5))