見出し画像

【MLB】世界一きれいな変化量チャートの書き方講座!

 いよいよ球春も爛漫ですね。皆さんそれぞれにシーズンが楽しみに、あるいは不安になってくる頃かと思います。今回は、「RによるMLBデータ分析のチュートリアル」シリーズ第3弾として、MLBのシーズンを一味違ったやり方で楽しむために皆さんが好きな選手のデータを図にする方法を書いていきます。

 今回の記事は先にも書いたようにシリーズ第3弾で、第1弾、第2弾をお読みになってからお読みいただけるとより理解しやすい内容となっています。ぜひそちらも合わせてお読みください。

 おっと!こんなところにちょうどよく当該記事へのリンクが……!

 さて、茶番劇はこの辺にして、そろそろ本題に入っていきましょう。今回皆さんと一緒に作るのはこちら。

 ダルビッシュ有投手の2023年の投球データを基に作成した変化量チャートです。とても色とりどりできれいですね。おそらく昨年のMLBで最も多くの球種を投げた選手だと考えて間違いないでしょう。

 この後は早速作り方を見ていきます。いくつかステップがありますが、一つ一つ丁寧に説明していけたらなと思います。わからないことがあればtwitterでもコメントでも質問箱でも構いませんので遠慮なく聞いてくださいね。

用意するもの

 まずは必要な環境からです。前回までの記事をすべて読んでその通りに実行された方は、お手元のパソコンにすべてあるかと思います。気を付けてはいますが、その通りにやったのになかったらごめんなさい。

  • R

  • テキストエディタ(RStudioがおすすめ)

  • MySQLサーバー(Statcastデータを格納したテーブルがあるデータベース)

 これだけはないと本当に始まりません。Rは嫌だという方は齋藤周さんの本辺りを読んでPythonを勉強してください。エディタとしてVSCode辺りを使ってもよいのですが、私はVSCodeを扱うのが苦手なのでサポートはしません。

 最後のデータベースについては、ない場合の代替手段を本章末尾に補足として記載するのでそちらを読んでから続けて頂けたらと思います。

 続いて、必要なRパッケージたち。たぶん全部最初の記事でインストールしているでしょう。してなかったらinstall.packages()してください。

  • tidyverse

  • RMySQL

  • extrafont

  • (今回はオマケ)baseballr

 これらが用意できていれば準備はOKです。張り切ってグラフ作成と行きましょう!

(補足)データを取得する

 データを取得する際はtidyverseパッケージbaseballrパッケージを利用します。やることは先に挙げた2本目の記事とそう変わらないので、ぜひ参考にしてください。なお、情報取得関数がscrape_statcast_savant()からstatcast_search()に変わりました。名前が変わっただけらしいので気にしないで行きましょう。

library(baseballr)
library(tidyverse) 
#データを取得する
statcast_search(start_date = "2023-4-4",end_date = "2023-9-1",playerid = 506433,player_type = "pitcher")->yu

#単位などを加工する
yu %>% 
  mutate(sz_top=12*sz_top,sz_bot=12*sz_bot,pfx_x=12*pfx_x,pfx_z=12*pfx_z,vx0=12*vx0,vy0=12*vy0,vz0=12*vz0,ax=12*ax,ay=12*ay,az=12*az,plate_x=12*plate_x,plate_z=12*plate_z,barrel=case_when(launch_speed_angle==6~1,launch_speed_angle>=1|launch_speed_angle<=5~0),PA_ID = paste(game_pk,at_bat_number,sep = "_")) %>% 
  mutate(pitch_ID=paste(PA_ID,pitch_number,sep = "_"),swing_flag=ifelse(description=="foul" |description=="swinging_strike" | description=="foul_tip "|description=="hit_into_play"|description=="swinging_strike_blocked",1,0),zone_flag=ifelse(plate_z-sz_bot>=0&sz_top-plate_z>=0&plate_x<=10&plate_x>=-10,1,0),contact_flag=ifelse(description=="foul"|description=="hit_into_play",1,0),game_month=str_split(game_date,"-",simplify = TRUE)[,2] %>% as.double(),num_hor=case_when(abs(plate_x)<6.7~1,abs(plate_x)>=6.7&abs(plate_x)<13.3~2,abs(plate_x)>=13.3&abs(plate_x)<20~3,abs(plate_x)>=20~4),num_var=case_when(abs(plate_z-30)<8~1,abs(plate_z-30)>=8&abs(plate_z-30)<16~2,abs(plate_z-30)>=16&abs(plate_z-30)<24~3,abs(plate_z-30)>=24~4)) %>% 
  mutate(attack_region=if_else(num_hor>num_var,num_hor,num_var))->yu
