内野守備シフトにバントは有効か

MLBでは年々、内野守備シフトを敷く割合が高まってきている。2015年は全投球に対しシフトを敷いた状態での投球は10%程度だったのに対し2020年では約1/3とかなりの割合を占めるようになった。

守備シフト 割合

シフトが失点を防ぐ効果については左打者では多少効果があるものの、右打者ではむしろ失点のリスクが増えるという先行研究がある。

シフトの効果そのものについては先行研究に譲るとしてシフト破りについてはいくつか手段がある。1つは狭い内野の間を速い打球で抜いてしまうこと、1つはフライを打って内野手にボールを触れさせないこと、もう1つはシフトの反対方向にゴロを打つこと、そして今回分析する野手のいないところにバントで打球を転がすことである。

シフトに対しバントはどの程度行われているか

バントはバットを振らず特殊な打法でゴロを打つことをいう。内野守備シフトは片側に内野手を寄せることが一般的だ。その逆側にゴロを打てば出塁の確率は飛躍的に高まる。また私は野球経験者ではないがバントはスイングを伴う打法に比べるとコンタクトが容易であり打球の方向もコントロールしやすそうだ。シフトに対してバントを行うのは合理的な選択のように思える。

では実際内野守備シフトに対して打者はバントをどの程度を行っているのだろうか。2015-2020年のシフト打席に対してバントが行われた打席の程度を見てみることにする。

画像4

打者がシフトを敷いた打席に対しバントを行った打席数はわずか0.5%程度だった。シフトを敷く回数が急激に増えている一方でバントの割合は横這いといった感じだ。打者側がバントをすることにメリットを感じていないのか、打撃を崩したくないのか、バントが苦手なので行いたくない選手が多いのか、原因は不明だが打者はシフト対策にバントを採用することは稀なようである。

シフトに対するバントの効果

実際シフトに対してバントはどの程度の効果をあげているのだろうか。以下の画像は2015-2020年のMLBで内野守備シフトに対しバントを行った全ての打席の打撃成績だ。

画像3

wOBAとは得点価値を基に各イベントに対し加重が行われている打撃指標である。この.520という値はかなり優秀な値であり実在の選手で2001年以降シーズンwOBAが.500を超えたのはバリー・ボンズだけであるといえばその凄さがわかってもらえるだろうか。OBP(出塁率)は6割近くを記録しておりシフトに対してのバントはかなり出塁率が高い。こうしてみるとシフトに対するバントはかなり効果的な作戦のように思える。

とはいえこの結果を基にシフトを敷かれたら全ての打者がバントを行えばいいかというと早計だろう。前述のようにずっとバントをしたら打撃を崩す可能性もあるし、この成績についてもそもそもヒッティングとは明らかに異なる打法であるバントを積極的に行うのはバントが得意な打者でありバントが苦手な打者はそもそもバントをしていないというバイアスがかかっていて全ての打者にバントを行わせた場合と比べると高い値になっているという可能性があるからだ。

とはいえこの出塁率の高さはかなり魅力的だ。まとまった点を取らなければ相手に勝てない状況で相手がシフトを敷いたら打者はバントを積極的に試みてもいいように思える。また守備側はまとまった点を取られさえしなければいいという状況になったらバントされるリスクを考えてシフトを敷くのを控えた方がよいかもしれない。

バントをしなかった場合と比較してどれだけ効果的かを推定する

シフトを敷かれるのは打球を引っ張る強打者が多いように思う。バントが効果的といっても普通に打った場合と比べてどの程度効果的なのかを知ることには意味があるように思える。

そこで2015-2020年のMLBでシフト状態でバントをしたときとバントを仮に行わなかったとされる状態のときに推定される打撃成績を比較してバントがどの程度効果的かを見てみることにする。

方法としては2015-2020年のMLBでシフト状態で300打席以上立った打者がバントをした打席での実際にバントを行ったことで得られた成績とバントを仮に行わなかった場合に期待される数値を比較する、というものだ。

バントを行わなかった時の成績を推定する方法としては例えばシフト状態でバントを行った打者のwOBAの内訳がOBA.320の打者が3打席、.300の打者が2打席の場合は

