1. 本記事の目的と背景
本記事では、自動車レースの Super GT GT500 クラスにおける、Q1 予選結果とサクセスウェイトの相関を Python で集計し、サーキットごとにどんな特徴があるか簡単に分析してみたいと思います。
(遊びの範疇で真面目に分析しようとは思ってないのでその点ご了承ください)
Super GT という自動車レースをご存じない人のために、本章では背景となる知識を説明します。
- 予選
- 各レースでは、まず予選を行い、各車両がサーキット1周のタイムを競い、その成績の良い順に決勝レースのグリッド順が決定されます。
- 決勝は作戦やトラブルなどが結果に影響する割合が多い一方、予選は1周のみのタイム結果のため、車両本来の性能は予選結果により反映されやすいと思います。
- 多くのレースでは予選は Q1 (全車両走行)と Q2 (Q1 上位車両のみ走行)の2回に分けて行われます。
- サクセスウェイト
- Super GT では各車両の性能が拮抗し面白いレースになるように、サクセスウェイトという制度があります。
- この制度により、各車両は前回までのラウンドの成績に基づいて追加の重り(ウェイト)を搭載することが課せられています。成績の良い車両は重りが増えて遅くなるため、今まで成績が振るわなかった車両にも上位進出のチャンスがでてきます。
- 公式記録としては、このサクセスウェイトは搭載する重りの重量(kg)を指示していますが、実際には 100kg を超える場合は代わりに利用できる燃料を制限することもあります。
本記事では、2013~2021年のSuper GT 上位クラスである GT500 の Q1 の予選結果(全車両が走行しているため)において、予選結果(1周のタイム)とサクセスウェイトがどれだけ相関しているか確認していきたいと思います。
(なぜ 2021年までかという、この記事を書いたのが2022年頃だからです)
2. 相関係数を計算するプログラム
今回は Oracle Linux 8.5 + Python 3.9.6 + Jupyter Notebook の環境で作業します。
事前に必要なモジュールをインポートしておきます。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
2-1. 前処理
Q1 の予選結果は Super GT の公式ページから取得できますので、それを整理した CSV ファイルを読み込みます。
# CSVから全データを読み込み
# 各ラウンドでlap_time順では並んでいないことに注意(Q1ではなくQ2のタイム順)
all_data = pd.read_csv('SuperGT_Results_Q1_2013-2021.csv', sep = '\t')
all_data.head(20)
このデータでは1周のラップタイムは (分)'(秒).(ミリ秒) というフォーマットの文字列になっているので、これを秒数を表す float64 型に変換する関数を準備しておきます。
# CSVファイルの lap_time 列(M'SS.FFF形式)を秒(float64)に変換する関数
def convert_lap_time(lap_time):
(min, sec, milli) = (int(lap_time[0]), int(lap_time[2:4]), int(lap_time[5:8]))
return min * 60 + sec + (milli / 1000)
また、lap_time 列と weight 列には NaN が含まれています(CSVファイル内では空欄だったため)。lap_time 列が NaN は予選で計測結果を出せなかったことを表しているので今回の計算からは除外します。また、weight 列が NaN はサクセスウェイトを搭載していないことを表すので 0.0 で置換します。
# 以下の前処理を行う
# ・lap_timeがNaN(タイム計測できなかったケース)の行を削除
# ・weightがNaN(ウェイトなしのケース)について、0.0で置換
target_data = all_data.dropna(subset = ['lap_time'])
target_data = target_data.fillna({'weight' : 0.0})
target_data.head(20)
予選結果とサクセスウェイト(weight)の相関を見るのですが、lap_time 列はサーキットごとに大きく異なるため、異なるラウンド/サーキットで比較できるように (自車両のタイム) / (Q1 のベストタイム) で計算した値を使いたいと思います(トップの車両は 1.0、タイムが 1% 遅いと 1.01 になる)。
別に定数倍の差は相関係数には影響しないのですが、後で述べる外れ値除去のために計算するので、今回はそれを相関係数の計算元データとします。
# lap_timeを変換
target_data['lap_time_sec'] = target_data['lap_time'].apply(convert_lap_time)
# ラウンドごとのトップタイムとの比を計算する
target_data['top_time'] = target_data.groupby(['year', 'round']).lap_time_sec.transform(np.min)
target_data['time_ratio'] = target_data['lap_time_sec'] / target_data['top_time']
target_data.head(20)
ここで、求めた time_ratio の分布を確認してみます。
plt.hist(target_data['time_ratio'], bins = 12, range = (1.0, 1.12))
time_ratio が大きいのは何かしらの問題(車両にトラブルが発生した、別の車両に引っかかったなど)で予選において満足な走行ができていないケースが多いです。それらの結果は車両性能を反映していないため、今回の分析対象からは外します。若干恣意的ですが、今回は time_ratio が 1.03 以上は外れ値として除外します。
target_data = target_data.query('time_ratio <= 1.03')
2-1. 相関係数の計算
各ラウンドごとの time_ratio と weight の相関を確認するために、相関直線付きの散布図を作成する関数を準備します。
# 指定された年/ラウンドのweightとtime_ratioの相関グラフを作成する
def get_corr_graph(target_data, year, round):
# 該当年/ラウンドに絞り込み
round_data = target_data.query('year == {} & round == \'Rd{}\''.format(year, round))
time_ratio = round_data[['time_ratio']]
weight = round_data[['weight']]
# 相関係数の計算
corr = round_data['time_ratio'].corr(round_data['weight'])
# グラフの設定
fig = plt.figure()
fig.suptitle('{} Round.{} {} r = {:.2f}'.format(year, round, round_data['circuit'].iloc[0], corr))
plt.xlim(1.00, 1.03)
plt.xlabel('time_ratio')
plt.ylabel('weight')
# 散布図の描画
plt.scatter(time_ratio, weight)
# 相関直線の描画
lr = LinearRegression()
lr.fit(time_ratio, weight)
plt.plot(time_ratio, lr.predict(time_ratio), color = 'red')
# グラフの保存
plt.savefig('{}_Rd{}_{}.png'.format(year, round, round_data['circuit'].iloc[0]))
グラフにも相関係数を記載していますが、表形式で入手できた方が便利なので、計算結果を CSV ファイルに出力しておきます。
target_data.groupby(['year', 'round', 'circuit'])[['time_ratio', 'weight']].corr().to_csv('corr.csv')
これで分析する準備が整いました。
3. サーキットごとの分析
それでは、本題であるサーキットごとの計算結果を見ていきたいと思います。
ちなみにグラフは横軸は 1.00-1.03 で合わせてありますが、縦軸は合わせていないのでその点注意してください。タイトル部分に予選結果(time_ratio)とサクセスウェイトの相関係数が表示してあるので、グラフの傾きよりそちらを見てもらった方が良いかもしれません。
(以下で「相関が比較的高い」というあいまいな表現を使っていますが、0.4 以上を指しています)
3-1. 鈴鹿サーキット
鈴鹿サーキットはテクニカルコースとして有名ですが、それを反映してか 2016, 2017, 2018, 2020, 2021年と比較的相関係数が高くなっており、ウェイトの大きい車両は不利なのかなと思います。
3-2. 富士スピードウェイ
富士スピードウェイは 1.5km のストレートと後半の上りテクニカルセクションが特徴的なサーキットです。2014, 2015, 2020(Rd5)年と相関係数が比較的高い一方、2017, 2021年と逆相関に近い年もあり、特に近年は差が小さい(グラフの点が左に偏っている)ということもあり、サクセスウェイトの差が出づらい年もあるのかなと思います。
3-3. ツインリンクもてぎ(現:モビリティリゾートもてぎ)
ツインリンクもてぎはストップ&ゴー(ストレートとブレーキを強く踏むコーナーが交互に続く)が特徴のサーキットです。2016年は比較的相関が高いですが、それ以外はサクセスウェイトの差が出づらいのかなと思います。
3-4. オートポリス
オートポリスはコーナーが続く箇所が多くテクニカルなサーキットです。2021年は相関高いですが、それ以外はどちらかというと逆相関の年も少なくなく、意外にサクセスウェイトの影響は少ないんですかね。
3-5. スポーツランドSUGO
スポーツランドSUGOは最終コーナーからホームストレートにかけて急な登り勾配で車のパワーが求められるサーキットです。それを反映してか、比較的相関が高く出ている年が多いように思えます。
4. まとめ
雑な分析なので、今回の結果から何か言えるわけではないですが、サーキットごとに違いがあって面白いですね。