#必要な列に絞る
usecol<-c("game_year","game_date","game_month","game_type","pitch_type","release_speed","release_pos_x","release_pos_z","batter","pitcher","events","description","zone","des","stand","p_throws","home_team","away_team","type","hit_location","bb_type","balls","strikes","pfx_x","pfx_z","plate_x","plate_z","on_3b","on_2b","on_1b","outs_when_up","inning","inning_topbot","hc_x","hc_y","vx0","vy0","vz0","ax","ay","az","sz_top","sz_bot","hit_distance_sc","launch_speed","launch_angle","effective_speed","release_spin_rate","release_extension","game_pk","fielder_2","fielder_3","fielder_4","fielder_5","fielder_6","fielder_7","fielder_8","fielder_9","release_pos_y","estimated_ba_using_speedangle","estimated_woba_using_speedangle","woba_value","woba_denom","babip_value","iso_value","launch_speed_angle","at_bat_number","pitch_number","pitch_name","home_score","away_score","bat_score","fld_score","post_away_score","post_home_score","post_bat_score","post_fld_score","if_fielding_alignment","of_fielding_alignment","spin_axis","delta_home_win_exp","delta_run_exp","barrel","PA_ID","pitch_ID","swing_flag","zone_flag","contact_flag","num_hor","num_var","attack_region")
as.data.frame(yu) %>% 
  select(all_of(usecol))->yu
#CSVファイルとして書き出して保存
yu %>% write_csv("Yu.csv")

 これで、ダルビッシュ選手の2023年レギュラーシーズンの投球データを取得して単位を加工して保存できました。データを読み込むときは以下のようにしてください。

read_csv("Yu.csv")->yu

やってみよう!

 実践パートです。丁寧に書くつもりなので丁寧に読んでくださいね。「データの読み込み」までのパートの内容は、RStudioを開く際に毎回行う必要があります。

パッケージの読み込み

 RStudioを立ち上げたら、まず以下のコードを実行しましょう。

library(tidyverse)
library(RMySQL)
library(extrafont)
library(baseballr)

 今回図を書くために必要なパッケージを読み込みました。

conn <- dbConnect(MySQL(),dbname="bbdata",user="root",password="password")

 データベースへの接続を作っておきましょう。dbname変数は皆さんそれぞれが設定したものに合わせてください。これで準備は完了です。

データの読み込み

 今回はMySQLのデータベースからデータを読み込むので、保存したStatcastデータのテーブルからデータを絞り込んで取り込みます。せっかくなので、ダルビッシュ選手の選手IDをbaseballrの関数を使って検索してみましょう。

playerid_lookup(last_name="Darvish",first_name="Yu")

 このコードを実行すると、ネット接続があれば「trying URL~~」みたいなメッセージが何個か出るので信じて待ちましょう。するとこんなものが出てくると思います。出てこなくてもエラーが出ていなければたぶんあります。

 使う時にこれを見ながら書き写してもいいのですが、確実に正確に入力するためにコード化しちゃいましょう。

playerid_lookup(last_name="Darvish",first_name="Yu") %>% #選手情報取得
  pull(mlbam_id) ->yu_id #必要な列(MLB公式のID)だけ取り出し

 こうすることで、絶対に取得するデータを間違えない最強の布陣が出来上がります。それでは万全の備えでデータを読み込みましょう。

dar<-tbl(src=conn,"statcast") %>% 
  filter(pitcher==yu_id & game_year==2023 & game_type=="R") %>% #IDは506433と直打ちしてもよい
  collect()

 一行目は私もよくわかっていないのですが、statcastというテーブル(お手元の環境に合わせて書き換えてください)からデータを取ってくるよ、という構えをとるやつだと思ってください。こればっかりはソースコードを読んでも何もわからなかったのでこれ以上のことは書けません。

 二行目は、テーブルのデータから必要なものだけを絞って取り出すようにするコードです。

 pitcher列が先ほど取得したIDと一致するものに絞り込んでダルビッシュ選手の投球のみを、game_yearで年度を、game_typeでレギュラーシーズンの投球に限定しています。

 そして3行目でデータをメモリに読み込みます。この時、必要なデータをすべて保存できていれば2218行の表データが得られます。これで使用データが用意できました。

