得点価値で見る打たれにくいスライダー・カットボール

 「曲がりの大きな横スライダー」「切れ味鋭い高速スライダー」スライダーを形容する呼び名は数多くあるがその中でも打たれにくいスライダーとはどんなスライダーだろうか。

スライダーを得点価値で評価する


 守備の目的は失点を防ぐことだ。つまり良いスライダーとは失点を防ぐスライダーだ。セイバーメトリクスではRE288と呼ばれるアウト×塁状況×ボール・ストライクカウントを合わせた288の状況の得点期待値の変動を用いることでプレーを1球単位で得点化することが可能だ。この得点期待値の変動(Statcastではdelta_run_expと呼ばれる)を用いることによってスライダーを得点価値で評価する。難しい説明と思われたかもしれないが簡単に言えばストライクを取る、アウトを取るといった失点を減らすことが期待されるプレーにはマイナスの得点価値を与え、ヒットを打たれる、ボールを投げるといった失点の見込みを増やすプレーにはプラスの得点価値を与えることでプレーを評価しようということだ。(投手にとっては低ければ低いほど失点のリスクが低いことを意味する。)


球速・変化量・利き手の組み合わせでグループ化


 2017-2020年のMLBで投げられたスライダー・カットボールを対象に球速は5km/h、ボール変化量を7.5cm(ボール1個分)ずつに区切りそれぞれのグループでの得点価値の平均を取る。このときスケールを100球あたりの得点価値(RV/100)にする。また利き手の組み合わせ(同じ利き腕同士の対戦か、異なる利き腕同士の対戦か)についてもグループで分ける。


同じ利き腕同士のスライダー

画像1

画像2

画像3

画像4


 値は低ければ低いほど得点価値が低い、つまり投手にとっては失点のリスクが低いことを意味している。また値が高ければ高いほど赤色が濃く低ければ低いほど青色が濃くなるようカラースケールされている。全体的な傾向でいえば
・グラブ側方向への曲がりが大きければ大きいほど失点のリスクが低くなる。
・130km/h未満のスライダーは自由落下より浮くほうが失点のリスクが低くなりやすい。
・140km/hを超えるようなスライダーは縦変化が小さい方が失点のリスクが低くなる。
といったことが挙げられる。横変化が大きいほうが失点を防ぎやすいのは共通しているが縦方向の変化については球速により効果が異なっているようだ。

異なる利き腕同士のスライダー

画像5

画像6

画像7

画像8

 全体的な傾向で言うと
・同じ利き腕同士と比べて得点価値が高くなりやすい。
・140km/h未満の場合、22.5cm以上グラブ側に大きく曲がる球が有効。
・球速140km/h以上の場合は縦変化15cm未満、もしくは横変化7.5cm以上だと有効。

異なる利き腕同士だと同じ利き腕同士のときと比べてスライダー・カットボールで失点を防ぐのは難しいことがわかる。140km/h以上出て横変化がボール1個分以上あるスライダーか沈むスライダーは広い範囲でRun Value(得点価値)が低くなりやすく異なる利き腕の打者を抑えるには条件を満たすカットボール気味のスライダーを習得するのが良さそうだ。

Rのコード

library(tidyverse)

df <- df %>%
 mutate(
   pitch_type = case_when(
     pitch_type == "FF" ~ "FF", 
     pitch_type == "FT" | pitch_type == "SI" ~ "SI",
     pitch_type == "SL" | pitch_type == "FC" ~ "SL",
     pitch_type == "CU" | pitch_type == "KC" ~ "CU",
     pitch_type == "CH" | pitch_type == "FS" ~ "CH",
     TRUE ~ "ELSE"),
   pfx_x_cm = ifelse(p_throws == "R",pfx_x*30.48,pfx_x*-30.48),
   pfx_z_cm = pfx_z*30.48,
   velocity = release_speed * 1.609,
   throw_stand = ifelse(p_throws == "R" & stand == "R" | p_throws == "L" & stand == "L" ,"same","difference"),
   swing_denom = case_when( 
     description == "swinging_strike" ~ 1,
     description == "swinging_strike_blocked" ~ 1,
     description == "foul_tip" ~ 1,
     description == "foul" ~ 1,
     description == "hit_into_play" ~ 1,
     description == "hit_into_play_score" ~ 1,
     description == "hit_into_play_no_out" ~ 1,
     TRUE ~ 0),
   swstr = case_when( 
     description == "swinging_strike" ~ 1,
     description == "swinging_strike_blocked" ~ 1,
     description == "foul_tip" ~ 1,
     TRUE ~ 0),
   contact = case_when( 
     description == "foul" ~ 1,
     description == "hit_into_play" ~ 1,
     description == "hit_into_play_score" ~ 1,
     description == "hit_into_play_no_out" ~ 1,
     TRUE ~ 0),
   ball_zone = ifelse(zone == 11 | zone == 12 | zone == 13 | zone == 14 ,1,0),
   strike_zone = ifelse(ball_zone == 0,1,0),
   ball_contact = ifelse(ball_zone == 1 & contact == 1 ,1,0),
   strike_contact = ifelse(strike_zone == 1 & contact == 1 ,1,0),
   ball_swing_denom = case_when( 
     ball_zone == 1 & description == "swinging_strike" ~ 1,
     ball_zone == 1 & description == "swinging_strike_blocked" ~ 1,
     ball_zone == 1 & description == "foul_tip" ~ 1,
     ball_zone == 1 & description == "foul" ~ 1,
     ball_zone == 1 & description == "hit_into_play" ~ 1,
     ball_zone == 1 & description == "hit_into_play_score" ~ 1,
     ball_zone == 1 & description == "hit_into_play_no_out" ~ 1,
     TRUE ~ 0),
   strike_swing_denom = case_when( 
     strike_zone == 1 & description == "swinging_strike" ~ 1,
     strike_zone == 1 & description == "swinging_strike_blocked" ~ 1,
     strike_zone == 1 & description == "foul_tip" ~ 1,
     strike_zone == 1 & description == "foul" ~ 1,
     strike_zone == 1 & description == "hit_into_play" ~ 1,
     strike_zone == 1 & description == "hit_into_play_score" ~ 1,
     strike_zone == 1 & description == "hit_into_play_no_out" ~ 1,
     TRUE ~ 0),
   strike_denom = case_when( 
     description == "swinging_strike" ~ 1,
     description == "swinging_strike_blocked" ~ 1,
     description == "foul_tip" ~ 1,
     description == "called_strike" ~ 1,
     TRUE ~ 0),
   called_strike = ifelse(description == "called_strike",1,0),
   xwoba_value = ifelse(type == "X",estimated_woba_using_speedangle,woba_value))

