小ネタ:statcastのcsvデータから動画を確認する
表題の通り小ネタ記事です。
いつもの如くやってみた記事の個人的備忘録ですのでご承知を。特に今回の内容はデータ取得と加工ですが、コーディングに強いわけでもなく(どころか超弱い)、データ取得・加工に詳しいわけでもない(こっちも全然分からん)人間が書いているのでご注意ください。また、2025年2月某日時点の情報がふくまれており、例えば今後savantのページ構成等が変わったりすると使えなくなるかもしれません。動画が何年前のものまであるのかとか、既存のライブラリで同じ事できないのかとかは調べていません。
1. 背景と目的
statcastデータを触っているとたまに実際のプレーを確認したくなる瞬間がある(気がする)。savantの画面上で触っている時はテレビ局のカメラみたいなアイコンが出て来るのでそれを押せば良いが、手元にあるcsvとかでどんな感じだったのかをぱっと知りたい時には当然そんなものは出ない。なのでcsvデータから動画に飛ぶリンクを出力するのが目的。実用性に乏しいお遊びだが、サイトまで行って検索するのが面倒な時には使えるかも。
2. 方法
これを参考にした。ありがたや。以下、(不正確かもしれない)個人的な理解を書いておく。
動画のリンクを取得するにはplay_idという情報が必要だが、これはcsvデータ内には存在しない(らしい)。なので一旦savant上のデータ置き場的なページからplay_idを取得し、

そのplay_idを使って動画にアクセスするという手順が必要。

この手順に必要なcsvデータの項目は(私のやり方だと)以下の通り:
game_pk(各試合のID):play_idが存在するページは各試合ごとに分けられており、そこにアクセスするために必要
at_bat_number(試合中の打順の通し番号):どの打席での投球かを知る必要がある
pitch_number(各打席内の投球数):打席内で何球目の投球かを知る必要がある
後ろ2つはどのデータ(の行)に対応するかを知るために必要なだけなので、最悪game_pkを知っていればそこから探せない事もないかも(目視ではほぼ不可能だと思うが)。
具体的な処理は添付ファイル(ipynbやRmd)を参照。作成した関数に動画確認対象のデータを入れるとリンクが出る。参考までに出力サンプルを載せておく(偶然にもSEA時代のキクーチがユニコーンから見逃し三振を奪った打席!):

ちなみにリンクを踏んでページを開いても動画は自動再生されず、画面上でクリックする等のアクションが必要。やろうと思えば動画ファイルを落としてjupyter上で再生とかもできるはず。
作成ファイルは以下。
ipynbの方はPythonで書いており、polarsとpandas両方でやってみた。テケトーなサンプルを用意して簡単な確認をしたのみ。
RmdはRで書いているが、pandasのものを○hatG○Tに「変換してちょ」と丸投げして、エラーが出た部分だけ手直しした。サンプルデータでの確認はほぼ行っていない。こっちは直接リンクを出すにはknitr等でhtmlに変換するしかない(と思う)。
注意:
作成コードは最大でも3試合程度のデータに対する利用を想定している。試合数を多くするとデータ取得に時間が掛かる上に、場合によってはサイト側にも迷惑が掛かるかもしれないので気を付けること。
3. 補足
関数の引数sortに関連して少し補足。
普通は生(に近い)データを眺める場合はgame_date, game_pk, inning, inning_topbot, at_bat_number, pitch_numberあたりで(つまり時系列順に)sortした上で見ると思う。ただいろいろと弄っているとそこが壊れてしまう場合もあって(特にpolarsはデフォルトだとindexがないので壊れやすい)、でも「そんな状況でも確認したいよ〜」とか「元データと行ったり来たりするの怠いよ〜」みたいなケースもあり得るとは思う。そんな時のためにデータの順序を崩さないようにsort=Falseをデフォルトにしてデータ順序を保つようにしている(つもり、上手く行ってなかったらごめんなさい)。一方で触っている中で「これ時系列的にはどうなっとんじゃ?」となるケースもあり得るので、その場合はsort=Trueにしてやると時系列順でリンクが出るようになっている。複数の試合が含まれる場合も考慮しているつもり。
そんな感じでsortという引数を入れたが、そのためにindexという名前の列を新しく作成している。これはよく使う名前な気もするので良くなかったかも。変えるのが面倒なので一旦このままにしておくが要注意。
4.感想
コードを書くのがちょっと面倒だった。その辺はセンスも知識もないので当然かも。ついでに関数や変数の命名も良くない気がするが「今までもそうだったし今更だな」と思ってそのままにしておいた。
今回の内容はこれまで以上にネタというかお遊び感マシマシでやってみたが、もしかしたら定性分析的な事(ちゃんとデータを見ましょうねというやつ、定量的な分析だけだと限界はあるからデータを細かく見て示唆出しとかするアレ)に使えるかも。芯を食った仮説はそういう所から出て来ることが多々ある気がする。野球だと単にデータを眺めるだけでも何とかなるかもだけど、それでもデータの発生源を全く見ないのは論外だよね、本当は。机上のお勉強のためでもそういうので理解が深まったり広がったりするし。この記事の内容は置いておくとして、ある程度真っ当にやるならその手の事はちゃんとやるべきだと思う。
野球のデータ分析には直接関係ないかもしれないが、個人的にはそういうのって自然言語処理とかだとより効く印象がある。定量分析がきつい場面もある上に、生データから直接的な原因が発見しやすい…時もちょこちょこあるというか。ただ最近はLLMの台頭なんかもあってそこまで頑張らなくても良くなったりしてるのかも。作る側もだけど使う側の意識も変わった気がする。
なんて、まあいろいろと語ってみましたが「じゃあお前はその定性分析(擬き)をちゃんとやっているのか」と問われると、少なくとも趣味のデータ分析では極力やらないようにして来ましたし、今後も極力避けたいです。兎にも角にも面倒なので。それ故にお遊びであり、またそれ故に続けられるのかもしれませんね。
何とも間抜けな締めになってしまいましたが…以上、ご覧いただきありがとうございました。