見出し画像

Retrosheetのファイル展開が俺たちの入門を妨げている件

 友達が彼女に渡すアクセサリーを失くしたと聞いたので「マイナス4°Cだね」と慰めたら棘のある口調で「二度と喋んな」と返されました。2度じゃなくて4℃だっつってんのに。ひょっとすると怒られたのはぼくがニヤニヤしていたからかもしれませんが、同じ内容でも話した時の顔つきで評価が変わるというのはいささか納得いきませんよね。彼とは今度アイスホッケーでもやって決着をつけようと思います。これが表情の格闘技ということでしょうか。


 どうも、TJ(@11_tjr)です。お世話になっているtsuyupon(@yakiu_pon)さん、西脇さんが翻訳された 'Analyzing Baseball Data with R'、邦題『Rによるセイバーメトリクス入門』について、テキスト通りにやっても上手くいかないというツイートをタイムラインでちょいちょい拝見します。多くの場合どことは明示されていないのですが、ぼく自身も勉強中に一ヶ所苦戦したところがあったので恐らくそれじゃないかと思います。やってみると大した問題ではないのですが、noteにまとめておくことでお困りの皆様のお役に立てば。

 冒頭にしょうもない雑談を書くやつ、よく考えたら「いかがでしたでしょうか!?」系のク〇まとめサイトの出だしとあんまり変わらないですよね。せっかく安くない書籍を購入したのに実行が上手くいかず、対処に困ってこの記事に流れ着いた読者の皆さんの貴重なお時間を奪うことには強い罪悪感を覚えます。まあそれでも書いちゃうんですけどね。


問題

 今回私が解決する問題はRetrosheetファイルの展開に関するところ。テキストで言うとP.299、付録Aに記載の箇所です。

 以下、データと問題の説明なので、とっとと対策に行けという方は「解決」の章へどうぞ。

 得点期待値の計算に必要不可欠なPlay-by-Play (打席ごとの)データ。Retrosheet では、1915年以降(!)の全試合のデータとロスター(出場選手)の情報が公開されています。各打席の結果はもちろん、投球間の牽制の有無や盗塁の成功・失敗に伴う状況変化も全て記録されているので、PitchValueなど、投球・球種や打球の質毎の情報が必要なこと以外はだいたい出来てしまうすごいファイルです。テキスト5章、6章の分析はそのほとんどがこのPlay-by-Playファイルの存在を前提に行われています。Baseball Savantで公開されているPitch-by-Pitchデータでも代替しきれないといえば、その詳細さが伝わることでしょう。

 で、問題はこのデータがかなり特殊な形式で保存・公開されていること。Data downloadsタブからPlay-by-Playファイルを含むzipファイルがダウンロードできるのですが、中身は大量のカンマ区切りのテキストデータ。csvのように行列が揃った形で格納されているわけではなく、またデータがホームチームの球団ごとに細切れになっているので手作業で使える状態にクリーニングするのは相当骨が折れます。というか無理です。

parse_retrosheet_pbp関数のエラー

 これをまとめて行うのがテキストの補論A.1.で紹介されているparse_retrosheet_pbp関数。chadwickと呼ばれる専用のソフトウェアを利用して、

  1. Retrosheetから任意のシーズンのevent filesデータが入ったzipファイルをダウンロード

  2. zipファイルを展開

  3. 展開したファイルを元にPlay-byPlayデータを作成

  4. 同じくそのシーズンのロスターを作成

  5. 3,4で作成したcsvを残して不要なファイルを消去する

以上の操作をRの中で完結させることができる関数です。これを利用することで、先に述べたようなファイルをワンタッチで解凍、たのしい得点期待値計算に進むことができる、はずでした。

 ところが、これを実行するとなんかエラーが出て上手くいかない。Chadwickソフトウェアからcwevent.exeファイルを取得し、所定のフォルダ(unzipped)に保存してテキスト通りの関数を実行しても途中で実行が止まってしまうという問題が各地で起こりました。セイバーメトリクスに入門して諦めた皆さんのほとんどがここで躓いたと言っているような気がします。

 関数をバラして1つずつ実行してみると、どうやら悪さをしているのは③の段階、create_csv_file()関数であることが分かります。

create_csv_file <- function (season) {
# 前略
cmd <- paste0("cwevent -y ", season, " -f 0-96 ",
              season, "*.EV*", " > all", season, ".csv")
if (.Platform$OS.type == "unix"){
   system (cmd)
 } else {
 shell(cmd)
 }
}

 ファイル操作系の関数はなんだかよく分かんなくて面食らっちゃいますよね。ぼくもあやうくマイメロになるところでした。詳しい解説が書籍に記載されていなかったので少しだけ解説すると、

  1. paste0()というのは文字列を生成する関数です。今 season という関数内のオブジェクトに展開したい年度が数値で入っている(関数を実行する時に、例えば"create_csv_file(2005)"などの形でシーズンを指定している)状態なので、要は「指定されたシーズンに値を差し替えて所定の文字列を作る」という命令になります。"cwevent~"で始まるメッセージをそのシーズンに対応した形で生成し、それをcmdという文字に代入しているということですね。たとえばseason = 2018を指定すると、
    cwevent -y 2018 -f 0-96  2018 *.EV* > all2018.csv
    という文字列になるわけですね。

  2. で、このcmdに割り振った文字が何かというと、「ターミナルで実行するコマンド」となります。ターミナルっていうのはアプリを経由せずPCに直接命令を書いて操作や設定を実行させるやつですね。フォルダに保存したcwevent.exeファイルを起動・実行して下さいという命令です。"-y~"以降の文字列はその実行に係るオプションを指定していて、「-y 2018」で「2018年のデータを処理して下さい」という内容になります。「-f 0-96」は「0列目から96列目までの項目を格納して保存して下さい」、「2018*.EV* > all2018.csv」は「"2018(任意の文字列).EV(任意の文字列)"を処理して出来上がったものを"all2018.csv"という名前のファイルに保存して下さい」という命令になります。zipファイルの中身は"年度・球団名.EVA"もしくは"年度・球団名.EVN"という名前なので、↑のように指定すれば全てのファイルが該当してくるわけですね。

  3. 最後のshell()、system()はそれぞれ①で生成した命令文を実際にPCに実行させるコードです。OSによって書き方が異なるので条件式で分岐させて、各に適したものを実行させています。

 コーディングのミスなどR内部の問題ならgoogleしてみれば大体解決策が提示されていますが、知らないソフトウェアについてのコマンドのエラーとなるとちょっと簡単には解決できそうにないですよね。困った。

