本記事ではモデリングに使えるデータセットを作っていきます!
競馬予測チャレンジ目次!
- データ取得編1(過去のレースデータ取得)
- データ取得編2(スピード指数取得)
- 特徴量エンジニアリング編 ⇦本記事はココ
- モデリング、評価編
- 実践編
競馬の予測モデルを作成するためのデータセットを作ってい行きます。データ取得編でスクレイピングしてcsvファイルにしたものを結合していき、使える形にしていきます!
使用するデータの確認
これまで、netkeiba.comと競馬新聞&スピード指数(無料)から競馬レースに関するデータを取得しました。さらに使えそうなデータを取捨選択します。内訳としては次のようになります。
- 馬名
- 馬場種類(ダート or 芝)
- 右曲がり or 左曲がり
- 天気
- 走行距離
- 枠番
- 馬番
- 斤量
- 騎手
- 馬主
- 賞金
- 性別
- 年齢
- 馬体重
- 馬体重変化量
- スピード指数
ここから様々な特徴量を生成します。
どんな特徴量が作れそうか?
今回検討した特徴量は、
- 過去レースの着順(1回前、2回前、3回前、5回前)
- 過去スピード指数(1回前、2回前、3回前、5回前)
- 前回レースからの日数
- 前回レースの走行距離
- これまでの獲得賞金合計
- 連帯率(過去に2位以上になった割合)
- 平均スピード(これまでの総走行距離÷全レースの走行タイム合計)
やっぱりやらないといけないデータクレンジング
来ましたね。面倒くさいやつ。
これをやらないとモデリングができないんです。
今回スクレイピングでデータを取得していますので、スクレイピングミスによる異常値を疑いましょう。
スクレイピングは使えると便利ですが、あくまで機械的にしか情報を取ることができません。
全てに対応することもできるかもしれませんが、数が多く非常に手間です。
混ざってしまったおかしなデータはPythonで取り除きましょう。
具体的には、
- 欠損処理
- 数値データの異常値除去(例として着順には数値だけでなく”中”と”取”といった文字が含まれる)
→そもそも数値ではなくテキストデータなので異常値を見つけるのが大変 - 馬名の訂正
などなどです。
今回はテキストデータの異常値を見つける方法を共有したいと思います。
異常値、欠損値の発見にvalue_counts()を使う
value_counts()はDataFrameの一つのカラムを指定し、そのカラムに含まれるユニークな値とその個数をカウントしてくれる機能です。
自分はよくこの機能を異常値発見に使います。
例えば、今回のデータに含まれる着順を見てみます。
df['着順'].value_counts(dropna=False)
ちなみに、”dropna=False”を指定すると、NaNになっているデータ数もカウントしてくれます。
結果は、、、
1 10201
3 10195
2 10191
#### 省略 ####
中 629
取 251
除 220
2(降) 4
3(降) 2
4(降) 1
5(降) 1
失 1
着順のカラムなので数値だけかと思いきや、後半の方に文字列が混じっていることがわかります。
ただしvalue_counts()の欠点は、指定したカラムのユニーク数が1000を超えるなど多い場合、異常値を探すことが困難となります。
そこで次のテクニックとして、文字数に着目します。
文字数をカウントし、極端に長いデータを見つける
この手法を使うことで、通常では考えにくい長さのデータを発見することができます。
今回の例だと、スクレイピングミスや文字化けで発生する馬名の異常値です。
文字数をカウントするのに便利な関数は自分の知る限りないので、少し工夫してやります。
下のコードは馬名を1文字レベルでリストに分割し、そのリスト数つまり文字数をカウントしています。
df['馬名カウント'] = df['馬名'].map(lambda x:len(list(str(x))))
df.sort_values(by='馬名カウント',ascending=False)
さて、データを並び替えて表示してみると…
馬名 name_num
<font size="-2">(外)</font>ショウナンダニエル 35
<font size="-2">(外)</font>マテンロウゴースト 35
<font size="-2">(外)</font>エイシンエルヴィン 35
<font size="-2">(外)</font>フランクエトワール 35
<font size="-2">(外)</font>バーボンハイボール 35
<font size="-2">(外)</font>ロシアンサモワール 35
このように馬名の前にサイトに記載されているコードの一部と思われる英数字が紛れ込んでいることがわかります。
異常値を見つけることができたらあとは該当するデータを除去するいった処理を行います。
今回の例では運よく、余計な英数字が共通した長さの文字だったので、簡単に取り除くだけで修正できました。
df['馬名']=df['馬名'].map(lambda x:str(x)[26:] if 'font' in x else x)
↑のコードでは英数字の”font”という文字が入っていたら26文字目までを削除しています。
最終的なデータセット
データセットを作る際に、気を付けたいのはリーケージです。
スピード指数などはそのまま使うことはできません。
2015年~2019年までのデータで特徴量を生成して反映させます。
カラム名 | 説明 |
weather | 天気 |
field_cond | 馬場の状態 |
kisyu | 騎手名 |
wakuban | 枠番 |
umaban | 馬番 |
kinryo | 斤量 |
surface | 芝 or ダート |
distance | レース距離 |
turn | 右回り、左回りか |
sex | 性別 |
age | 年齢 |
race_num | 〇日目のレース |
race_place | レース場所 |
weight | 馬体重 |
weight_c | 馬体重の変化量 |
last_days | 最後のレースからの日数 |
target | 3着以内かどうか |
speed | 総走行距離/総タイム |
5prev_result | 5回前の着順 |
3prev_result | 3回前の着順 |
2prev_result | 2回前の着順 |
prev_result | 1回前の着順 |
p_speed_index | 1回前のスピード指数 |
2p_speed_index | 2回前のスピード指数 |
3p_speed_index | 3回前のスピード指数 |
5p_speed_index | 5回前のスピード指数 |
prev_speed | 前回レースの平均速度 |
prev_dist | 前回レースの走行距離 |
prize_sum | これまでの賞金総額 |
rentai_rate | 連帯率(2位以上の割合) |
ついにデータセット完成や!
次はモデリング、評価をやっていきます!
コメント