(.320+.320+.300+.300+.300)/(1+1+1+1+1)

と計算して打者のwOBAの期待値を出す。(他の指標も同様に行う)

結果が以下となる。

比較

推定されるwOBA、出塁率と比較すると大分高い値になっている。wOBAでは196ポイント差でありこの差はとてつもなく大きい。ただし前述したようなバイアスがかかったうえでの数字であることには注意が必要だ。

対シフトのバント成功率の損益分岐

バントの成功率の損益分岐を考える。基本的にシフト状態の打者のwOBAが高いほど求められる成功率は高くなると考えられる。大雑把に求めてみたい。

まずバントヒットのほとんどはシングルヒットだ。そのため成功した時のwOBAvalueはだいたい0.9である。この0.9に成功率を掛けた値がバントをしたときに期待されるwOBAとなる。

0.9(単打のwOBAvalue)× p (バント成功率) = wOBA

右辺のwOBAにその打者のシフト状態のときに期待される値を代入しpについて解けばバント成功率の損益分岐を出せると考えられる。例えばシフト状態のwOBAが.360の打者の場合は

0.9p = 0.360

p = 0.4

40%、といった具合だ。wOBAが.450の場合は

0.9p = 0.450

p = 0.5

50%、となる。wOBAが高いほど求められるバント成功率は高くなる。

まとめ

内野守備シフト状態でバントをすると

・wOBAが推定値よりはるかに高くなる。

・OBP(出塁率)が飛躍的に高くなる。

・求められるバント成功率は打者のシフト状態のwOBAの値が高いほど高くなる。

ということがわかった。もちろんこの結果を受けてどんな打者、場面でもバントをするべきかというとそうではないが現在0.5%程度しか行われていないシフト状態でのバントを再考してみてもよいのではないかと思う。

今回の分析にはBaseball Savantから入手できるStatcastのデータを使用している。

以下、今回の分析に使ったRのコード。

#tidyverseを使用
library(tidyverse)

# dfには2015-2020のstatcastのデータが入っている
# 列が全部含まれていると止まるので必要な列に絞る
df <- df%>%
  select(batter, pitcher, events, type,
         des,description,stand,if_fielding_alignment,game_type,
         woba_value,woba_denom,game_date,inning,game_year)
save(df, file="sc_data_for_shift_Bunt.Rdata")

#データの下処理
df <- df %>% filter(game_type == "R")

strikeouts <- c("strikeout", "strikeout_double_play")

df <- df %>%
 mutate(K_FL = ifelse(events %in% strikeouts, 1, 0),
        Shift_IF_FL = ifelse(if_fielding_alignment == "Infield shift", 1, 0),
        Strategic_IF_FL = ifelse(if_fielding_alignment == "Strategic", 1, 0))

#内野守備シフトがどれくらい増えてるか確認
table(df$if_fielding_alignment, df$game_year)


#Buntした打席はwoba_denomがnullなのでPAが1の列を作る。
#(正確にはsac_buntを0としているため厳密なPAではない)
df <- df  %>% mutate(PA = case_when( 
  events == "null" ~ "0",
  events == "single" ~ "1",
  events == "double" ~ "1",
  events == "triple" ~ "1",
  events == "home_run" ~ "1",
  events == "force_out" ~ "1",
  events == "field_out" ~ "1",
  events == "sac_fly" ~ "1",
  events == "grounded_into_double_play" ~ "1",
  events == "field_error" ~ "1",
  events == "fielders_choice_out" ~ "1",
  events == "fielders_choice" ~ "1",
  events == "hit_by_pitch" ~ "1",
  events == "walk" ~ "1",
  events == "strikeout" ~ "1",
  events == "strikeout_double_play" ~ "1",
  events == "double_play" ~ "1",
  events == "sac_bunt" ~ "0"),
  PA = as.double(PA))


