負け試合で最終回にヒットを打つと, 解説のおじさんがよく言います.
“明日につながるヒット”.
本当につながるのか, 確認します.
ここが肝心です.
「つながるヒットを打った人の翌日の打率」を出しても, それだけでは何も言えません. 大差で負けている試合の最終打席に立つのは主力打者が多いので, リーグ平均と比べれば当然高く出ます. 打者の質を拾っているだけです.
必要なのは対照群です. 同じ「大差で負けている試合の最終打席」に立って, ヒットが出なかった人の翌日と比べます. こうすれば場面の条件はそろい, 違うのは「その打席で打てたかどうか」だけになります.
library(data.table)
library(dplyr)
library(ggplot2)
source("../../R/retrosheet.R")
dat <- fread("../../data/all2013.csv")
setnames(dat, fread("../../data/reference/retrosheet-columns.csv", header = FALSE)[[1]])
game_stats <-
dat |>
as_tibble() |>
mutate(打席 = as_flag(AB_FL), 安打 = H_FL > 0, 塁打 = H_FL) |>
summarise(打数 = sum(打席), 安打 = sum(安打 & 打席), 塁打 = sum(塁打),
.by = c(BAT_ID, GAME_ID)) |>
mutate(日付 = game_date_key(GAME_ID)) |>
arrange(BAT_ID, 日付) |>
mutate(出場順 = row_number(), .by = BAT_ID)
「翌日」はカレンダーの翌日ではなく,
その打者の次の出場試合とします. 移動日や休養日があるので,
日付を1足すだけだと取りこぼします. ダブルヘッダーも GAME_ID
の末尾で区別されます.
最終回の攻撃で, 打っている側が4点以上負けている場面を取ります.
もとのコードは HOME_SCORE_CT - AWAY_SCORE_CT >= 4
としていましたが, これはホームから見た点差です.
ホームが攻撃しているときは
勝っている側を拾ってしまうので,
攻守で符号を変えます.
last_ab <-
dat |>
as_tibble() |>
filter(INN_CT == max(INN_CT), .by = GAME_ID) |>
filter(BAT_HOME_ID == max(BAT_HOME_ID), .by = GAME_ID) |>
mutate(ビハインド = if_else(BAT_HOME_ID == 1L,
AWAY_SCORE_CT - HOME_SCORE_CT,
HOME_SCORE_CT - AWAY_SCORE_CT)) |>
filter(ビハインド >= 4, as_flag(AB_FL)) |>
summarise(安打あり = any(H_FL > 0), .by = c(BAT_ID, GAME_ID))
対象は3,478打者×試合, うち安打があったのは810件 (23.3%) です.
この場面の打率0.233は, リーグ全体の 0.253より21ポイント 低くなっています. 大差で負けている終盤なので, 相手は勝ちを固める継投に 入っていることが多く, また打つ側も主力が退いている場合があります. 場面として楽ではない, ということです.
next_game <-
last_ab |>
inner_join(game_stats |> select(BAT_ID, GAME_ID, 出場順), by = c("BAT_ID", "GAME_ID")) |>
mutate(出場順 = 出場順 + 1L) |>
inner_join(game_stats |> select(BAT_ID, 出場順, 打数, 安打, 塁打),
by = c("BAT_ID", "出場順")) |>
filter(打数 > 0)
次の試合まで追えたのは3,377件です.
result <-
next_game |>
summarise(件数 = n(), 打数 = sum(打数), 安打 = sum(安打), 塁打 = sum(塁打),
.by = 安打あり) |>
mutate(打率 = 安打 / 打数, 長打率 = 塁打 / 打数) |>
arrange(desc(安打あり))
result |> knitr::kable(digits = 4)
| 安打あり | 件数 | 打数 | 安打 | 塁打 | 打率 | 長打率 |
|---|---|---|---|---|---|---|
| TRUE | 780 | 2715 | 682 | 1049 | 0.2512 | 0.3864 |
| FALSE | 2597 | 8811 | 2265 | 3583 | 0.2571 | 0.4067 |
翌試合の打率の差は -0.0059, 95%信頼区間は [-0.0246, +0.0128] です.
区間は0をまたいでいます.
対照群を置いても, まだ気になることがあります. 最終打席で打てる打者はもともと良い打者かもしれません.
そこで同じ打者の中で比べます. その打者のシーズン打率を引いて, 「いつもの自分と比べてどうだったか」を見ます.
season <-
game_stats |>
summarise(季節打数 = sum(打数), 季節安打 = sum(安打), .by = BAT_ID) |>
filter(季節打数 >= 100) |>
mutate(季節打率 = 季節安打 / 季節打数)
within <-
next_game |>
inner_join(season, by = "BAT_ID") |>
mutate(翌試合打率 = 安打 / 打数, 差 = 翌試合打率 - 季節打率)
within |>
summarise(件数 = n(), `自分の平均との差` = mean(差), sd = sd(差), .by = 安打あり) |>
mutate(se = sd / sqrt(件数)) |>
arrange(desc(安打あり)) |>
knitr::kable(digits = 4)
| 安打あり | 件数 | 自分の平均との差 | sd | se |
|---|---|---|---|---|
| TRUE | 742 | -0.0139 | 0.2467 | 0.0091 |
| FALSE | 2411 | 0.0012 | 0.2581 | 0.0053 |
同一打者内で見ても差は-0.0151, 95%信頼区間[-0.0356, +0.0055]です.
within |>
ggplot(aes(x = 差, fill = 安打あり)) +
geom_density(alpha = 0.4) +
geom_vline(xintercept = 0, linetype = "dashed") +
xlab("翌試合の打率 − その打者のシーズン打率") +
ggtitle("いつもの自分と比べてどうだったか")

1試合の打率は4打数のうち何本か, という粗い量なので分布は飛び飛びです. 平均で見るしかありません.
「明日につながるヒット」の効果は見つかりませんでした.
大差で負けている試合の最終打席という同じ場面で比べると, 打てた人と 打てなかった人で, 翌試合の打率に意味のある差はありません. 同じ打者の中で比べても同じです.
もとの分析は「つながるヒットを打った人の翌日の打率」をリーグ平均と 比べていましたが, それでは打者の質を見ているだけになります. 対照群を置くと差は消えました.