はじめに
今回は騎手のデータを因子分析することでどのようなクラスタリングができるかを試してみます。
希望としては平場に強い騎手や、重賞で強い騎手などのクラスタリングができればとても面白いと思うところです。
使うもの
- Python
- Factor Analyzer
- Seaborn
- Matplotlib
- Pandas
使用データ
今回もnetkeibaさんからデータをスクレイピングさせていただきます。
データベースの騎手データから「現役」「美浦・栗東所属」とフィルタリングをかけたときのテーブルデータをそのままお借りしました。
import pandas as pd
import time
df = pd.read_html("https://db.netkeiba.com/?pid=jockey_list&word=&bel%5B%5D=1&bel%5B%5D=2&sort=name&list=100&act%5B%5D=0")[0]
time.sleep(1)
df = pd.concat([df, pd.read_html("https://db.netkeiba.com//?pid=jockey_list&word=&bel%5B0%5D=1&bel%5B1%5D=2&sort=name&list=100&act%5B0%5D=0&page=2")[0]])
最低限の前処理をして、生データとして書き出しておきます。
df = df.set_axis([i[0] for i in df.columns], axis=1).reset_index(drop=True)
df.set_axis(['騎手名', '所属', '生年月日', '1着', '2着', '3着', '着外', '重賞_出走', '重賞_勝利', '特別_出走', '特別_勝利',
'平場_出走', '平場_勝利', '芝_出走', '芝_勝利', 'ダート_出走', 'ダート_勝利', '勝率', '連対率', '複勝率',
'収得賞金 (万円)', '代表馬'], axis=1).to_csv("./row_data/jockey.csv", index=False)
データの前処理
まず、数字ではないデータも含まれるので数字データのみにしておきます(一応それ以外も別のデータフレームで保存しておきます)。出走数と勝利数は勝率という一つの変数に統合できるのでしておきます。データを見てみると欠損値は出走数0で割ったときのものなので、0にしておきます。
df = pd.read_csv("row_data/jockey.csv").dropna()
# 数字データとそうでないので分ける
df_non_num = df[['騎手名', '所属', '生年月日', '代表馬']]
df_num = df[['1着', '2着', '3着', '着外', '重賞_出走', '重賞_勝利', '特別_出走',
'特別_勝利', '平場_出走', '平場_勝利', '芝_出走', '芝_勝利', 'ダート_出走', 'ダート_勝利',
'勝率', '連対率', '複勝率', '収得賞金 (万円)']]
# 勝率へ変換、それ以外は除外
df_num["重賞_勝率"] = df_num["重賞_勝利"] / df_num["重賞_出走"]
df_num["特別_勝率"] = df_num["特別_勝利"] / df_num["特別_出走"]
df_num["平場_勝率"] = df_num["平場_勝利"] / df_num["平場_出走"]
df_num["芝_勝率"] = df_num["芝_勝利"] / df_num["芝_出走"]
df_num["ダート_勝率"] = df_num["ダート_勝利"] / df_num["ダート_出走"]
df_num = df_num[['勝率', '連対率', '複勝率', '収得賞金 (万円)', '重賞_勝率',
'特別_勝率', '平場_勝率', '芝_勝率', 'ダート_勝率']]
# 欠損値は0で埋める
df_num = df_num.fillna(0)
# 標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
std_data = sc.fit_transform(df_num)
因子分析
Factor Analyzerで因子分析します。sklearnでもやれますが、寄与率を出せるこっちを使っています。
from factor_analyzer import FactorAnalyzer
df_std = pd.DataFrame(std_data, columns=['勝率', '連対率', '複勝率', '収得賞金 (万円)', '重賞_勝率',
'特別_勝率', '平場_勝率', '芝_勝率', 'ダート_勝率'])
n_factors=3
fa = FactorAnalyzer(n_factors=n_factors)
fitted = fa.fit_transform(df_std)
因子負荷行列
因子負荷行列を作ってみましょう。
from factor_analyzer import FactorAnalyzer
import japanize_matplotlib
import seaborn as sns
df_loadings = pd.DataFrame(fa.loadings_,
columns=[f"第{i+1}因子" for i in range(n_factors)],
index=[df_num.columns])
sns.heatmap(df_loadings,annot=True, fmt='g', cmap='Blues')
結果です。
因子 | 特徴 | 解釈 |
---|---|---|
第1因子 | (+)勝率、連帯率、複勝率、平場勝率 | 平場で多く勝ってる |
第2因子 | (+)収得賞金、芝、ダート勝率 | 収得賞金の量(レースでの勝利数) |
第3因子 | (+)重賞、特別勝率 (-)平場勝率 | 平場以外のレースで活躍しがち |
ですかね?
累積寄与率
寄与率を見てみましょう。結果は以下の通りです。
最終的に累積寄与率が0.6以上でよいとされています。第3因子で0.7超えていたので問題ないですね。
var = fa.get_factor_variance()
df_var = pd.DataFrame(list(zip(var[1], var[2])),
index=[f"第{i+1}因子" for i in range(n_factors)],
columns=["寄与率", "累積寄与率"])
sns.heatmap(df_var.T,annot=True, fmt='g', cmap='Blues')
散布図へプロット
因子それぞれを軸にして、因子得点をプロットするとサンプルごとの関係性を見ることができます。
fact_name = {1: "平場で多く勝ってる",
2: "収得賞金の量(レースでの勝利数)",
3: "平場以外のレースで活躍しがち"}
df_non_num = df_non_num.reset_index(drop=True)
import matplotlib.pyplot as plt
import matplotlib
# 軸にする因子を決める
x = 1
y = 2
font = {'family': 'IPAexGothic'}
matplotlib.rc('font', **font)
plt.figure(figsize=(12,10))
plt.scatter(fitted[:,x-1],fitted[:,y-1])
plt.ylabel(fact_name[y], fontsize=20)
plt.xlabel(fact_name[x], fontsize=20)
count = 0
for xy in zip(fitted[:,x-1], fitted[:,y-1]):
plt.annotate(df_non_num["騎手名"][count], xy=xy, textcoords='data', fontsize=8)
count = 1+ count
まず、収得料金の量とレースでの勝利数が多い因子と、平場で多く勝ってるという因子でプロットしてみました。単純に右上に行くほど成績の良い騎手になってますね。
少し面白いのが下の部分に障害騎手がまとまっているところ。理由としては障害レースは「芝」「ダート」の両方を走るため「障害」という枠組みになるのですが、今回は芝ダートの勝率しか入ってないからですね。なのでレースは多く勝ってるけど、第1因子の得点は低いのが障害騎手になっています。
次に収得賞金の量と平場以外のレース活躍を軸にしてみます。
すると重賞や○○特別といたレースの勝ち鞍が多い騎手と少ない騎手で右左に分かれます。
右上のエリアにいる騎手は確かに今年、重賞などを勝ってる騎手ですね!
左上エリアは最近あまり大舞台での活躍がないうまい騎手がそろっていたりしますね!
まとめ
実際に勝率などのデータからここまでしっかり特徴が分かれるのは面白いですね。
今後のデータ分析にも生かせそうな気がしています。
参考文献
- netkeiba: https://www.netkeiba.com/
- factor_analyzer documentation: https://factor-analyzer.readthedocs.io/en/latest/factor_analyzer.html