#OBP_valueを作る。公式記録とは異なり失策・野手選択出塁も出塁にカウントする。
df <- df  %>% mutate(OBP_value = case_when( 
   events == "null" ~ "0",
   events == "single" ~ "1",
   events == "double" ~ "1",
   events == "triple" ~ "1",
   events == "home_run" ~ "1",
   events == "force_out" ~ "0",
   events == "field_out" ~ "0",
   events == "sac_fly" ~ "0",
   events == "grounded_into_double_play" ~ "0",
   events == "field_error" ~ "1",
   events == "fielders_choice_out" ~ "0",
   events == "fielders_choice" ~ "1",
   events == "hit_by_pitch" ~ "1",
   events == "walk" ~ "1",
   events == "strikeout" ~ "0",
   events == "strikeout_double_play" ~ "0",
   events == "double_play" ~ "0",
   events == "sac_bunt" ~ "0"),
   OBP_value = as.double(OBP_value))

#バントを検索しバントのデータが入ったdfを作る
Bunt <- df %>%
 filter(if_fielding_alignment == "Infield shift" & grepl("bunt", des))

#バントのフラッグを作りdfと結合
Bunt <- Bunt %>%
  mutate(Bunt_FL = "Bunt")

df <- df%>%
  left_join(Bunt %>% select(game_date,pitcher,batter,inning,Bunt_FL))


#シフトPAとbuntPAがどれくらい増えてるか確認
bunt_PA <- df %>% 
  group_by(if_fielding_alignment,Bunt_FL,game_year) %>%
  dplyr::summarise(B_PA = sum(PA, na.rm = TRUE))

write_csv(bunt_PA,"bunt_PA.csv")

#内野守備シフト時のバントStats

bunt_stats <- df %>% 
  filter(if_fielding_alignment == "Infield shift") %>%
  filter(Bunt_FL == "Bunt") %>%
  dplyr::summarise(wOBA_value = sum(woba_value, na.rm = TRUE),
                   B_PA = sum(PA, na.rm = TRUE),
                   wOBA = wOBA_value / B_PA,
                   OBP_value = sum(OBP_value, na.rm = TRUE),
                   OBP = OBP_value / B_PA ,
                   K  =  sum(K_FL, na.rm = TRUE),
                   Kpct =  K *100 / B_PA)

#バント基本statsをcsvで保存

write_csv(bunt_stats,"Bunt_stats_ALL.csv")

#バントを除いたシフト状態の打撃成績を算出。推定に使えそうな300打席以上の打者を対象。

bat <- df %>% 
  filter(if_fielding_alignment == "Infield shift") %>%
  group_by(batter) %>%
  dplyr::summarise(wOBA_value = sum(woba_value, na.rm = TRUE),
                   B_PA = sum(PA, na.rm = TRUE),
                   wOBA = wOBA_value / B_PA,
                   OBP_value = sum(OBP_value, na.rm = TRUE),
                   OBP = OBP_value / B_PA ,
                   K  =  sum(K_FL, na.rm = TRUE),
                   Kpct =  K *100 / B_PA) %>%
  
  
  filter(B_PA >= 300) 

#打席結果のみと結合するためPA=1を列に加える

bat <- bat %>%
  mutate(PA = 1,
         taisyou = "taisyou")

df <- df%>%
  left_join(bat %>% select(batter,PA,wOBA,Kpct,OBP,taisyou))

#バント時打撃成績としなかったときの推定打撃成績を算出

Bunt_bat <- df %>%
  filter(if_fielding_alignment == "Infield shift")%>%
  group_by(Bunt_FL,taisyou)%>%
  dplyr::summarise(ex_wOBA = mean(wOBA, na.rm = TRUE),
                   ex_Kpct = mean(Kpct, na.rm = TRUE),
                   wOBA_value = sum(woba_value, na.rm = TRUE),
                   PA = sum(PA, na.rm = TRUE),
                   wOBA = wOBA_value / PA,
                   ex_OBP = mean(OBP, na.rm = TRUE),
                   OBP_value = sum(OBP_value, na.rm = TRUE),
                   OBP = OBP_value / PA ,
                   K  =  sum(K_FL, na.rm = TRUE),
                   Kpct =  K *100 / PA)

#csvを作成し表をExcelで作成した
write_csv(Bunt_bat,"Bunt_stats.csv")

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