図の作成①~普通にやってみる~

 さて、いよいよ皆さんお待ちかねの、図を描画するパートです。とは言ってもすぐには完成品は作りません。目次を再掲するので、あまりおすすめはしませんが完成品を作るパートまで飛びたい人はそうしてください。

 なぜいちいち細々と順を追って書いていくかというと、コードの各部分がどうなっているかについて皆さんにちゃんと理解してほしいからです。その方が応用が利きますからね。

 今回はggplot2パッケージを用いて描画を行います。このパッケージはtidyverseのパッケージ群の一部で、tidyverseパッケージを読み込んだときに自動的に読み込まれるうちの一つです。よって、既に前パートでtidyverseパッケージを読み込んだ方は改めてggplot2を読み込む必要はありません

 まずは単に図を書いてみましょう。

ggplot(data=dar,
       mapping = aes(x=pfx_x,y=pfx_z))+
  geom_point()

 ggplot()キャンバスを作っているイメージです。その中の引数のdataで描画するデータを、mappingで縦軸と横軸にとる変数を指定しています。今回は変化量チャートなので、横軸に水平方向、縦軸に鉛直方向の重力を取り除いた変化量を書きました。

 geom_point()は、ggplot()で指定された条件で散布図を描く関数です。ggplot()の中身を空っぽにしてここで諸変数を指定するやり方もありますが、それはまたの機会に扱うということにしましょう。

 そうして作った画像がこちら。

 コードの面では最もオーソドックスにして最も美しくないグラフの完成です。正直、これが何か役に立つとは思えません。シンプルイズベストなんてまやかしです。ここからどう改善していくか見ものですね。

図の作成②~球種ごとに色分け~

 前項で作ったグラフは改善点だらけですが、まずは真っ黒で気が滅入りそうな色使いから変えていきましょう

ggplot(data=dar,
       mapping = aes(x=pfx_x,y=pfx_z,
                     color=pitch_type))+
  geom_point()

 3行目を見てください。先ほど軸を指定したaes()の中に、「color=pitch_type」と書きました。球種によって点の色が変わるようになっています。その結果がこちら。

 かなり気分が変わりました。時間がなければこれで完成でもまあいいのかもしれないくらいです。ただ、当然ながらまだ完成ではありません。このグラフには重大な欠陥があります。なんでしょうか。考えてみてください。

 そう。グラフの縦横比が一定ではないのです。これでは正確とは言えません。ここまでで掲載した画像を見るとなにがいけないのかわからないという人のために極端な例を出しておきましょう。

 この画像は同じコードを、画像を保存するときに縦横比が1:6になるようにして書き出したものです。ダルビッシュ選手はこんなに横に動く球ばかり投げているんでしょうか?もちろんそんなことはありません。しかしこれでは直観的にはそう見えてしまいます。

 この問題を次項で解決します。

図の作成③~縦横比を一定に~

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()

 この最終行に追加したcoord_fixed()関数で縦横比を数字通りにします。ここではpfx_x(横変化量)とpfx_z(縦変化量)の単位が同じなので、実際の変化方向を念頭に置いたグラフが作れるというわけです。

 これでかなり見やすくなったと思います。これで本当に第1の完成図です。しかし、まだいくつか問題があります。たとえば凡例が全然何が何やらわからないことなど。

図の作成④~端の値を設定~

 ggplot2は描画するデータに合わせて表示する上限と下限の値を自動で決めてくれます。便利なのですが、違う選手の図を比べるときに不便です。そこで、これを手動で設定します。これにはxlim()ylim()の二つを使用します。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)

 具体的な数値は、だいたいこれくらいならほぼ全投球を違和感なくカバーできるという範囲で適当に決めています。今のところ問題はありません。これで、異なる年度や選手のグラフを安心して見比べられるようになりました。

休憩

 疲れたので少し休憩を取りましょう。

作業再開

図の作成⑤~凡例をわかりやすく~

 凡例が今のままでは何の球種が何やらわからないので、カタカナ表記に直します

 まずは、現在MLBでどのような球種区分があるのか見てみましょう。baseballrmlb_pitch_types()関数で取得したデータを、print()関数ですべての行を表示するようにして実行します。

