STATCAST で遊ぶ vol.5

前回は、[STATCAST で遊ぶ vol.4](https://qiita.com/RemixWorksLLC/items/a952d62f0c130e191a48)

## OPS を計算してみる

　OPS（On-base plus sluggin, 出塁率 + 長打率） は日本でもかなり浸透してきた指標で、かなり古い指標であるにもかかわらず、その有用性は今でも健在です。
　計算も簡単で、出塁率と長打率を単純に足すだけです。

 ```math
 OPS = OBP + SLG
 ```

　OBP は出塁率で、次の式で計算できます。

 ```math
 OBP = \frac{H + BB + HBP}{AB + BB + SF + HBP}
 ```

- H ... ヒット
- BB ... 四球
- HBP ... 死球
- AB ... 打数
- SF ... 犠飛

 SLG は長打率で、次の式で計算できます。

 ```math
 SLG = \frac{TB}{AB}
 ```

 - TB ... 塁打

 TB は、次の式で計算できます。

 ```math
 TB = Single \times 1 + Double \times 2 + Triple \times 3 + Home{-}run \times 4
 ```

- Single ... 単打
- Double ... 二塁打
- Triple ... 三塁打
- Home-run ... 本塁打

　計算用に新しいデータを STACAST から取得します。
 [2015年〜2024年STATCAST規定到達打者データ](https://baseballsavant.mlb.com/leaderboard/custom?year=2024%2C2023%2C2022%2C2021%2C2020%2C2019%2C2018%2C2017%2C2016%2C2015&type=batter&filter=&min=q&selections=player_age%2Cab%2Cpa%2Chit%2Csingle%2Cdouble%2Ctriple%2Chome_run%2Cstrikeout%2Cwalk%2Ck_percent%2Cbb_percent%2Cbatting_avg%2Cslg_percent%2Con_base_percent%2Con_base_plus_slg%2Cisolated_power%2Cbabip%2Cb_rbi%2Cb_lob%2Cb_total_bases%2Cr_total_caught_stealing%2Cr_total_stolen_base%2Cb_ab_scoring%2Cb_ball%2Cb_called_strike%2Cb_catcher_interf%2Cb_foul%2Cb_foul_tip%2Cb_game%2Cb_gnd_into_dp%2Cb_gnd_into_tp%2Cb_gnd_rule_double%2Cb_hit_by_pitch%2Cb_hit_ground%2Cb_hit_fly%2Cb_hit_into_play%2Cb_hit_line_drive%2Cb_hit_popup%2Cb_out_fly%2Cb_out_ground%2Cb_out_line_drive%2Cb_out_popup%2Cb_intent_ball%2Cb_intent_walk%2Cb_interference%2Cb_pinch_hit%2Cb_pinch_run%2Cb_pitchout%2Cb_played_dh%2Cb_sac_bunt%2Cb_sac_fly%2Cb_swinging_strike%2Cr_caught_stealing_2b%2Cr_caught_stealing_3b%2Cr_caught_stealing_home%2Cr_defensive_indiff%2Cr_interference%2Cr_pickoff_1b%2Cr_pickoff_2b%2Cr_pickoff_3b%2Cr_run%2Cr_stolen_base_2b%2Cr_stolen_base_3b%2Cr_stolen_base_home%2Cb_total_ball%2Cb_total_sacrifices%2Cb_total_strike%2Cb_total_swinging_strike%2Cb_total_pitches%2Cr_stolen_base_pct%2Cr_total_pickoff%2Cb_reached_on_error%2Cb_walkoff%2Cb_reached_on_int%2Cxba%2Cxslg%2Cwoba%2Cxwoba%2Cxobp%2Cxiso%2Cwobacon%2Cxwobacon%2Cbacon%2Cxbacon%2Cxbadiff%2Cxslgdiff%2Cwobadiff%2Cavg_swing_speed%2Cfast_swing_rate%2Cblasts_contact%2Cblasts_swing%2Csquared_up_contact%2Csquared_up_swing%2Cavg_swing_length%2Cswords%2Cexit_velocity_avg%2Claunch_angle_avg%2Csweet_spot_percent%2Cbarrel%2Cbarrel_batted_rate%2Csolidcontact_percent%2Cflareburner_percent%2Cpoorlyunder_percent%2Cpoorlytopped_percent%2Cpoorlyweak_percent%2Chard_hit_percent%2Cavg_best_speed%2Cavg_hyper_speed%2Cz_swing_percent%2Cz_swing_miss_percent%2Coz_swing_percent%2Coz_swing_miss_percent%2Coz_contact_percent%2Cout_zone_swing_miss%2Cout_zone_swing%2Cout_zone_percent%2Cout_zone%2Cmeatball_swing_percent%2Cmeatball_percent%2Cpitch_count_offspeed%2Cpitch_count_fastball%2Cpitch_count_breaking%2Cpitch_count%2Ciz_contact_percent%2Cin_zone_swing_miss%2Cin_zone_swing%2Cin_zone_percent%2Cin_zone%2Cedge_percent%2Cedge%2Cwhiff_percent%2Cswing_percent%2Cpull_percent%2Cstraightaway_percent%2Copposite_percent%2Cbatted_ball%2Cf_strike_percent%2Cgroundballs_percent%2Cgroundballs%2Cflyballs_percent%2Cflyballs%2Clinedrives_percent%2Clinedrives%2Cpopups_percent%2Cpopups%2Cpop_2b_sba_count%2Cpop_2b_sba%2Cpop_2b_sb%2Cpop_2b_cs%2Cpop_3b_sba_count%2Cpop_3b_sba%2Cpop_3b_sb%2Cpop_3b_cs%2Cexchange_2b_3b_sba%2Cmaxeff_arm_2b_3b_sba%2Cn_outs_above_average%2Cn_fieldout_5stars%2Cn_opp_5stars%2Cn_5star_percent%2Cn_fieldout_4stars%2Cn_opp_4stars%2Cn_4star_percent%2Cn_fieldout_3stars%2Cn_opp_3stars%2Cn_3star_percent%2Cn_fieldout_2stars%2Cn_opp_2stars%2Cn_2star_percent%2Cn_fieldout_1stars%2Cn_opp_1stars%2Cn_1star_percent%2Crel_league_reaction_distance%2Crel_league_burst_distance%2Crel_league_routing_distance%2Crel_league_bootup_distance%2Cf_bootup_distance%2Cn_bolts%2Chp_to_1b%2Csprint_speed&chart=false&x=player_age&y=player_age&r=no&chartType=beeswarm&sort=xwoba&sortDir=desc)

 CUSTOM COLUMUNS が 180 になっていることを確認して「Download CSV」でダウンロードします。ダウンロードした stats.csv を 2015-2024_batting_all.csv にリネームして、RStudio の作業ディレクトリに放り込みます。
　CSV を R に読み込んで、計算に必要なデータを揃えます。

```
player_batting_all <- read.csv("2015-2024_batting_all.csv") 

hit <- as.numeric(player_batting_all$hit) # 安打
walk <- as.numeric(player_batting_all$walk) # 四球
hit_by_pitch <- as.numeric(player_batting_all$b_hit_by_pitch) # 死球
ab <- as.numeric(player_batting_all$ab) # 打数
sac_fly <- as.numeric(player_batting_all$b_sac_fly) #犠飛
single <- as.numeric(player_batting_all$single) # 単打
double <- as.numeric(player_batting_all$double) # 二塁打
triple <- as.numeric(player_batting_all$triple) # 三塁打
home_run <- as.numeric(player_batting_all$home_run) # 本塁打
```

 それでは、OPS まで計算して、STATCAST に含まれる OPS と比べてみましょう。

```
obp = (hit + walk + hit_by_pitch) / (ab + walk + sac_fly + hit_by_pitch) # 出塁率
tb = single * 1 + double * 2 + triple * 3 + home_run * 4 # 塁打
slg = tb / ab # 長打率
ops = obp + slg # OPS

summary(ops)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.5390  0.7272  0.7817  0.7886  0.8391  1.1846 
```
```
summary(player_batting_all$on_base_plus_slg)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.5390  0.7270  0.7820  0.7886  0.8390  1.1850 
```

 小数点第３位以下に誤差はあるものの正しいようです。
 誤差が生じるのは、STATCAST の OPS などのデータは小数点以下第３位までを有効桁数としているためです。
 一応、他も確認してみましょう。

```
summary(tb)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   45.0   199.0   233.0   228.2   269.0   411.0 
```
```
summary(player_batting_all$b_total_bases)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   45.0   199.0   233.0   228.2   269.0   411.0 
```
```
summary(obp)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2383  0.3147  0.3344  0.3373  0.3581  0.4898 
```
```
summary(player_batting_all$on_base_percent)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2380  0.3150  0.3340  0.3373  0.3580  0.4900 
```
```
summary(slg)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2663  0.4068  0.4455  0.4513  0.4913  0.7013 
```
```
summary(player_batting_all$slg_percent)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2660  0.4070  0.4460  0.4513  0.4910  0.7010 
```

　誤差がありますが、これも正しいようです。

 ## 大谷翔平とアーロン・ジャッジのデータを抽出する

　player_id を使えば、ある選手のSTATCAST規定到達シーズンのデータが抽出できます。

 <table>
  <thead>
    <tr>
      <th>選手名</th>
      <th>last_name...first_name</th>
      <th>player_id</th>
    </tr>
  </thead>
  <tr>
    <td>大谷翔平</td>
    <td>Ohtani, Shohei</td>
    <td>660271</td>
  </tr>
  <tr>
    <td>アーロン・ジャッジ</td>
    <td>Judge, Aaron</td>
    <td>592450</td>
  </tr>
    <tr>
    <td>フアン・ソト</td>
    <td>Soto, Juan</td>
    <td>665742</td>
  </tr>
    <tr>
    <td>鈴木誠也</td>
    <td>Suzuki, Seiya</td>
    <td>673548</td>
  </tr>
</table>

　last_name...first_name でも同姓同名がいなければ抽出できますが、危険なので、player_id を使いましょう。

```
ohtani_shohei <- player_batting_all[player_batting_all$player_id == "660271",]
ohtani_shohei$on_base_plus_slg # 大谷翔平のSTATCAST規定到達シーズンの OPS
```
```
[1] 0.964 0.875 1.066 1.036
```
```
judge_aaron <- player_batting_all[player_batting_all$player_id == "592450",]
judge_aaron$on_base_plus_slg # アーロン・ジャッジのSTATCAST規定到達シーズンの OPS
```
```
[1] 1.049 0.917 1.111 1.159
```

  player_batting_all は、規定打席到達の年度別データが選手ごとに入っています。プライマリキーは、player_id と year の複合キーになるということなので、ある年度のある選手のデータを抽出する場合は、両方を指定しなければなりません。

```
ohtani_shohei_2024 <- player_batting_all[player_batting_all$player_id == "660271" & player_batting_all$year == "2024",]
ohtani_shohei_2024$on_base_plus_slg  # 大谷翔平の2024シーズンの OPS
```
```
[1] 1.036
```
```
judge_aaron_2024 <- player_batting_all[player_batting_all$player_id == "592450" & player_batting_all$year == "2024",]
judge_aaron_2024$on_base_plus_slg # アーロン・ジャッジの2024シーズンの OPS
```
```
[1] 1.159
```

　ある年度の全データが欲しい場合は、応用で、year で絞り込めば抽出できます。複合キーの一つである year で抽出しているので、当然ですが player_id のみで一意にデータを特定できます。

```
player_batting_2024 <- player_batting_all[player_batting_all$year == "2024",] # 2024シーズンのデータ
```
```
player_batting_2024$on_base_plus_slg[player_batting_2024$player_id == "660271"] # 大谷翔平の 2024シーズンの OPS
```
```
[1] 1.036
```
```
player_batting_2024$on_base_plus_slg[player_batting_2024$player_id == "592450"] # アーロン・ジャッジの 2024シーズンの OPS
```
```
[1] 1.159
```

2024 シーズンの OPS の summary を見てみましょう。

```
summary(player_batting_2024$on_base_plus_slg)
```
```
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.6130  0.7160  0.7550  0.7648  0.7910  1.1590 
```

　二人とも平均の 0.7550 を大きく上回り、ジャッジに関しては最大値を叩き出していることがわかります。