原因

 できね~っつってネットの海を泳いでいたらありました。自分が直面した問題はだいたい過去の偉人が既に通った道であるわけですよ。

Englishに抵抗がない方はこちらの記事のQ&Aを呼んでもらえれば一発で解決です。そうでない方はこのまま続きを読んで下さい。

 このエラーの原因はズバリ、chadwickのバージョンにあります。原著は発行が2018年なので、邦訳版の発行までに新しいバージョンのcwevent.exeファイルが発行され、これがテキスト通りのコマンドでエラーを吐く仕様に変更してしまったことが諸悪の根源だったようです。ちなみに最新版は0.7.2で、テキスト通りに動く最も新しいバージョンは0.6.5です。

解決:chadwick.exeのダウングレード

 原因が分かれば後は解決するだけ。以下のリンクからバージョン0.6.5のchadwickファイルをダウンロードし、cweventファイルをご自身がダウンロードした最新版からそちらに置き換えて下さい。これでcreate_csv_file()関数を実行してもらえばうまく行くと思います。

 上手くいった方へ:おめでとうございます。快適なSABRmetrics Lifeを。

 なんで最新版で動かなくなったのかについてはあまりちゃんと調べていないのですが、chadwickのcwevent documentationをナナメ読みした所、どうやら最新版では「-f」のオプションが削除されているのが原因のようです。ここを適当な書式に変えてあげれば0.7.2でもちゃんと動かせると思いますし、バージョンを新しくしている以上は何かしらそれにメリットがあると思うのですが、今のところ0.6.5で困ったことがないので放置してます。妥協、だいじ。

その他のエラー

 いや、chadwickはうまく行ったんだけど問題はそこじゃねえぞという皆さんへ

 そこじゃないとしたらぼくは正直わかんないです。著者でも訳者でもないぱんぴーなので勘弁して下さい。幸い技術評論社さんが書籍のコードを実行するためのサポートサイトを用意して下さっているので、そちらを参考にされるとよろしいかと思います。以下、それ以外で考えられるエラーの原因を思いつくだけ書いてみます。

  • Rのコーディングミス:まあこれが一番多いです。クオーテーションが必要なところで入れてないとか、関数のスペルミスなど。

  • 実行したい作業の前段階で必要な作業の抜け:library()関数によるパッケージの起動やデータフレームの読み込みなど、事前に行うべき作業が上手くいっていない場合。スクリプトは必ず一番上から実行しましょう。

  • R、tidyverseのバージョン違い:Rのバージョンが3.以前の方はアップデートしましょう。特にtidyverseパッケージは大幅アップデートで関数の仕様が変更されたため、4.0.0以降のRでないと最新版が動きません。2022年4月時点では4.2.0が公開されているので、できればR、パッケージの両方を最新版にアップデートしておくことをおすすめします。

  • MLBAM Gameday, Pitch f/xデータのダウンロードができない:もう一つの大きな問題は補論BのPitch f/xデータの利用。こちらは実は2年ほど前から更新がストップしていて、現在はデータが利用できない状態です。これは恐らくMLB公式のトラッキングデバイスがTrackmanからHawkEyeに変更されたことが原因で、同じカメラでのトラッキングを行うPitch f/xは完全に廃止されてしまったのではないかと推測されます。6章、7章のフレーミング評価なんかはこちらの情報を前提にコーディングされているので一見困った問題ですが、基本的にはBaseball Savantの一球データを利用すれば代替可能かと思います。Pitch f/xデータにおけるPitch Location(投球の通過位置)データが"plx", "plz"列に格納されているのに対し、Savantデータのそれがそれぞれ"plate_x", "plate_z"に入っているなど、データの差分を埋めるようにコードを書き直せばうまく行くはずです。

まあ困ったらTwitterにぶん投げましょう。結構助けてもらえます。ぼくのRの技術は半分以上Twitterが育てたと思っています。

おまけ

 しょっぱなのしょうもない文章はおまけじゃないのかと言われると胸が痛くなりますよね。あれを書いて最後に好きな音楽を貼らないとやってられないので許して下さい。

 この前初めてスピッツを生で観てきました。ベースが(物理的にも)めちゃくちゃ動いててやっぱたまにはライブの音響で聴かないとダメだなと思いました。Saucy Dog、奥田民生との3マンライブだったんですが、ぼくが尊敬してやまない民生さんは相変わらずライブ中に酒を飲みながら好き勝手をやっていました。あの人こっちが声を出せないのを分かっててコール&レスポンスを振ってきますからね。

 スピッツの春の歌といえば『春の歌』ですよね。藤原さくらさんのカバーも好きです。YouTubeに公開されていなかったので貼るのは断念したんですが、ライブで一番気に入ったのは『8823』ですね。


この記事が参加している募集

野球が好き

貨幣の雨に打たれたい