print(
    mlb_pitch_types(),
    n=24)

 すると以下のような結果が返ってきます。

 24個のうち球種と呼べるのは上から18個。このうちジャイロボールと、すべてシンカーとして判定されるようになったツーシームはないものとして大丈夫です。

 そういうわけで、これを利用して凡例のタイトルと内容を編集しましょう。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",
      FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
      GY="ジャイロボール",
      KC="ナックルカーブ",KN="ナックル",
      SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
      PO=""))

 scale_color_hue()関数を追加しました。scale_~~という関数は、図中の色や形などの要素やその凡例の操作に使うもので、その中でもscale_color_~~というのは散布図の点の色や棒グラフなどの縁の線の色などについての関数です。詳しく知りたい方はこちらの記事が大いに参考になりそうです。

 nameで凡例の題名を、labelsにベクトル(1列の表データのようなもの)を作るc()関数で球種名を書いています。

図の作成⑥~軸タイトルも変える~

 さて、凡例を日本語に変えたならついでに軸も変えちゃいましょう。pfx_xとpfx_zではよくわかりません。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
                  labels=c(
                    CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
                    EP="スローボール",
                    FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
                    GY="ジャイロボール",
                    KC="ナックルカーブ",KN="ナックル",
                    SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
                    PO=""))+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")

 xlab()ylab()の2つの関数で、軸ラベルを変えました。これで徐々にぱっと見で意味がわかるグラフになってきたような気がします。

 いい感じになってきたグラフですが、絶望的にダサい部分があると思いませんか?次項でそれを直しましょう。

図の作成⑦~日本語部分のフォントを変える~

 次は、ミミズが這っているような日本語フォントをまともな姿にしてやりましょう。extrafontパッケージを読み込んでおいたことが、ここで役に立ってきます。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
                  labels=c(
                    CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
                    EP="スローボール",
                    FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
                    GY="ジャイロボール",
                    KC="ナックルカーブ",KN="ナックル",
                    SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
                    PO=""
                  ))+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  theme(text = element_text(family = "Yu Mincho"))

 theme()関数はggplotの図の体裁を作るための関数です。今回のような使い方のほかにもいろいろできることはあるのですが、立ち入ると長くなるのであまり詳しいことは扱いません。

 text変数で図中に登場する文字を全部游明朝にしました。かなり仕上がってきましたね!現時点で十分実用レベルです。

 ちなみに、フォントを変えたいときは以下のように書きます。たとえば、「球種」の文字のところを大きくしたいときは、その部分は凡例のタイトルなので、legend.titleという引数を指定します。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
                  labels=c(
                    CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
                    EP="スローボール",
                    FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
                    GY="ジャイロボール",
                    KC="ナックルカーブ",KN="ナックル",
                    SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
                  PO=""))+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  theme(text = element_text(family = "Yu Mincho"),
        legend.title = element_text(size = 16))

 最終行で、凡例のタイトルのフォントサイズを16にしました。

でっか

 こんな感じで自在に指定できるので、いろいろ遊んでみましょう。

休憩

 疲れたので休憩を取りましょう。別に私が指示したタイミングでないと休憩してはいけないということはないのですが、作業のまとまりの観点から考えるとこのタイミングで休むのがキリが良いのでいいと思います。

作業再開

図の作成⑧~補助線を引く~

 参考になる数値を示す線が何もないのではわかりにくいので、横変化0と縦変化が重力以外ないところのラインに線を引きます。これには、縦線を引くgeom_vline()と横線を引くgeom_hline()を使います。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
                  labels=c(
                    CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
                    EP="スローボール",
                    FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
                    GY="ジャイロボール",
                    KC="ナックルカーブ",KN="ナックル",
                    SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
                    PO=""))+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  theme(text = element_text(family = "Yu Mincho"))+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)

 両関数の中のyinterceptxinterceptは、「どこの数値のところに線を引く」ということを指定します。今回は両方とも0のところに線を引きたいのでこれで大丈夫です。

 実行するとこんな感じ。

 ちなみに、線の種類を変えたいときにはlinetype=のところの値を変更するといいです。私は2を好んで使っていますが、下図の上から1、2、という感じで6まで並べたので、好きなものを選んで使ってください

