Help us understand the problem. What is going on with this article?

【Python】競走馬の強さをレーダーチャートで可視化

はじめに

Pythonと競馬は今年から始めたのですが、レースに出走する競走馬の強さみたいなものを一覧で可視化できたら面白そうだなーということで、今回は2020年の日本ダービーを題材に、出走馬の強さを一覧でレーダーチャートで並べるプログラムを書いてみました。

サンプル

日本ダービー.png

一着がコントレイル、二着がサリオスだったのですが、やはりこの二頭の強さが際立ちますね…!

作り方

今回は各出走馬の過去5走を平均化したデータを題材としました。
カテゴリーは平均順位、速度指数最大値、速度指数平均値、速度指数中央値、5着以内率、現在の騎手の複勝率の6つとしました。
速度指数については、独自に計算したものを使っています。
また、こういうレーダーチャートを作る場合、カテゴリー毎のデータにばらつきがあると、レイアウトが崩れていくので、sklearnのMinMaxScalerを使って、全ての値を0から1の間で変換した上でプロットしています。
pandasを使って、プロットするデータを確認していきます。

データの確認

import pandas as pd

df=pd.read_csv(file_name,encoding='shift_jis',index_col=None)
df=df[['horseName','rank_mean', 'point_max', 'point_mean', 'point_median', 'now-jockey_top3_ratio', 'top5_ratio']]
df=df.round(2)
print('---------カテゴリー--------')
print(df.columns.values)
print('------------値-------------')
print(df.values)

--出力--

---------カテゴリー--------
['horseName' 'rank_mean' 'point_max' 'point_mean' 'point_median'
 'now-jockey_top3_ratio' 'top5_ratio']
-------------------------
[['マンオブスピリット' 2.25 3.2 -0.38 -0.95 0.37 1.0]
 ['ウインカーネリアン' 4.2 1.8 0.26 0.3 0.35 0.6]
 ['アルジャンナ' 2.0 4.1 0.62 0.45 0.31 1.0]
 ['レクセランス' 3.5 1.1 -0.22 -0.1 0.28 0.75]
 ['ヴァルコス' 2.4 4.3 1.6 1.0 0.32 1.0]
 ['ダーリントンホール' 2.8 3.6 1.18 0.9 0.42 0.8]
 ['コルテジア' 4.0 0.8 0.18 0.3 0.29 0.6]
 ['コントレイル' 1.0 4.9 1.88 1.65 0.42 1.0]
 ['サリオス' 1.25 3.0 1.5 2.2 0.54 1.0]
 ['ヴェルトライゼンデ' 2.8 1.1 -0.46 0.3 0.26 0.8]
 ['サトノインプレッサ' 4.0 2.0 -0.45 -0.35 0.21 0.75]
 ['ディープボンド' 4.0 3.2 0.82 0.9 0.25 0.6]
 ['ガロアクリーク' 4.0 1.4 -0.14 0.5 0.56 0.8]
 ['ブラックホール' 4.8 0.8 0.0 -0.2 0.16 0.6]
 ['ワーケア' 1.75 1.3 0.0 0.15 0.56 1.0]
 ['マイラプソディ' 4.0 1.2 -0.44 0.5 0.3 0.8]
 ['ビターエンダー' 4.4 2.1 0.78 0.3 0.23 0.8]
 ['サトノフラッグ' 2.8 2.4 0.62 0.9 0.41 0.8]]

データの変換

このデータを元にレーダーにプロットしていきます。
前準備でデータを0から1の間に収まるように変換していきます。
平均順位は低い方が高スコアとなるので、一律で18から引いてスコア化します。

import numpy as np
from sklearn.preprocessing import MinMaxScaler

def get_plot_data(df):
    horse_list = df['horseName'].values
    #カテゴリーと対応したカラムを設定する
    df = df[['rank_mean', 'point_max', 'point_mean', 'point_median', 'now-jockey_top3_ratio', 'top5_ratio']]

    # 18位から引くことで平均順位が低い方が好スコアにする
    df['rank_mean'] = 18 - df['rank_mean'].values
    # レーダーが閉じるように、データの末尾に先頭の要素を追加
    df['rank_mean_'] = df['rank_mean']
    # 値をスケール変換
    values = df.values
    scaler = MinMaxScaler()
    scaler.fit(values)
    values = scaler.transform(values)
    values=values.round(2)

    print('---------馬名リスト--------')
    print(horse_list)
    print('---------カテゴリー名------')
    print(df.columns.values)
    print('---------変換後の値--------')
    print(values)

    return horse_list,values

--出力--

---------馬名リスト--------
['マンオブスピリット' 'ウインカーネリアン' 'アルジャンナ' 'レクセランス' 'ヴァルコス' 'ダーリントンホール' 'コルテジア'
 'コントレイル' 'サリオス' 'ヴェルトライゼンデ' 'サトノインプレッサ' 'ディープボンド' 'ガロアクリーク' 'ブラックホール'
 'ワーケア' 'マイラプソディ' 'ビターエンダー' 'サトノフラッグ']
