初めに
こちらはゆっくりと作る競馬予想ソフト開発のロードマップ2の内容になっています。
ここでは、簡易分析その1から3までの分析結果のJupyter Notebookを公開しています。
Youtubeで競馬予想ソフトの開発の制作過程を投稿しています。再生リストはこちらを参照ください。
YouTube再生リスト:
チャンネル登録もしてもらえると嬉しいです。
競馬予想ソフト開発の概要
作成予定の競馬予想ソフトの概要は、以下のページを参照ください。
注意
簡易分析のコードには、一部有料で公開しているモジュールを使用しています。もし一緒に競馬予想ソフトを作ってみたい方は、以下のBookersリンクからソースを購入していただければと思います。
Bookersに登録&YouTubeチャンネル登録をしていただけると1000円オフで購入できます。
Bookersリンク:低アクセス回数で競馬データをスクレイピングする方法
データ分析内容:Jupyter notebook
0.必要モジュールのインポート¶
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))
データ準備¶
2010年~2023年のレースデータを用意する
# レース結果
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")
カラム情報を確認¶
思ったこと:object型が大量なので、数値化できるものは数値化したい
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 686047 entries, 0 to 686046 Data columns (total 36 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 raceId 686047 non-null object 1 place 686047 non-null object 2 raceName 686047 non-null object 3 raceDetail 686047 non-null object 4 raceDate 686047 non-null object 5 startTime 686047 non-null object 6 distance 686047 non-null object 7 weather 686047 non-null object 8 field 686047 non-null object 9 condition 686047 non-null object 10 direction 686047 non-null object 11 inoutside 686047 non-null object 12 rapTime 664019 non-null object 13 rapSumTime 664019 non-null object 14 f3Ftol3F 664019 non-null object 15 remarks 686047 non-null object 16 number 686047 non-null int64 17 boxNum 686047 non-null int64 18 label 686047 non-null object 19 odds 686047 non-null object 20 favorite 686047 non-null object 21 horseName 686047 non-null object 22 horseId 686047 non-null object 23 sex 686047 non-null object 24 age 686047 non-null int64 25 jweight 686047 non-null float64 26 jockey 686047 non-null object 27 jockeyId 686047 non-null object 28 time 686047 non-null object 29 passLabel 686047 non-null object 30 last3F 686047 non-null object 31 weight 686047 non-null object 32 gl 686047 non-null object 33 teacher 686047 non-null object 34 teacherId 686047 non-null object 35 boss 686047 non-null object dtypes: float64(1), int64(3), object(32) memory usage: 188.4+ MB
1.障害レースを除外¶
まずは、レース種がダートと芝と障害の三種類あるので、それぞれのレース数を確認
dfinfo["field"].value_counts()
field ダ 23336 芝 23237 障 1783 Name: count, dtype: int64
明らかに障害レースの開催数が少ないので除外したい。。。
他にも障害レースは他2種と比べて特殊なので、一緒に分析するのは好ましくないだろう
horseId_obstacle = df[df["field"].isin(["障"])]["horseId"].unique()
horseId_other = df[~df["field"].isin(["障"])]["horseId"].unique()
print("障害レース出走馬数:\t\t\t", len(horseId_obstacle))
print("芝,ダートレース出走馬数:\t\t\t", len(horseId_other))
print("芝,ダートにも障害にも出走している馬数:\t", len(set(horseId_obstacle) & set(horseId_other)))
障害レース出走馬数: 4493 芝,ダートレース出走馬数: 72026 芝,ダートにも障害にも出走している馬数: 4222
しかし、障害レースに出走したことがあるお馬さんのほとんどは、芝やダートレースにも出走していることが分かった
ローテーションという観点があることから、出走したレース間隔というのは、
お馬さんのパフォーマンスの発揮において重要なファクターであることは十分あり得る
出走間隔を計算する場合は障害レース除外前のデータで計算することにする
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(["障"])]
display(df[["raceId", "field"]].drop_duplicates(subset="raceId")["field"].value_counts())
display(df[["race_span"]].describe().T)
field ダ 23336 芝 23237 Name: count, dtype: int64
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
race_span | 592068.0 | 52.327077 | 61.119321 | 5.0 | 21.0 | 29.0 | 63.0 | 2143.0 |
除外できてることが分かり、出走間隔についても中央値が29日と1か月以内には半分の競走馬は次のレースに出場している。
また、4分の3にあたる競走馬も63日以内、つまり約2か月以内に次のレースに出場しているとわかった。
2.着順データの評価¶
レース結果によっては、除外とか中止とかあるよね?その辺どうなってんの?
→ ということで確認
df["label"].unique()
array(['2', '5', '9', '14', '4', '7', '10', '6', '16', '1', '8', '13', '3', '11', '15', '12', '取', '中', '除', '失', '9(降)', '17', '18', '10(降)', '5(降)', '16(降)', '12(降)', '14(降)', '8(降)', '18(降)', '11(降)', '15(降)', '2(降)', '6(降)', '7(降)', '4(降)', '13(降)', '17(降)', '3(降)'], dtype=object)
取消、中止、除外、失格は想定通りだったが降格処分が気になる。。。
調べたところ・・・
タイム上着順が高いが、何かしらの違反で着順が下がってしまっているとのこと
→ 着順を目的変数に使うのはやや危ない?
→ 降格や失格になっている場合の走破タイムはどうなのか確認
以下で、「降格」「失格」「除外」「中止」「取消」データが入っているデータを確認
idf = df[df["label"].apply(lambda d: True if re.search(r"\(降\)", d) else False)]["raceId"]
print(idf.nunique())
raceId = np.random.choice(idf.values)
display(df[df["raceId"].isin([raceId])][["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]].sort_values("time"))
idf = df[df["label"].apply(lambda d: True if re.search(r"失", d) else False)]["raceId"]
print(idf.nunique())
raceId = np.random.choice(idf.values)
display(df[df["raceId"].isin([raceId])][["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]].sort_values("time"))
idf = df[df["label"].apply(lambda d: True if re.search(r"除", d) else False)]["raceId"]
print(idf.nunique())
raceId = np.random.choice(idf.values)
display(df[df["raceId"].isin([raceId])][["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]].sort_values("time"))
idf = df[df["label"].apply(lambda d: True if re.search(r"中", d) else False)]["raceId"]
print(idf.nunique())
raceId = np.random.choice(idf.values)
display(df[df["raceId"].isin([raceId])][["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]].sort_values("time"))
idf = df[df["label"].apply(lambda d: True if re.search(r"取", d) else False)]["raceId"]
print(idf.nunique())
raceId = np.random.choice(idf.values)
display(df[df["raceId"].isin([raceId])][["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]].sort_values("time"))
85
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
42439 | 201008060101 | 2歳未勝利 | 2 | 1 | 86.2 | 1 | 1-1 |
42448 | 201008060101 | 2歳未勝利 | 11 | 2 | 86.9 | 4 | 4-3 |
42446 | 201008060101 | 2歳未勝利 | 9 | 3 | 87.1 | 3 | 4-3 |
42453 | 201008060101 | 2歳未勝利 | 16 | 4 | 87.3 | 11 | 6-6 |
42441 | 201008060101 | 2歳未勝利 | 4 | 5 | 87.5 | 9 | 14-16 |
42449 | 201008060101 | 2歳未勝利 | 12 | 6 | 87.9 | 6 | 9-8 |
42442 | 201008060101 | 2歳未勝利 | 5 | 7 | 88.0 | 2 | 2-2 |
42443 | 201008060101 | 2歳未勝利 | 6 | 8 | 89.1 | 15 | 14-12 |
42452 | 201008060101 | 2歳未勝利 | 15 | 9 | 89.3 | 10 | 6-6 |
42445 | 201008060101 | 2歳未勝利 | 8 | 10 | 89.5 | 5 | 3-3 |
42447 | 201008060101 | 2歳未勝利 | 10 | 11 | 89.7 | 16 | 14-12 |
42438 | 201008060101 | 2歳未勝利 | 1 | 13(降) | 90.0 | 12 | 10-9 |
42451 | 201008060101 | 2歳未勝利 | 14 | 12 | 90.0 | 14 | 12-12 |
42440 | 201008060101 | 2歳未勝利 | 3 | 14 | 90.3 | 13 | 13-15 |
42444 | 201008060101 | 2歳未勝利 | 7 | 15 | 91.0 | 8 | 10-11 |
42450 | 201008060101 | 2歳未勝利 | 13 | 16 | 91.3 | 7 | 6-10 |
15
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
132425 | 201210020908 | 3歳以上500万下 | 1 | 中 | 15 | 11-11-10 | |
132426 | 201210020908 | 3歳以上500万下 | 2 | 中 | 11 | 3-4-7 | |
132430 | 201210020908 | 3歳以上500万下 | 6 | 中 | 8 | 5-5-9 | |
132435 | 201210020908 | 3歳以上500万下 | 11 | 中 | 7 | 2-2-2 | |
132434 | 201210020908 | 3歳以上500万下 | 10 | 1 | 105.9 | 1 | 9-9-5-1 |
132440 | 201210020908 | 3歳以上500万下 | 16 | 2 | 106.3 | 2 | 13-13-13-6 |
132433 | 201210020908 | 3歳以上500万下 | 9 | 失 | 106.4 | 6 | 14-14-2-2 |
132432 | 201210020908 | 3歳以上500万下 | 8 | 3 | 106.9 | 3 | 11-11-10-3 |
132428 | 201210020908 | 3歳以上500万下 | 4 | 4 | 107.4 | 4 | 7-7-10-8 |
132437 | 201210020908 | 3歳以上500万下 | 13 | 5 | 107.5 | 12 | 16-16-16-7 |
132427 | 201210020908 | 3歳以上500万下 | 3 | 6 | 108.2 | 16 | 14-14-13-8 |
132431 | 201210020908 | 3歳以上500万下 | 7 | 7 | 108.5 | 10 | 3-3-2-5 |
132436 | 201210020908 | 3歳以上500万下 | 12 | 8 | 110.4 | 14 | 1-1-1-3 |
132438 | 201210020908 | 3歳以上500万下 | 14 | 9 | 110.4 | 5 | 7-7-7-10 |
132439 | 201210020908 | 3歳以上500万下 | 15 | 10 | 110.9 | 13 | 5-5-5-10 |
132429 | 201210020908 | 3歳以上500万下 | 5 | 11 | 112.2 | 9 | 9-9-15-12 |
1131
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
35305 | 201009040104 | 2歳新馬 | 9 | 除 | |||
35307 | 201009040104 | 2歳新馬 | 11 | 1 | 87.9 | 4 | 6-4 |
35301 | 201009040104 | 2歳新馬 | 5 | 3 | 88.2 | 7 | 1-1 |
35304 | 201009040104 | 2歳新馬 | 8 | 2 | 88.2 | 2 | 6-6 |
35299 | 201009040104 | 2歳新馬 | 3 | 4 | 89.1 | 6 | 6-7 |
35302 | 201009040104 | 2歳新馬 | 6 | 5 | 90.0 | 10 | 2-8 |
35306 | 201009040104 | 2歳新馬 | 10 | 6 | 90.5 | 3 | 2-2 |
35297 | 201009040104 | 2歳新馬 | 1 | 7 | 90.7 | 1 | 2-4 |
35303 | 201009040104 | 2歳新馬 | 7 | 8 | 90.7 | 5 | 2-2 |
35300 | 201009040104 | 2歳新馬 | 4 | 9 | 91.4 | 8 | 10-10 |
35298 | 201009040104 | 2歳新馬 | 2 | 10 | 91.5 | 9 | 9-9 |
1932
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
619336 | 202204020207 | 3歳未勝利 | 4 | 中 | 7 | 9-12 | |
619340 | 202204020207 | 3歳未勝利 | 8 | 1 | 113.2 | 1 | 6-7-6-5 |
619338 | 202204020207 | 3歳未勝利 | 6 | 2 | 113.4 | 6 | 2-2-2-2 |
619335 | 202204020207 | 3歳未勝利 | 3 | 3 | 113.7 | 3 | 1-1-1-1 |
619343 | 202204020207 | 3歳未勝利 | 11 | 4 | 113.8 | 4 | 4-3-3-3 |
619341 | 202204020207 | 3歳未勝利 | 9 | 5 | 114.0 | 2 | 9-7-3-3 |
619333 | 202204020207 | 3歳未勝利 | 1 | 6 | 114.4 | 13 | 13-12-11-9 |
619347 | 202204020207 | 3歳未勝利 | 15 | 7 | 114.5 | 5 | 6-5-6-6 |
619346 | 202204020207 | 3歳未勝利 | 14 | 8 | 115.7 | 14 | 8-7-9-8 |
619344 | 202204020207 | 3歳未勝利 | 12 | 9 | 115.8 | 11 | 4-5-5-6 |
619339 | 202204020207 | 3歳未勝利 | 7 | 10 | 116.2 | 15 | 14-15-12-9 |
619334 | 202204020207 | 3歳未勝利 | 2 | 12 | 117.6 | 9 | 14-14-13-13 |
619337 | 202204020207 | 3歳未勝利 | 5 | 11 | 117.6 | 12 | 9-10-13-13 |
619342 | 202204020207 | 3歳未勝利 | 10 | 13 | 117.6 | 8 | 9-11-9-12 |
619345 | 202204020207 | 3歳未勝利 | 13 | 14 | 118.1 | 10 | 3-3-6-11 |
1141
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
129625 | 201201010609 | 3歳以上500万下 | 13 | 取 | |||
129615 | 201201010609 | 3歳以上500万下 | 3 | 2 | 69.0 | 10 | 6-5 |
129617 | 201201010609 | 3歳以上500万下 | 5 | 1 | 69.0 | 2 | 9-7 |
129620 | 201201010609 | 3歳以上500万下 | 8 | 3 | 69.1 | 5 | 11-12 |
129627 | 201201010609 | 3歳以上500万下 | 15 | 4 | 69.1 | 4 | 6-7 |
129614 | 201201010609 | 3歳以上500万下 | 2 | 5 | 69.4 | 9 | 15-14 |
129626 | 201201010609 | 3歳以上500万下 | 14 | 6 | 69.4 | 6 | 4-5 |
129613 | 201201010609 | 3歳以上500万下 | 1 | 8 | 69.5 | 12 | 13-10 |
129622 | 201201010609 | 3歳以上500万下 | 10 | 7 | 69.5 | 1 | 1-1 |
129624 | 201201010609 | 3歳以上500万下 | 12 | 9 | 69.7 | 15 | 14-15 |
129616 | 201201010609 | 3歳以上500万下 | 4 | 11 | 69.8 | 7 | 6-7 |
129621 | 201201010609 | 3歳以上500万下 | 9 | 12 | 69.8 | 3 | 2-2 |
129623 | 201201010609 | 3歳以上500万下 | 11 | 10 | 69.8 | 11 | 4-3 |
129619 | 201201010609 | 3歳以上500万下 | 7 | 13 | 69.9 | 8 | 9-10 |
129618 | 201201010609 | 3歳以上500万下 | 6 | 14 | 70.1 | 13 | 11-13 |
129628 | 201201010609 | 3歳以上500万下 | 16 | 15 | 70.4 | 14 | 2-3 |
降格、失格以外は、走破タイムがないので、それらはレコードを削除して良さそう。
降格や失格が発生したレースは本来の走りが出来なかった馬が少なくとも1頭は存在しているため、そのまま扱うのは難しい印象
しかし、走破タイムは残っているためデータとしては使用したい。
→ 降格や失格が起きたレースはフラグを立てて置き、降格や失格した馬にはマークを付ける
- 失格の場合は、着順を19位にする
- 降格の場合は、降格したというマークを付ける
- 降格や失格が出たレースにはフラグを立てる
結果を見ると、降格や失格となった馬の数は2010年から2023年の間で86頭であり、決して多くないが影響を受けた馬の総数は全体で1202頭(内ユニーク数1152頭)もいることから馬のパフォーマンスの評価をする上では影響している可能性もあるためデータとして含めて置いた方が良さそう
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)
print("期間:", df["raceId"].min()[:4]+"年", "~", df["raceId"].max()[:4]+"年")
print("降格, 失格処分馬数:", df["label_mark"].sum())
print("降格, 失格馬発生レース数:", len(raceId_list))
print(
"影響を受けた馬の総数:",
df[df["label_flag"].isin([1])&df["label_mark"].isin([0])&~df["label"].isin(["19"])].__len__()
)
print(
"影響を受けた馬のユニーク数:",
df[df["label_flag"].isin([1])&df["label_mark"].isin([0])&~df["label"].isin(["19"])]["horseId"].nunique()
)
期間: 2010年 ~ 2023年 降格, 失格処分馬数: 86 降格, 失格馬発生レース数: 85 影響を受けた馬の総数: 1202 影響を受けた馬のユニーク数: 1152
以下でみると、「中止」「取消」「除外」のデータには走破タイムがないので、データとして不十分とみなし削除してもよさそう
pd.concat(
[
df[df["label"].apply(lambda d: True if re.search(r"中", d) else False)].iloc[[-1]],
df[df["label"].apply(lambda d: True if re.search(r"取", d) else False)].iloc[[-1]],
df[df["label"].apply(lambda d: True if re.search(r"除", d) else False)].iloc[[-1]],
]
)[["raceId", "raceDetail", "number", "label", "time", "favorite", "passLabel"]]
raceId | raceDetail | number | label | time | favorite | passLabel | |
---|---|---|---|---|---|---|---|
685854 | 202306050911 | 2歳オープン | 9 | 中 | 8 | 6-7-5-10 | |
685862 | 202306050911 | 2歳オープン | 17 | 取 | |||
685629 | 202309050809 | 3歳以上2勝クラス | 3 | 除 |
以下で、「中止」「取消」「除外」のデータを削除する
# df_backup = df.copy()
# df = df_backup.copy()
df = df[df["label"].apply(lambda d: False if re.search(r"中|取|除", d) else True)].reset_index(drop=True)
df["label"].unique()
array(['2', '5', '9', '14', '4', '7', '10', '6', '16', '1', '8', '13', '3', '11', '15', '12', '19', '9(降)', '17', '18', '10(降)', '5(降)', '16(降)', '12(降)', '14(降)', '8(降)', '18(降)', '11(降)', '15(降)', '2(降)', '6(降)', '7(降)', '4(降)', '13(降)', '17(降)', '3(降)'], dtype=object)
降格も含めたデータも併せて、着順データを数値データに変換する。
以下では、正規表現を用いて、数字のみを検索するやり方で対応している
df["label"] = df["label"].apply(lambda d: int(re.search(r"\d+", d).group()))
df["label"].unique()
array([ 2, 5, 9, 14, 4, 7, 10, 6, 16, 1, 8, 13, 3, 11, 15, 12, 19, 17, 18], dtype=int64)
3.Noneデータの確認¶
まずは、以前分析していた際に発見していた上り3Fがないデータが存在しているので、該当データを確認する
df["last3F"] = pd.to_numeric(df["last3F"], errors="coerce")
df["time"] = pd.to_numeric(df["time"], errors="coerce")
df[df["last3F"].isna()][["raceId", "horseId", "label", "last3F", "time"]]
raceId | horseId | label | last3F | time | |
---|---|---|---|---|---|
242465 | 201506010808 | 2011105359 | 11 | NaN | 177.2 |
287706 | 201509050807 | 2011104178 | 18 | NaN | 220.4 |
297573 | 201606020601 | 2013103166 | 16 | NaN | 184.7 |
上り3Fのデータがない
実際にレース情報を見ると、通常のレースと比してゴールするまでの時間が非常に長い
何かしらのトラブルがあったが、最後まで走り続けたか
いずれにせよ、本来のパフォーマンスを出して走り切ったとは言えないので除外する
dfna = df["last3F"].isna()
df = df[~df["last3F"].isna()]
df.reset_index(drop=True, inplace=True)
# 削除されたか確認
display(df[dfna]["last3F"])
242465 38.4 287706 36.1 297573 43.0 Name: last3F, dtype: float64
df.columns
Index(['raceId', 'place', 'raceName', 'raceDetail', 'raceDate', 'startTime', 'distance', 'weather', 'field', 'condition', 'direction', 'inoutside', 'rapTime', 'rapSumTime', 'f3Ftol3F', 'remarks', 'number', 'boxNum', 'label', 'odds', 'favorite', 'horseName', 'horseId', 'sex', 'age', 'jweight', 'jockey', 'jockeyId', 'time', 'passLabel', 'last3F', 'weight', 'gl', 'teacher', 'teacherId', 'boss', 'race_span', 'label_mark', 'label_flag'], dtype='object')
他にNoneがあるデータがないか確認
そのために、数値化できるカラムをすべて数値化させる
対象のカラム: [
‘distance’, ‘number’, ‘boxNum’,
‘label’, ‘odds’, ‘favorite’, ‘age’,
‘jweight’, ‘time’, ‘weight’, ‘gl’
]
# 数値化対象のカラムを列挙
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")
# NaNがあるか確認
df[target_columns].isna().sum()
distance 0 number 0 boxNum 0 label 0 odds 0 favorite 0 age 0 jweight 0 time 0 weight 1 gl 1 dtype: int64
# 居たので、確認
display(df[df["weight"].isna()][["raceId", "raceDetail", "horseName", "horseId", "odds", "jweight", "weight", "gl"]])
display(df[df["gl"].isna()][["raceId", "raceDetail", "horseName", "horseId", "odds", "jweight", "weight", "gl"]])
raceId | raceDetail | horseName | horseId | odds | jweight | weight | gl | |
---|---|---|---|---|---|---|---|---|
164295 | 201305030305 | 2歳新馬 | サビーナクレスタ | 2011101732 | 25.9 | 54.0 | NaN | NaN |
raceId | raceDetail | horseName | horseId | odds | jweight | weight | gl | |
---|---|---|---|---|---|---|---|---|
164295 | 201305030305 | 2歳新馬 | サビーナクレスタ | 2011101732 | 25.9 | 54.0 | NaN | NaN |
NaNとなっているのが1データだけで、おまけに新馬戦。直近のデータ(次の出走レース)の馬体重と増減で埋め合わせる方針とする
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"]
# 埋め合わせられたか確認
display(df[dfna][
["raceId", "raceDetail", "horseName", "horseId",
"odds", "jweight", "weight", "gl"]
])
raceId | raceDetail | horseName | horseId | odds | jweight | weight | gl | |
---|---|---|---|---|---|---|---|---|
164295 | 201305030305 | 2歳新馬 | サビーナクレスタ | 2011101732 | 25.9 | 54.0 | 450.0 | 0.0 |
df[target_columns].info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 659571 entries, 0 to 659570 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 distance 659571 non-null int64 1 number 659571 non-null int64 2 boxNum 659571 non-null int64 3 label 659571 non-null int64 4 odds 659571 non-null float64 5 favorite 659571 non-null int64 6 age 659571 non-null int64 7 jweight 659571 non-null float64 8 time 659571 non-null float64 9 weight 659571 non-null float64 10 gl 659571 non-null float64 dtypes: float64(5), int64(6) memory usage: 55.4 MB
コメント