図の作成⑨~背景を変える~

 さて、線を引いたら灰色の背景と上手く馴染んでいない感じがしてなんとなく気持ち悪いですね。背景を白くしましょう。背景を変更するにはtheme_**系列の関数を使います。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  scale_color_hue(name = "球種",
                  labels=c(
                    CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
                    EP="スローボール",
                    FA="ファストボール",FC="カッター",FF="ストレート",FO="フォーク",FS="スプリッター",FT="ツーシーム",
                    GY="ジャイロボール",
                    KC="ナックルカーブ",KN="ナックル",
                    SC="スクリュー",SI="シンカー",ST="スイーパー",SL="スライダー",SV="スラーブ",
                    PO=""))+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal(base_family="Yu Mincho")+

 今回はtheme_minimal()を使いました。注意点としては、もしtheme()を使って何か指定している場合には、theme_**系の関数はtheme()の前に書かないとtheme()で書いたことが無意味になってしまうので必ず順番に気を付けるということですね。

 今回は、theme_minimal()内のbase_family引数で基本のフォントファミリーを変更できるのでそれを使ってフォントを指定しました。これで今回の記事内でtheme()とtheme_minimal()の順番に迷う必要はもうありません。

 ほかの背景を使いたい人は以下の記事を参考にしてくださいね。

不要なデータを削除する

 ここまで凡例に空欄がありました。これはピッチアウト(いわゆるウエスト?)があったことによるものですが、正直いらないのでデータから排除しましょう。

dar<-dar %>% 
  filter(pitch_type!="UN"&pitch_type!="IN"&pitch_type!="PO"&pitch_type!="AB"&pitch_type!="AS"&pitch_type!="NP")

 !=はノットイコールの意味です。先ほどmlb_pitch_types()で得た返り値の下から6つに当てはまらないものだけを取り出すために、それらを記述し「かつ」の記号で結びました。

休憩

 次が最後のステップなのですが、その前に。最後がまともにやろうとするとやや大変なので、しっかり休養してからにしてくださいね。私もこの記事の残りを書くのは一旦お風呂に入ってからにしたいと思います。ついでに最近お気に入りのチルな曲のライブ映像を置いておくのでそれでも聴いておいてください。ちゃんと時間指定URLにしておきましたから、ぜひ。

図の作成⑩~公式準拠配色にする~

 お風呂から上がりました。ついに最終ステップです。いくつかやることがあって大変なパートでもあります。

球種記号をリストアップする

 まずは、先ほどから再三登場するmlb_pitch_types()に出てくる記号を、先ほど取り除いたもの以外すべて並べます。ここでは順番が大事です。私はだいたい辞書順にしているので、どの色がどの記号と対応するかを注意しながら並べ替え作業をしたい人以外は同じように辞書順に並べてください。

lv<-c("CH","CS","CU","EP","FA","FC","FF","FO","FS","FT","GY","KC","KN","SC","SI","SL","ST","SV")

 次に、これをfactor型にします。factor型はベクトルに似ていますが、順番(階級)があるものです。よくわかっていないのでこれ以上は言いませんが、そういう感じです。

factor(character(),
       levels=lv)->fact_p

 この作業をすっ飛ばすと、あなたは図を描くたびに毎回各球種が扱うデータに含まれているかに注意を払わねばならなくなります。サボるのをやめましょう

色のリストを用意する

 次は使う色のカラーコードのリストです。私ががんばってカラーピッカーで集めたりカラーサイト.comを使って適当に作ったりしましたので、皆さんはコピペすればOKです。

colscale<-c("#1dbe3a",
            "#0068ff",
            "#00d1ed",
            "#888888",
            "#888888",
            "#933f2c",
            "#d22d49",
            "#55ccab",
            "#3bacac",
            "#ffd899",
            "#888888",
            "#6236cd",
            "#3c44cd",
            "#60db33",
            "#fe9d00",
            "#c3bd0e",
            "#f172a3",
            "#93afd4"
            )

 このうち、ツーシームとスイーパーとジャイロボールは私が捏造しました。ツーシームとジャイロボールは先に言った通り観測事例がなく、スイーパーは公式配色だと見た目がスライダーに近すぎるのが理由です。スイーパーの公式配色は"#ddb33a"ですので、公式配色にしたい人は"#f172a3"と入れ替えてください。

色と順番を対応させる

names(colscale)<-levels(fact_p)

 このコードで、球種記号の順番と色の順番を対応させています。この部分はこの記事の執筆中に学んだことなので詳しい人はぜひ詳しいことを教えてください。

球種データを加工する

 描画するデータの方でも球種の列をfactor型にしておかねばなりません。