Group <- seq(-75,75,7.5)
Group_2 <- seq(125,150,5)
df$pfx_x_cm_bin <- with(df, cut(pfx_x_cm, Group))
df$pfx_z_cm_bin <- with(df, cut(pfx_z_cm, Group))
df$velocity_bin <- with(df, cut(velocity, Group_2))

PT_Plate <- df %>%
 filter(pitch_type == "SL" | pitch_type == "FC")%>%
 group_by(throw_stand,velocity_bin,pfx_z_cm_bin,pfx_x_cm_bin)%>%
 dplyr::summarise(N = n(),
                  Ball_zone = sum(ball_zone ,na.rm = TRUE),
                  sum_called_strike = sum(called_strike ,na.rm=TRUE),
                  Strike = sum(strike_denom,na.rm=TRUE),
                  sum_Strike_Zone = sum(strike_zone ,na.rm=TRUE),
                  Swing = sum(swing_denom,na.rm =TRUE),
                  O_Swing = sum(ball_swing_denom,na.rm =TRUE),
                  Z_Swing = sum(strike_swing_denom,na.rm=TRUE),
                  Zonepct = round(sum_Strike_Zone/N*100,1),
                  called_strikepct = round(sum_called_strike/N*100,1),
                  Strikepct = round(Strike/N*100,1),
                  Swingpct = round(Swing / N*100,1),
                  O_Swingpct = round(O_Swing / Ball_zone*100,1),
                  Z_Swingpct = round(Z_Swing / sum_Strike_Zone*100,1),
                  Contactpct = round(sum(contact,na.rm=TRUE)/Swing*100,1),
                  O_Contactpct = round(sum(ball_contact,na.rm=TRUE)/O_Swing*100,1),
                  Z_Contactpct = round(sum(strike_contact,na.rm=TRUE)/Z_Swing*100,1),
                  SwStrpct = round(sum(swstr,na.rm=TRUE)/N*100,1),
                  RV_100 = round(mean(delta_run_exp,na.rm = TRUE)*100,2))%>%
 select(throw_stand,velocity_bin,pfx_z_cm_bin,pfx_x_cm_bin,N,Zonepct,Strikepct,called_strikepct,Swingpct,O_Swingpct,Z_Swingpct,Contactpct,O_Contactpct,Z_Contactpct,SwStrpct,RV_100)%>%
 filter(N >= 500)
 
PT_typeX <- df %>%
 filter(pitch_type == "SL" | pitch_type == "FC")%>%
 filter(type == "X")%>%
 group_by(throw_stand,velocity_bin,pfx_z_cm_bin,pfx_x_cm_bin)%>%
 dplyr::summarise(wOBAcon_denom = sum(woba_denom),
                  xwOBA_value = sum(estimated_woba_using_speedangle,na.rm=TRUE),
                  xwOBAcon = round(xwOBA_value/wOBAcon_denom,3)
 )%>%
 select(throw_stand,velocity_bin,pfx_x_cm_bin,pfx_x_cm_bin,xwOBAcon)

PT <- left_join(PT_Plate,PT_typeX)%>%
 select(throw_stand,velocity_bin,pfx_z_cm_bin,pfx_x_cm_bin,N,Zonepct,Strikepct,called_strikepct,Swingpct,O_Swingpct,Z_Swingpct,Contactpct,O_Contactpct,Z_Contactpct,SwStrpct,xwOBAcon,RV_100)

PT <- na.omit(PT)

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