独学でプログラミングを学んでみた

python初心者がテックブログを書いてみた

python初学者が競馬予測をしてみたpart5(特徴量、前処理追加)

皆さんお久しぶりです。KHnodeです。

全く更新できていなくて申し訳ございません。

色々なことが起こりすぎてブログに手が付かない状態でした。
不定期更新にはなりますが、コツコツと更新していきたいです。

######################################################

今回は前処理と特徴量を変更・追加してみました。
※追記(2021.10.19)前処理のコードを一部変更しています。結果自体は変わりません。

概要

前処理

・父親、父親のタイプごとのコース、距離、芝ダート、馬場状態別の連対率を追加。
・騎手のコース別連対率を追加。
・各コースごとの連対した際の平均人気、オッズ、上がり3ハロンなどを追加。

特徴量

・3角から4角に移動した際の順位変換
・父親の平均3ハロンタイムと前走の3ハロンタイム
・父親のタイプ別平均3ハロンタイムと前走の3ハロンタイム
・前走オッズと現在オッズの差
・前走人気と現在人気の差

コード

前処理
df = read_csv_data
# 父親、父親タイプごとの連対率を作成する。

# 2着以上は1のフラグを追加する。
df.loc[df['result'] >= 3, 'result'] = 0
df.loc[df['result'] == 2, 'result'] = 1

# 父親、父親タイプごとの合計をピポットテーブルでまとめる。(indexとなっているところは父親と父親タイプで切り替え可能性)
table_father_place = pd.pivot_table(df, index=index, columns='place', values='result', aggfunc='mean',
                                            dropna=False)
table_father_distance = pd.pivot_table(df, index=index, columns='distance', values='result', aggfunc='mean',
                                               dropna=False)
table_father_turf = pd.pivot_table(df, index=index, columns='turf', values='result', aggfunc='mean',
                                           dropna=False)
table_father_condition = pd.pivot_table(df, index=index, columns='condition', values='result', aggfunc='mean',
                                                dropna=False)
# 作成したピポットテーブルをマージしていく
table_father = pd.merge(table_father_place, table_father_distance, on=index, how='left')
table_father = pd.merge(table_father, table_father_turf, on=index, how='left')
table_father = pd.merge(table_father, table_father_condition, on=index, how='left')

table_father1 = table_father.fillna(0)

# 脚質の最頻値を求める
df['legtype'] = df['legtype'].map({'逃げ': 0, '先行': 1, '差し': 2, '追込': 3, '自在': 4})
legtypes = df.groupby(index).legtype.apply(lambda x: x.mode()).reset_index()

legtype = pd.DataFrame(legtypes)
legtype['legtype'] = legtype['legtype'].map({0: '逃げ', 1: '先行', 2: '差し', 3: '追込', 4: '自在'})
legtype = legtype.drop('level_1', axis=1)

# 3ハロンタイムを出す
time_3f = df.groupby(index).mean()['3ftime']
time3f = pd.DataFrame(time_3f)

# 脚質の最頻値、3ハロンタイムをメインのデータにマージする。
father = pd.merge(table_father1, legtype, on=index, how='left')
father = pd.merge(father, time3f, on=index, how='left')

father = father.round(3)
father = father.add_prefix('{}_'.format(index))

少し解説
全体の流れとしては
父親の出走回数と2着の回数をピポットテーブルを用いて作成する。

脚質、平均3ハロンタイムを追加する
といった流れで作成しています。

脚質の最頻値を求めたかった理由としては現役で走っている馬は少なくとも父親の脚質にある程度は引き継がれるのではないかと思い作成しました。
根拠はなく、なんとなくつけているので検証していく中で削除するかもしれません。
父親のタイプも同様のコードで作成することができます。

#騎手ごとの連対率(父親と同様に作成した)

df = read_csv_data

# 2着以上は1のフラグを追加する。
df.loc[df['result'] >= 3, 'result'] = 0
df.loc[df['result'] == 2, 'result'] = 1

table_jockey = pd.pivot_table(df, index='jocky', columns='place',
                                      values='result', aggfunc='mean', dropna=False)
table_jockey = table_jockey.fillna(0)

table_jockey1 = table_jockey.fillna(0)

table_jockey1 = pd.DataFrame(table_jockey1)
table_jockey1 = table_jockey1.round(4)
table_jockey1 = table_jockey1.add_prefix('jockey_')

こちらも同様にピポットテーブルでまとめて、forループで連対率を出してまとめています。

# 各コースごとの連対
df = read_csv_data

df.loc[df['result'] >= 3, 'result'] = 0
df.loc[df['result'] == 2, 'result'] = 1

df = df.query('result == 1')

if index == 'distance':
    table_place_time = pd.pivot_table(df, index=index, values='time', aggfunc='mean')

table_place_rank3 = pd.pivot_table(df, index=index, values='rank3', aggfunc='mean')
table_place_rank4 = pd.pivot_table(df, index=index, values='rank4', aggfunc='mean')
table_place_3ftime = pd.pivot_table(df, index=index, values='3ftime', aggfunc='mean')
table_place_pop = pd.pivot_table(df, index=index, values='pop', aggfunc='mean')
table_place_odds = pd.pivot_table(df, index=index, values='odds', aggfunc='mean')

table_place = pd.merge(table_place_odds, table_place_pop, on=index, how='left')

if index == 'distance':
    table_place = pd.merge(table_place, table_place_time, on=index, how='left')
    table_place = pd.merge(table_place, table_place_rank3, on=index, how='left')
    table_place = pd.merge(table_place, table_place_rank4, on=index, how='left')
    table_place = pd.merge(table_place, table_place_3ftime, on=index, how='left')
else:
    table_place = pd.merge(table_place, table_place_rank3, on=index, how='left')
    table_place = pd.merge(table_place, table_place_rank4, on=index, how='left')
    table_place = pd.merge(table_place, table_place_3ftime, on=index, how='left')

place = pd.DataFrame(table_place)
place = place.round(3)

place = place.add_prefix('{}_'.format(index))

これも同様に作成します。
※ifとかある理由は関数で作成しているので条件分岐のためにつけています。

特徴量
# 特徴量の生成
df['re_3_to_4time'] = (df['pre_rank3'] - df['pre_rank4'])
df['father_3f_to_my'] = (df['father_3ftime'] - df['pre_3ftime'])
df['fathertype_3f_to_my'] = (df['fathertype_3ftime'] - df['pre_3ftime'])
df['re_pop_now_pop'] = (df['pre_pop'] - df['pop'])
df['re_odds_now_odds'] = (df['pre_odds'] - df['odds'])

こんな感じで特徴量を生成しました。

part1~5までの課題点

・予想の範囲が1~3番人気にと偏っている。(馬券的な旨みが少ない)
・根拠のない(or薄い)前処理特徴量の生成
・新たなモデルの作成。

この3点が挙げられます。

複勝で馬券のうまみのある馬を見つけるために前処理を中心に対策を取っていけたらなと思います。

次の記事では、そういったのに対応するための前処理について書いていこうと思います。

作成した競馬の予想モデルはgithubに挙げていますのでもしよろしければこちらも確認していただくと幸いです。
github.com