dar<- dar %>% 
  mutate(
    pitch_type=factor(
      pitch_type,
      levels=lv)) 

 データの準備ができたので、次はついに図を書きます。

実際に描画する

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal(base_family="Yu Mincho")+
  scale_color_manual(
    values = colscale,
    name="球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",FA="ファストボール",FF="ストレート",FO="フォーク",
      SI="シンカー",ST="スイーパー",
      KC="ナックルカーブ",KN="ナックル",FC="カッター",FS="スプリッター",SC="スクリュー",SL="スライダー",SV="スラーブ"
    ))

 前の描画コードとの相違点として、まずscale_color_hue()の代わりにscale_color_manual()を使っています。hueの方はデフォルト配色専用なので、自作のカラーパレットで配色をするならmanualの方に書き換えねばなりません

 Values引数で色を指定している以外はhue時代と同じですね。これでついにほぼ公式準拠の色使いの鮮やかなグラフが出来上がります。

何度見ても色数が多いですね

保存する①~RStudioの場合~

 グラフが完成したので保存しましょう。RStudioで作業されている方は、例えば以下のような画面になっているはずです。

 ここで「Plots」のところにある「Export」のボタンを押して「Save as Image」を選択すると、以下のような画面が出てきます。

 Image formatで保存するファイル形式を、File nameで保存するファイル名を、Widthで横幅のピクセル数を、Heightで縦幅のピクセル数を設定します。この時、Maintain aspect ratioのチェックボックスにチェックを入れておくと、WidthやHeightを書き換えても縦横比が変わりません。

 お好みの内容・数値にしたらSaveボタンを押して、保存完了です。私は660×440でいつも保存しています。

保存する②~どんなプラットフォームでも~

 RStudioをお使いでない方でもいけるように今回は別のやり方も紹介しておきます。ここでは、ggplot2の保存用の関数であるggsave()を使います。ただ、私は普段使わないのであまり慣れていない方法ですし、個人的には扱いづらいと感じているところですので、詳しくは以下のウェブサイトをご覧いただくなどしてください。

ENJOY!

 この後おまけが続きますが、本編はここまでです。最後に、最初から最後までの通しのコードを貼っておきます。次回は、今回の図にアニメーションを加えたり、投球割合の棒グラフと並べたりする予定ですので楽しみにしてお待ちいただけると嬉しいです。

library(tidyverse)
library(RMySQL)
library(extrafont)
library(baseballr)
 
playerid_lookup("Darvish","Yu") %>% 
  pull(mlbam_id) ->yu_id

conn <- dbConnect(MySQL(),dbname="bbdata",user="root",password="password")
dar<-tbl(conn,"statcast") %>% 
  filter(pitcher==yu_id&game_year==2023&game_type=="R") %>%
  collect()
 
lv<-c("CH","CS","CU","EP","FA","FC","FF","FO","FS","FT","GY","KC","KN","SC","SI","SL","ST","SV")

factor(character(),
       levels=lv)->fact_p

colscale<-c("#1dbe3a",
            "#0068ff",
            "#00d1ed",
            "#888888",
            "#888888",
            "#933f2c",
            "#d22d49",
            "#55ccab",
            "#3bacac",
            "#ffd899",
            "#888888",
            "#6236cd",
            "#3c44cd",
            "#60db33",
            "#fe9d00",
            "#c3bd0e",
            "#f172a3",
            "#93afd4"
            )



names(colscale)<-levels(fact_p)

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal()+
  scale_color_manual(
    values = colscale,
    name="球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",FA="ファストボール",FF="ストレート",FO="フォーク",
      SI="シンカー",ST="スイーパー",
      KC="ナックルカーブ",KN="ナックル",FC="カッター",FS="スプリッター",SC="スクリュー",SL="スライダー",SV="スラーブ"
    ))

 長々と作業お疲れ様でした。ご質問等ございましたらTwitter質問箱にお願いします。この記事がいいなと思っていただけた方はスキフォローTwitterのフォローもよろしくお願いします。


おまけ

凡例の消し方

 今回は球種数が多いので凡例が必要でしたが、気分的に別にいらないとか、見た目のバランスを考えると載せたくないとか、そういう事情で凡例を消したいことがあるかもしれません。そういうときには、guides()関数を指定します。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal(base_family = "Yu Mincho")+
  scale_color_manual(
    values = colscale,
    name="球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",FA="ファストボール",FF="ストレート",FO="フォーク",
      SI="シンカー",ST="スイーパー",
      KC="ナックルカーブ",KN="ナックル",FC="カッター",FS="スプリッター",SC="スクリュー",SL="スライダー",SV="スラーブ"
    ))+
  guides(color="none")

 消したい凡例は色のものだけなので、ほかに凡例で示すべき変量があったときにはそれは残ります。すべての凡例を消したいときには、先ほど一旦用済みにしたtheme()を使います。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal(base_family = "Yu Mincho")+
  scale_color_manual(
    values = colscale,
    name="球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",FA="ファストボール",FF="ストレート",FO="フォーク",
      SI="シンカー",ST="スイーパー",
      KC="ナックルカーブ",KN="ナックル",FC="カッター",FS="スプリッター",SC="スクリュー",SL="スライダー",SV="スラーブ"
    ))+
  theme(legend.position  = "none")

 theme()の内部でlegend.position="none"として、凡例を表示しないようにしました。いずれの操作でも以下のような画像ができます。

 ついでに、軸ラベルも消してみましょう。

ggplot(data=dar,
       mapping = aes(pfx_x,pfx_z,
                     color=pitch_type))+
  geom_point()+
  coord_fixed()+
  xlim(-35,35)+ylim(-31,31)+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  theme_minimal(base_family = "Yu Mincho")+
  scale_color_manual(
    values = colscale,
    name="球種",
    labels=c(
      CH="チェンジアップ",CS="スローカーブ",CU="カーブ",
      EP="スローボール",FA="ファストボール",FF="ストレート",FO="フォーク",
      SI="シンカー",ST="スイーパー",
      KC="ナックルカーブ",KN="ナックル",FC="カッター",FS="スプリッター",SC="スクリュー",SL="スライダー",SV="スラーブ"
    ))+
  theme(legend.position  = "none",
        axis.title = element_blank())

 theme()内で軸ラベルの体裁を指定するaxis.title引数にelement_blank()を代入しました。これできれいさっぱりと軸ラベルがなくなります。

 同様に、element_blank()をaxis.text引数に代入して軸メモリの数字を、panel_grid引数に代入して背景の薄い罫線を消すことができます。

この記事のサムネイルの背景の作り方

サムネイル

 サムネイル画像の背景も、ご覧の通り今回ご紹介した作図の応用で作っています。Canvaで文字を入れる前の画像は、前田健太選手の2023年の投球データを利用しています。

 Noteのサムネイルのサイズが1280×670なので、今回は640×335で仕上げます。上でやったのと同じようにデータを読み込んで図にするのですが、ここでは縦横比を整えない、描画範囲の値をより絞る、色合いの観点からカーブを削るという作業をしています。また、色合いを淡くするために、geom_point()内でalpha(ggplotで不透明度を指定する、0~1の引数)を0.3に設定して不透明度を下げています。

m_id<-playerid_lookup("Maeda","Kenta") %>% pull(mlbam_id) #選手ID取得
maeda<-tbl(conn,"statcast") %>% #データの読み込み
  filter(pitcher==m_id&game_year==2023&game_type=="R") %>%
  collect()

maeda %>% 
  mutate(#ファクター化
    pitch_type=factor(
      pitch_type,
      levels=lv)) %>% 
  filter(pitch_type!="CU")%>% #カーブを削る
  ggplot(
    aes(pfx_x,pfx_z,color=pitch_type))+
  geom_point(alpha=0.3)+
  theme_minimal()+
  xlab("横方向変化量(インチ)")+ylab("縦方向変化量(インチ)")+
  scale_color_manual(
    values = colscale)+
  theme(
    axis.title = element_blank(),axis.text = element_blank(),
    legend.position = "none")+
  geom_vline(xintercept = 0,linetype=2)+geom_hline(yintercept = 0,linetype=2)+
  xlim(-22,22)+ylim(-15,25)

 このように、何かの変数に連動させて見え方を変えたいならaes()内で、描画するもの全体を同じにしたいならaes()の外で値を設定することになります。このコードを実行すると、以下のようになります。

 これで本当にこの記事は終わりです。ご質問やご感想はぜひTwitter質問箱にお寄せください。

※本記事内で登場した画像は、すべて筆者がBaseball Savantから取得したデータを基に作成したものです。サムネイルはCanvaで作成しました。


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

野球が好き

仕事について話そう

この記事が気に入ったらサポートをしてみませんか?