2021年にアメリカンリーグMVPを受賞し、世界中で話題となった大谷翔平選手。
今回はSalesforceのTableau CRMを使用して、2021年の大谷選手の成績をもとに、レギュラーシーズン最終日の10/4までに本塁打を何本打つのか予測を行います。なお2021年シーズンはすでに終了しているため以下の2時点での予測と実績を比較します。
・7/12(オールスターゲーム)時点
・9/1時点
また大谷翔平選手だけでは物足りないので、本塁打争いを繰り広げたゲレーロJr選手とペレス選手の予測も行います。
使用データ
今回の分析で使用するデータは、Statcastのデータが溜まっているBaseball Savantから各選手のゲームごとの本塁打数を取得しています。このデータを時系列にならべて累計本塁打数を算出します。
Baseball Savant 大谷選手の成績
https://baseballsavant.mlb.com/savant-player/shohei-ohtani-660271?stats=gamelogs-r-hitting-mlb&season=2021
実績値
以下のグラフが2021年の各選手の累計本塁打数を時系列で表したものになります。
大谷選手は、6月だけで13本の本塁打を打ち松井秀喜さんの持つ日本人選手月間最多数タイとなりました。また松井秀喜さんの持っていた日本人メジャー最多シーズン本塁打数の31本を7/7時点で更新しています。5月から7月にかけて一気に本塁打数が増加していることがわかります。
後半戦になるとペレス選手が一気に追い上げているのがグラフからわかります。
7/12時点の予測
以下のグラフは、7/12時点の予測となります。予測は4/1からの時系列の増加をもとにSAQLステートメントのtimeaseriesを使用して算出しています。
※実装方法は、後ほど紹介します。
予測結果を見ると、大谷選手は6月の月間13本記録もあり同じ調子でシーズン終了まで続いていたら62本打つという結果になりました。(62本でも十分凄いのに、シーズン最多本塁打記録を持つB・ボンズの73本は恐ろしいですね。)
ゲレーロJr選手の予想は52本となっており、実際の値と比較しても4本差と大きなずれはなさそうです。ペレス選手は他2名と比べると本塁打数は少ないため、この時点での予測は少なめの38本となっています。
9/1時点の予測
続いて9/1時点の予測となります。
9/1時点の予測では、大谷選手は52本となっています。7月の終わりから不調が続きますが7月までの量産結果が52本という予測を生み出したのではないかと思います。
ゲレーロJr選手とペレス選手の予測は47本となっており、実際の値である48本と1本差となっているのでかなり精度の高い予測ができているのではないかと思います。
以上の2点の予測のとおり、予測にあてるデータ(過去データ)数が多くなるほど、予測も正確に近づいていくことがわかります。
実装方法
最後に簡単にTableau CRMでの予測の実装方法を解説します。
まず今回7/12時点の予測で使用したSAQLは以下になります。
q = load "DailyBatStats";
q = group q by ('date__c_Year', 'date__c_Month', 'date__c_Day', 'player__c.name_display_first_last__c');
q = filter q by date('date__c_Year', 'date__c_Month', 'date__c_Day') in [dateRange([2021,4,1], [2021,7,12])];
q = foreach q generate 'date__c_Year','date__c_Month', 'date__c_Day', 'player__c.name_display_first_last__c' as 'player__c.name_display_first_last__c', max('TotalHR__c') as 'max_TotalHR__c';
q = fill q by (dateCols=('date__c_Year','date__c_Month','date__c_Day', "Y-M-D"), partition='player__c.name_display_first_last__c');
q = timeseries q generate 'max_TotalHR__c' as HR with (length=84, dateCols=('date__c_Year','date__c_Month','date__c_Day', "Y-M-D"),partition='player__c.name_display_first_last__c');
q = foreach q generate 'date__c_Year' + "~~~" + 'date__c_Month' + "~~~" + 'date__c_Day' as 'date__c_Year~~~date__c_Month~~~date__c_Day', HR, 'player__c.name_display_first_last__c' as '選手名';
まずデータは、一つのテーブルに「選手名」「日付」「累計本塁打数」を持たせています。
以下が投入したデータセットの一部です。
日付 | 名前 | 累計本塁打数 |
2021-04-02 | Salvador Perez | 0 |
2021-04-02 | Shohei Ohtani | 0 |
2021-04-02 | Vladimir Guerrero Jr. | 0 |
2021-04-03 | Shohei Ohtani | 1 |
2021-04-04 | Salvador Perez | 0 |
2021-04-04 | Shohei Ohtani | 1 |
2021-04-04 | Vladimir Guerrero Jr. | 0 |
2021-04-05 | Salvador Perez | 1 |
2021-04-05 | Shohei Ohtani | 2 |
2021-04-05 | Vladimir Guerrero Jr. | 1 |
q = load "DailyBatStats";
q = group q by ('date__c_Year', 'date__c_Month', 'date__c_Day', 'player__c.name_display_first_last__c');
q = filter q by date('date__c_Year', 'date__c_Month', 'date__c_Day') in [dateRange([2021,4,1], [2021,7,12])];
q = foreach q generate 'date__c_Year','date__c_Month', 'date__c_Day', 'player__c.name_display_first_last__c' as 'player__c.name_display_first_last__c', max('TotalHR__c') as 'max_TotalHR__c';
ここでは、以下の3つの処理を行なっています。
・データセットの読み込み
・日付と選手名でグルーピング
・4/1から7/12までのデータに絞り込み
→今回はシーズン終了後に予測を行っているため、7/12までのデータを過去データとしています。
・新しいカラム名の定義
q = fill q by (dateCols=('date__c_Year','date__c_Month','date__c_Day', "Y-M-D"), partition='player__c.name_display_first_last__c');
ここでは、予測を行う前のデータの整形を行っています。
・fillを使用し、欠落している日付値を埋めます。
fillを使用すると以下の例のように、試合がない日付のデータも作成してくれます。
<fill使用前のデータ>
日付 | 名前 | 累計本塁打数 |
2021-05-01 | Shohei Ohtani | 15 |
2021-05-03 | Shohei Ohtani | 16 |
2021-05-07 | Shohei Ohtani | 17 |
<fill使用後のデータ>
日付 | 名前 | 累計本塁打数 |
2021-05-01 | Shohei Ohtani | 15 |
2021-05-02 | Shohei Ohtani | 15 |
2021-05-03 | Shohei Ohtani | 16 |
2021-05-04 | Shohei Ohtani | 16 |
2021-05-05 | Shohei Ohtani | 16 |
2021-05-06 | Shohei Ohtani | 17 |
Tableau CRM SAQL 開発者ガイド fill
https://developer.salesforce.com/docs/atlas.ja-jp.bi_dev_guide_saql.meta/bi_dev_guide_saql/bi_saql_statement_fill.htm
q = timeseries q generate 'max_TotalHR__c' as HR with (length=84, dateCols=('date__c_Year','date__c_Month','date__c_Day', "Y-M-D"),partition='player__c.name_display_first_last__c');
q = foreach q generate 'date__c_Year' + "~~~" + 'date__c_Month' + "~~~" + 'date__c_Day' as 'date__c_Year~~~date__c_Month~~~date__c_Day', HR, 'player__c.name_display_first_last__c' as '選手名';
最後に、整形したデータをtimeseriesに当てて予測値を出します。
timeseriesで使用しているパラメーターは以下になります。
‘max_TotalHR__c’ : 予測対象値。ここでは累積本塁打数を割り当てています。
length=84:予測するポイント数。後述するdateColsがY-M-D場合は84日分の予測します。今回は7/12から10/4までの予測値を出すため84としています。
dateCols=(‘date__c_Year’,’date__c_Month’,’date__c_Day’, “Y-M-D”):グループ化で使用する日付項目と、日付列種別の文字。
partition=’player__c.name_display_first_last__c’:データをパーティションに分けるために使用する列。今回は選手ごとで予測値を出したいため、選手名の項目を指定しています。
Tableau CRM SAQL 開発者ガイド timeseries
https://developer.salesforce.com/docs/atlas.ja-jp.bi_dev_guide_saql.meta/bi_dev_guide_saql/bi_saql_statement_timeseries.htm
最後に、予測値の入ったデータをforeachを使用して表示しています。
'date__c_Year' + "~~~" + 'date__c_Month' + "~~~" + 'date__c_Day' as 'date__c_Year~~~date__c_Month~~~date__c_Day'
ここで使用している”~~~”は、”-”を意味しています。年、月、日のそれぞれの項目を一つの年月日項目に連結をおこなっています。
以上の操作でデータの予測は完了し、残りはグラフでの表現のみとなります。
グラフは「スケジュール」を使用しています。
X軸の予測線を設定して上げることで、実績値を実線、予測値を点線といった表現も可能です。
まとめ
今回は、過去データをもとにある時点の予測を行うという実装となりましたが、2022年シーズンではリアルタイムでのデータ予測を行い本塁打王、打点王、安打王、盗塁王を算出してみたいと思います。