---------カテゴリー名------
['rank_mean' 'point_max' 'point_mean' 'point_median'
 'now-jockey_top3_ratio' 'top5_ratio' 'rank_mean_']
---------変換後の値--------
[[0.67 0.59 0.03 0.   0.52 1.   0.67]
 [0.16 0.24 0.31 0.4  0.48 0.   0.16]
 [0.74 0.8  0.46 0.44 0.38 1.   0.74]
 [0.34 0.07 0.1  0.27 0.3  0.38 0.34]
 [0.63 0.85 0.88 0.62 0.4  1.   0.63]
 [0.53 0.68 0.7  0.59 0.65 0.5  0.53]
 [0.21 0.   0.27 0.4  0.32 0.   0.21]
 [1.   1.   1.   0.83 0.65 1.   1.  ]
 [0.93 0.54 0.84 1.   0.95 1.   0.93]
 [0.53 0.07 0.   0.4  0.25 0.5  0.53]
 [0.21 0.29 0.   0.19 0.12 0.38 0.21]
 [0.21 0.59 0.55 0.59 0.22 0.   0.21]
 [0.21 0.15 0.14 0.46 1.   0.5  0.21]
 [0.   0.   0.2  0.24 0.   0.   0.  ]
 [0.8  0.12 0.2  0.35 1.   1.   0.8 ]
 [0.21 0.1  0.01 0.46 0.35 0.5  0.21]
 [0.11 0.32 0.53 0.4  0.18 0.5  0.11]
 [0.53 0.39 0.46 0.59 0.62 0.5  0.53]]

無事にスケール変換ができました。
あとはこのデータを元にレーダーにプロットしていきます。
ループ処理を使って、馬名と対応するデータをレーダーチャートにプロットしていきます。

コード全文

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from math import pi
from sklearn.preprocessing import MinMaxScaler
import japanize_matplotlib

def get_plot_data(df):
    horse_list = df['horseName'].values
    #カテゴリーと対応したカラムを設定する
    df = df[['rank_mean', 'point_max', 'point_mean', 'point_median', 'now-jockey_top3_ratio', 'top5_ratio']]

    # 18位から引くことで平均順位が低い方が好スコアにする
    df['rank_mean'] = 18 - df['rank_mean'].values
    # レーダーが閉じるように、データの末尾に先頭の要素を追加
    df['rank_mean_'] = df['rank_mean']
    # 値をスケール変換
    values = df.values
    scaler = MinMaxScaler()
    scaler.fit(values)
    values = scaler.transform(values)
    values=values.round(2)

    return horse_list,values


df=pd.read_csv(filename,encoding='shift_jis',index_col=None)
df=df[['horseName','rank_mean', 'point_max', 'point_mean', 'point_median', 'now-jockey_top3_ratio', 'top5_ratio']]

#カテゴリー名、カテゴリー数の取得
#カテゴリー数は角度の計算で使用
categories=['平均順位', '速度最大', '速度平均', '速度中央','騎手複勝率', '5着以内率']
N=len(categories)

#figureの生成
fig=plt.figure(figsize=(15,9))
#figureにタイトルを与える
fig.suptitle('<開催名>\n日本ダービー')
#角度の設定
angles = [n / float(N) * 2 * pi for n in range(N)]
#レーダーが閉じるように末尾に先頭の角度(0度)を追加
angles += angles[:1]

horse_list,values=get_plot_data(df)

#馬ごとにレーダーを作成する。
for i,horse in enumerate(horse_list):

    #馬の数=データの数なので、馬に対応したインデックスでデータを取得
    value = values[i]
    #競馬は最大で18頭立てなので、3行×6列の範囲にプロットしていく
    ax=fig.add_subplot(3,6,i+1,polar=True)
    #レーダーごとに馬名を表示
    ax.set_title(horse, weight='bold', size='medium',loc="center")
    #X軸(レーダーの外周)の設定
    plt.xticks(angles[:-1],categories,color='grey',size=7)
    #Y軸(中心から外周への距離)の設定
    plt.yticks([0.2, 0.4, 0.6, 0.8], ["0.2", "0.4", "0.6", "0.8"], color="grey", size=3)
    ax.set_rlabel_position=(180)
    #MinMaxScalerで値を0から1に変換したので、0から1の範囲で最小・最大を設定
    ax.set_rlim(0, 1)
    #プロットと塗りつぶし
    ax.plot(angles, value,linewidth=1,c='m', linestyle='solid')
    ax.fill(angles, value, alpha=0.25,c='m')

plt.tight_layout()
plt.savefig('日本ダービー.png',dpi=200)
plt.show()

おわりに

馬柱の情報を見て予測を立てるのが競馬初心者の自分には難しいので、レーダーで表示させてみました。
今日のメインのキーンランドCのプロットデータを貼り付けて終了とします。
現在の一番人気はダイアトニックです。
ディメンシオンあたりが10番人気なので面白そうです、

rader_202001020611.png

kuri_tter
非IT企業に勤めるサラリーマン。 2019年の7月頃からVBAの勉強を始め、2020年1月よりPythonの勉強を開始。 金欠でヤバイ。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした