初めに
こちらはゆっくりと作る競馬予想ソフト開発のロードマップ2の内容になっています。
ここでは、簡易分析その4の分析結果のJupyter Notebookを公開しています。
Youtubeで競馬予想ソフトの開発の制作過程を投稿しています。再生リストはこちらを参照ください。
YouTube再生リスト:
チャンネル登録もしてもらえると嬉しいです。
競馬予想ソフト開発の概要
競馬予想ソフトの概要を知りたい場合は、以下のページを参照ください。
注意
簡易分析のコードには、一部有料で公開しているモジュールを使用しています。もし一緒に競馬予想ソフトを作ってみたい方は、以下のBookersリンクからソースを購入していただければと思います。
Bookersに登録&YouTubeチャンネル登録をしていただけると1000円オフで購入できます。
Bookersリンク:低アクセス回数で競馬データをスクレイピングする方法
データ分析内容:Jupyter notebook
0.ロードマップ2のパート1でやった分を実行¶
詳細は以下リンクを参照ください。
import warnings
import numpy as np
import pandas as pd
import sys
import re
import seaborn as sns
import matplotlib.pyplot as plt
import japanize_matplotlib
sys.path.append(".")
from src.core.db.controller import getDataFrame # noqa
warnings.filterwarnings("ignore")
dbpath = "./data/keibadata.db"
start_year = 2010
end_year = 2023
year_list = list(range(start_year, end_year+1))
# レース結果
dfrace = pd.concat(
[
getDataFrame(f"racedata{year}", dbpath)
for year in year_list
]
)
# レース情報
dfinfo = pd.concat(
[
getDataFrame(f"raceinfo{year}", dbpath)
for year in year_list
]
)
# 2つのDataFrameをマージ
df = pd.merge(dfinfo, dfrace, on="raceId")
df["raceDate"] = df["raceDate"].astype("datetime64[ns]")
df = df.sort_values(["raceDate", "raceId", "number"]).reset_index(drop=True)
df["race_span"] = df.groupby(
"horseId")["raceDate"].diff().apply(lambda d: d.days)
df = df[~df["field"].isin(["障"])].reset_index(drop=True)
df["label_mark"] = df["label"].apply(lambda d: 1 if re.search(r"\(降\)", d) else 0).astype("int8")
df["label"] = df["label"].apply(lambda d: d.replace("失", "19"))
raceId_list = df[df["label"].apply(
lambda d: True if re.search(r"\(降\)|失", d) else False)]["raceId"].unique()
df["label_flag"] = df["raceId"].apply(lambda d: 1 if d in raceId_list else 0)
df = df[df["label"].apply(lambda d: False if re.search(r"中|取|除", d) else True)].reset_index(drop=True)
df["label"] = df["label"].apply(lambda d: int(re.search(r"\d+", d).group()))
df["last3F"] = pd.to_numeric(df["last3F"], errors="coerce")
df["time"] = pd.to_numeric(df["time"], errors="coerce")
df = df[~df["last3F"].isna()]
df.reset_index(drop=True, inplace=True)
# 数値化対象のカラムを列挙
target_columns = [
'distance', 'number', 'boxNum',
'label', 'odds', 'favorite', 'age',
'jweight', 'time', 'weight', 'gl'
]
# 数値変換
for col in target_columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
dfhorse_fill = df.groupby("horseId")[
["weight", "gl"]].apply(
lambda x: x.ffill().bfill()).sort_index(level=1).reset_index(level=0)
dfna = df["weight"].isna()
dfhorse_fill = dfhorse_fill.sort_index(level=1).reset_index(level=0)
df["weight"] = dfhorse_fill["weight"]
df["gl"] = dfhorse_fill["gl"]
4.タイム関連データの確認¶
走破タイムに関連するデータはすべて秒数となっている
しかし、すべての馬は同じ距離を走るわけではない
よって、走破タイムに関連するデータは、すべて分速に変換する
ややこしいので、実際には分速に関するカラムを追加するという方針
# 一旦前処理
# レースのペース情報が入っている以下の3カラムを前処理
# "f3Ftol3F", "rapTime", "rapSumTime"
# f3Ftol3Fは、最初の3Fと上り3Fのペースタイム情報が入っているので、expandで二つに分ける
df_l3Ftol3F = df["f3Ftol3F"].str.extract(
r"(\d+\.\d)-(\d+\.\d)", expand=True).astype(
{0: float, 1:float}).rename(columns={0: "f3F", 1: "l3F"})
# 速度情報の追加
df_l3Ftol3F[["f3F_vel", "l3F_vel"]] = 60*600/df_l3Ftol3F.values
# 元のDFに統合
df = pd.concat([df, df_l3Ftol3F], axis=1)
# ペース情報は、一旦リストのままで処理する
df["rapTime"] = df["rapTime"].apply(lambda data: [float(d.strip()) for d in data.split("-")])
df["rapSumTime"] = df["rapSumTime"].apply(
lambda data: [float(d.strip()) for d in data.split("-")])
df["rapTime_vel"] = df["rapTime"].apply(lambda data_list: (60*200/np.array(data_list)).tolist())
df["rapSumTime_vel"] = df["rapSumTime"].apply(
lambda data_list: [60*200*i/d for i, d in enumerate(data_list, start=1)])
# 内容確認
df[["f3F_vel", "l3F_vel", "rapTime_vel", "rapSumTime_vel"]]
f3F_vel | l3F_vel | rapTime_vel | rapSumTime_vel | |
---|---|---|---|---|
0 | 1058.823529 | 947.368421 | [1016.9491525423729, 1132.0754716981132, 1034…. | [1016.9491525423729, 1071.4285714285716, 1058…. |
1 | 1058.823529 | 947.368421 | [1016.9491525423729, 1132.0754716981132, 1034…. | [1016.9491525423729, 1071.4285714285716, 1058…. |
2 | 1058.823529 | 947.368421 | [1016.9491525423729, 1132.0754716981132, 1034…. | [1016.9491525423729, 1071.4285714285716, 1058…. |
3 | 1058.823529 | 947.368421 | [1016.9491525423729, 1132.0754716981132, 1034…. | [1016.9491525423729, 1071.4285714285716, 1058…. |
4 | 1058.823529 | 947.368421 | [1016.9491525423729, 1132.0754716981132, 1034…. | [1016.9491525423729, 1071.4285714285716, 1058…. |
… | … | … | … | … |
659566 | 1037.463977 | 1055.718475 | [975.6097560975609, 1081.081081081081, 1061.94… | [975.6097560975609, 1025.6410256410256, 1037.4… |
659567 | 1037.463977 | 1055.718475 | [975.6097560975609, 1081.081081081081, 1061.94… | [975.6097560975609, 1025.6410256410256, 1037.4… |
659568 | 1037.463977 | 1055.718475 | [975.6097560975609, 1081.081081081081, 1061.94… | [975.6097560975609, 1025.6410256410256, 1037.4… |
659569 | 1037.463977 | 1055.718475 | [975.6097560975609, 1081.081081081081, 1061.94… | [975.6097560975609, 1025.6410256410256, 1037.4… |
659570 | 1037.463977 | 1055.718475 | [975.6097560975609, 1081.081081081081, 1061.94… | [975.6097560975609, 1025.6410256410256, 1037.4… |
659571 rows × 4 columns
# 走破速度
df["velocity"] = 60*df["distance"] / df["time"]
# 上り3Fの速度
df["last3F_vel"] = 60*600 / df["last3F"]
# 上り3Fに至るまでの速度
df["toL3F_vel"] = 60*(df["distance"]-600) / (df["time"]-df["last3F"])
通過ラベルである”passLabel”カラムの扱いをどうするか?
各コーナごとの通過順位を表しているので、最大4コーナー分の通過順位情報がある
以下のようにして、4コーナー分出す
また、レースごとに出走馬数が違うので、各通過順位を出走馬数で割る
→ このようにすることで、通過順位が5着だったとしても、出走頭数が18頭だった場合と10頭だった場合の違いを表現できる
# かなり強引だが以下のようなfor文で、最大4コーナー分の通過順位を出せる
# ポイントは、DFのfor文を回す前にnumpyのarrayに変換している
horseNumMap = dfrace.groupby("raceId")["raceId"].count().to_dict()
df["horseNum"] = df["raceId"].map(horseNumMap)
expand_pass_label_list = []
for hNum, passLabel in df[["horseNum", "passLabel"]].values:
insert_data = {f"label_{i}C": None for i in range(1, 5)}
# passLabelを文字列分割してinsert_dataのDictにデータを入れる。
# 4コーナー分ないものはNoneのまま入る
for num, d in enumerate(passLabel.split("-"), start=1):
insert_data[f"label_{num}C"] = int(d)/hNum
expand_pass_label_list += [insert_data.copy()]
# 統合できるようにDF化
dfpass = pd.DataFrame(expand_pass_label_list)
# 内容の確認
display(dfpass)
display(dfpass.describe())
label_1C | label_2C | label_3C | label_4C | |
---|---|---|---|---|
0 | 0.6250 | 0.5000 | NaN | NaN |
1 | 0.8125 | 0.6875 | NaN | NaN |
2 | 0.7500 | 0.8125 | NaN | NaN |
3 | 0.6250 | 0.5625 | NaN | NaN |
4 | 0.3750 | 0.3125 | NaN | NaN |
… | … | … | … | … |
659566 | 0.8750 | 0.8750 | NaN | NaN |
659567 | 0.3125 | 0.5000 | NaN | NaN |
659568 | 1.0000 | 0.9375 | NaN | NaN |
659569 | 0.5625 | 0.6875 | NaN | NaN |
659570 | 0.3125 | 0.2500 | NaN | NaN |
659571 rows × 4 columns
label_1C | label_2C | label_3C | label_4C | |
---|---|---|---|---|
count | 659571.000000 | 653697.000000 | 303870.000000 | 265341.000000 |
mean | 0.505128 | 0.504528 | 0.500228 | 0.499702 |
std | 0.285294 | 0.284304 | 0.283401 | 0.283376 |
min | 0.055556 | 0.055556 | 0.055556 | 0.055556 |
25% | 0.250000 | 0.250000 | 0.250000 | 0.250000 |
50% | 0.500000 | 0.500000 | 0.500000 | 0.500000 |
75% | 0.750000 | 0.750000 | 0.750000 | 0.750000 |
max | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
# うまくいってるようなので統合
df = pd.concat([df, dfpass], axis=1)
# 最終着順(走破タイム順)も出走馬数で割る
df["label_rate"] = df.groupby("raceId")["time"].rank() / df["horseNum"]
# 最終コーナー通過時の値のみを取り出す
df["label_lastC"] = df[["passLabel", "horseNum"]].apply(
lambda row: int(row["passLabel"].split("-")[-1].strip())/row["horseNum"], axis=1)
# 最終コーナー通過時と最終着順との差
df["label_diff"] = df["label_rate"] - df["label_lastC"]
すべて処理が終わったので内容の確認
labels_columns = [f"label_{i}C" for i in range(1, 5)] + ["label_lastC", "label_rate", "label_diff"]
vel_columns = ["velocity", "last3F_vel", "toL3F_vel"]
describe_columns = vel_columns + [f"label_{i}C" for i in range(1, 5)] + labels_columns
display(df[describe_columns])
display(df[describe_columns].describe())
velocity | last3F_vel | toL3F_vel | label_1C | label_2C | label_3C | label_4C | label_1C | label_2C | label_3C | label_4C | label_lastC | label_rate | label_diff | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 980.926431 | 937.500000 | 1028.571429 | 0.6250 | 0.5000 | NaN | NaN | 0.6250 | 0.5000 | NaN | NaN | 0.5000 | 0.12500 | -0.37500 |
1 | 971.659919 | 927.835052 | 1019.830028 | 0.8125 | 0.6875 | NaN | NaN | 0.8125 | 0.6875 | NaN | NaN | 0.6875 | 0.31250 | -0.37500 |
2 | 958.721704 | 900.000000 | 1025.641026 | 0.7500 | 0.8125 | NaN | NaN | 0.7500 | 0.8125 | NaN | NaN | 0.8125 | 0.56250 | -0.25000 |
3 | 952.380952 | 886.699507 | 1028.571429 | 0.6250 | 0.5625 | NaN | NaN | 0.6250 | 0.5625 | NaN | NaN | 0.5625 | 0.87500 | 0.31250 |
4 | 972.972973 | 913.705584 | 1040.462428 | 0.3750 | 0.3125 | NaN | NaN | 0.3750 | 0.3125 | NaN | NaN | 0.3125 | 0.21875 | -0.09375 |
… | … | … | … | … | … | … | … | … | … | … | … | … | … | … |
659566 | 1044.992743 | 1081.081081 | 1011.235955 | 0.8750 | 0.8750 | NaN | NaN | 0.8750 | 0.8750 | NaN | NaN | 0.8750 | 0.12500 | -0.75000 |
659567 | 1040.462428 | 1055.718475 | 1025.641026 | 0.3125 | 0.5000 | NaN | NaN | 0.3125 | 0.5000 | NaN | NaN | 0.5000 | 0.46875 | -0.03125 |
659568 | 1022.727273 | 1046.511628 | 1000.000000 | 1.0000 | 0.9375 | NaN | NaN | 1.0000 | 0.9375 | NaN | NaN | 0.9375 | 1.00000 | 0.06250 |
659569 | 1035.971223 | 1052.631579 | 1019.830028 | 0.5625 | 0.6875 | NaN | NaN | 0.5625 | 0.6875 | NaN | NaN | 0.6875 | 0.78125 | 0.09375 |
659570 | 1031.518625 | 1031.518625 | 1031.518625 | 0.3125 | 0.2500 | NaN | NaN | 0.3125 | 0.2500 | NaN | NaN | 0.2500 | 0.93750 | 0.68750 |
659571 rows × 14 columns
velocity | last3F_vel | toL3F_vel | label_1C | label_2C | label_3C | label_4C | label_1C | label_2C | label_3C | label_4C | label_lastC | label_rate | label_diff | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 659571.000000 | 659571.000000 | 659571.000000 | 659571.000000 | 653697.000000 | 303870.000000 | 265341.000000 | 659571.000000 | 653697.000000 | 303870.000000 | 265341.000000 | 659571.000000 | 659571.000000 | 659571.000000 |
mean | 977.147806 | 973.520558 | 983.314755 | 0.505128 | 0.504528 | 0.500228 | 0.499702 | 0.505128 | 0.504528 | 0.500228 | 0.499702 | 0.499826 | 0.531985 | 0.032160 |
std | 35.653170 | 55.701478 | 37.839850 | 0.285294 | 0.284304 | 0.283401 | 0.283376 | 0.285294 | 0.284304 | 0.283401 | 0.283376 | 0.283713 | 0.285165 | 0.310796 |
min | 504.201681 | 360.721443 | 589.812332 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | 0.055556 | -0.944444 |
25% | 952.380952 | 935.064935 | 956.175299 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.250000 | 0.281250 | -0.166667 |
50% | 978.260870 | 975.609756 | 980.592441 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.500000 | 0.531250 | 0.000000 |
75% | 1001.042753 | 1016.949153 | 1008.403361 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.750000 | 0.781250 | 0.214286 |
max | 1115.241636 | 1146.496815 | 1132.075472 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.944444 |
4.1. 速度にしたかった理由¶
速度を出して何がわかる?
馬場別の特徴が分かる
4.1.1. 馬場別の馬場状態と分速の関係¶
cond_map = {"良": 0, "稍重": 1, "重": 2, "不良": 3}
q_list = np.arange(0.1, 1., 0.2)
all_map_list = []
for field, dffield in df.groupby("field"):
dfmap_list = []
for dis, dfdis in dffield.groupby('distance'):
dfq = dfdis.groupby("condition")[["velocity"]].quantile(
q_list).sort_index(
level=0, key=lambda row: [cond_map[d] for d in row]).reset_index(level=1, names=["", "Q"])
dfmap = pd.DataFrame(
dfq["velocity"].values.reshape(dfq.index.nunique(), -1),
columns=[f"Q_{str(int(q*100))}" for q in q_list],
index = dfq.index.unique()
).reset_index(names=["cond"])
dfmap["dis"] = dis
dfmap["field"] = field
dfmap = dfmap.set_index(["field", "dis", "cond"])
dfmap_list += [dfmap.copy()]
vmin=min([dfm.min().min() for dfm in dfmap_list])
vmax=max([dfm.max().max() for dfm in dfmap_list])
dfmap_list = sorted(dfmap_list, key=lambda dfm: dfm.reset_index(level=1)["dis"].unique()[0])
all_map_list += [dfmap_list]
fig, axes = plt.subplots(len(dfmap_list), 1, figsize=(30, 15))
for idx, dfmap in enumerate(dfmap_list):
sns.heatmap(
dfmap,
ax=axes[idx],
vmin=vmin,
vmax=vmax,
linewidths=0.01,
linecolor="white",
cmap="summer" if field == "芝" else "copper"
)
plt.show()
さすがに見ずら過ぎるのでSMILE距離区分を採用する
SMILEとは?
ワールド・ベスト・レースホース・ランキングで競走馬の能力を数値化するために、
距離区分を5段階に分けてレーティングしている。
文字 | 元の語 | 距離(m) |
---|---|---|
S | sprint | 1000 – 1300 |
M | mile | 1301 – 1899 |
I | intermediate | 1900 – 2100 |
L | long | 2101 – 2700 |
E | extended | 2701 以上 |
# カテゴリ分けする関数を作成
col = "dist_cat"
def sort_category(dist):
if dist > 2700:
return "E"
if dist > 2100:
return "L"
if dist > 1899:
return "I"
if dist > 1301:
return "M"
return "S"
df[col] = df["distance"].apply(sort_category)
cond_map = {"良": 0, "稍重": 1, "重": 2, "不良": 3}
dist_cat_map = {"S": 0, "M": 1, "I": 2, "L": 3, "E": 4}
q_list = np.arange(0.1, 1., 0.1)
all_map_list = []
for field, dffield in df.groupby("field"):
dfmap_list = []
for dis, dfdis in dffield.groupby('dist_cat'):
dfq = dfdis.groupby("condition")[["velocity"]].quantile(
q_list).sort_index(
level=0, key=lambda row: [cond_map[d] for d in row]).reset_index(level=1, names=["", "Q"])
dfmap = pd.DataFrame(
dfq["velocity"].values.reshape(dfq.index.nunique(), -1),
columns=[f"Q_{str(int(q*100))}" for q in q_list],
index = dfq.index.unique()
).reset_index(names=["cond"])
dfmap["dis"] = dis
dfmap["field"] = field
dfmap = dfmap.set_index(["field", "dis", "cond"])
dfmap_list += [dfmap.copy()]
vmin=min([dfm.min().min() for dfm in dfmap_list])
vmax=max([dfm.max().max() for dfm in dfmap_list])
dfmap_list = sorted(dfmap_list, key=lambda dfm: dist_cat_map[dfm.reset_index(level=1)["dis"].unique()[0]])
all_map_list += [dfmap_list]
fig, axes = plt.subplots(len(dfmap_list), 1, figsize=(16, 9))
for idx, dfmap in enumerate(dfmap_list):
sns.heatmap(
dfmap,
ax=axes[idx],
vmin=vmin,
vmax=vmax,
linewidths=0.01,
linecolor="white",
cmap="summer" if field == "芝" else "copper"
)
plt.show()
4.1.2. 馬場別の馬場状態と上り3Fの関係¶
あがり3Fの場合はどうなる?
cond_map = {"良": 0, "稍重": 1, "重": 2, "不良": 3}
dist_cat_map = {"S": 0, "M": 1, "I": 2, "L": 3, "E": 4}
q_list = np.arange(0.1, 1., 0.1)
all_map_list = []
for field, dffield in df.groupby("field"):
dfmap_list = []
for dis, dfdis in dffield.groupby('dist_cat'):
dfq = dfdis.groupby("condition")[["last3F_vel"]].quantile(
q_list).sort_index(
level=0, key=lambda row: [cond_map[d] for d in row]).reset_index(level=1, names=["", "Q"])
dfmap = pd.DataFrame(
dfq["last3F_vel"].values.reshape(dfq.index.nunique(), -1),
columns=[f"Q_{str(int(q*100))}" for q in q_list],
index = dfq.index.unique()
).reset_index(names=["cond"])
dfmap["dis"] = dis
dfmap["field"] = field
dfmap = dfmap.set_index(["field", "dis", "cond"])
dfmap_list += [dfmap.copy()]
vmin=min([dfm.min().min() for dfm in dfmap_list])
vmax=max([dfm.max().max() for dfm in dfmap_list])
dfmap_list = sorted(dfmap_list, key=lambda dfm: dist_cat_map[dfm.reset_index(level=1)["dis"].unique()[0]])
all_map_list += [dfmap_list]
fig, axes = plt.subplots(len(dfmap_list), 1, figsize=(16, 9))
for idx, dfmap in enumerate(dfmap_list):
sns.heatmap(
dfmap,
ax=axes[idx],
vmin=vmin,
vmax=vmax,
linewidths=0.01,
linecolor="white",
cmap="summer" if field == "芝" else "copper"
)
plt.show()
4.1.3. 考察¶
ヒートマップから以下のことが分かる
- ダート芝どちらも距離が長くなるにつれて、全体の走破速度が落ちている
- ダートの場合、馬場状態が悪くなるにつれて速度が上がっている
- 芝の場合、馬場状態が悪くなるにつれて速度が下がっている
- 上り3Fの場合、距離区分でみると、芝は差がでてないが、ダートでは差が出ている
- ダートでは距離が長いほどスタミナが必要であるとわかる
4.2. コーナー通過順位と最終着順の関係¶
コーナー通過順位を細かく出して何が分かるか?
お馬さんの脚質の判断ができる
4.2.1. キタサンブラックの脚質分析¶
例として「キタサンブラック」で、最終コーナー通過順位と最終着順のヒートマップを見てみる
horseId = “2012102013”
horseId = "2012102013"
dfhorse = df[df["horseId"].isin([horseId])]
dfhorse["fav_rate"] = dfhorse["favorite"]/dfhorse["horseNum"]
score = [(dfhorse["label"] == 1).sum(), (dfhorse["label"] == 2).sum(), (dfhorse["label"] == 3).sum(), (dfhorse["label"] > 3).sum()]
print(horseId, dfhorse["horseName"].unique()[0], "-".join([str(d) for d in score]))
sns.heatmap(
(dfhorse.sort_values("raceDate")[
["raceName", "dist_cat", "label", "label_lastC", "label_rate"]].set_index(
["raceName", "dist_cat", "label"])),
annot=True,
cmap="RdPu",
linewidths=0.01,
linecolor="black",
vmin=0,
vmax=1
)
plt.show()
2012102013 キタサンブラック 12-2-4-2
最終コーナー通過順位のヒートマップが全体的に薄いことから、先行または逃げの脚質であると考えられる。
実際に調べたところ、逃げの脚質であるとも言われている。
また、距離適性は中・長距離であることが分かった。
4.2.2. 最近の名馬でも確認¶
horseId = "2018105165"
dfgroup = df.groupby("horseId")
dfh = dfgroup["horseId"].count()
dfh123 = df[df["label"].isin([1, 2, 3])].groupby('horseId')["horseId"].count()
# for key in random.sample(list(dfh[(dfh>10) & (dfh<16) & (dfh123>10)].index), 3):
for key in ["2019105219", "2019105283", "2018110015"]:
dfhorse = df[df["horseId"].isin([key])]
score = [(dfhorse["label"] == 1).sum(), (dfhorse["label"] == 2).sum(), (dfhorse["label"] == 3).sum(), (dfhorse["label"] > 3).sum()]
print(key, dfhorse["horseName"].unique()[0], "-".join([str(d) for d in score]))
dfhorse["fav_rate"] = dfhorse["favorite"]/dfhorse["horseNum"]
sns.heatmap(
dfhorse.sort_values("raceDate",)[[
"raceName", "field", "dist_cat", "label", "label_lastC", "label_rate"]].set_index(
["raceName", "field", "dist_cat", "label"]),
annot=True,
cmap="RdPu",
linewidths=0.01,
linecolor="black",
vmin=0,
vmax=1
)
plt.show()
2019105219 イクイノックス 7-2-0-0
2019105283 ドウデュース 6-1-1-2
2018110015 レモンポップ 9-3-0-0
上記のように最終コーナー通過順位と最終着順をみると、その馬の脚質が分かりやすくなる
これらのカラムを使ってクラスタリングなどをすれば、似た脚質の競走馬を知ることができる
→ モデル的にも、passLabelやlabelのままデータを扱われるよりは、解釈しやすいはず
4.2.3. 脚質が分かることで何ができる?¶
もう少し検証が必要だが、脚質が分かることでレース展開を予想できるのではないかと考える。
レース展開が分かれば、ハイペースの場合はスタミナがあるお馬さんが、ローペースの場合は上り3Fが速いお馬さんが勝てるのではないかなどが予測できると考える。
また、脚質と血統の関係も考察の観点もあり、予測精度の向上が期待できる
コメント