야구 분석/R

안타와 도루의 가치

sam_j_s 2024. 8. 10. 02:25
728x90
반응형

주제

야구 경기는 다양한 타격과 주루 플레이를 통해 득점을 올리는 게임입니다. 안타, 홈런, 도루는 모두 팀의 득점에 기여할 수 있는 중요한 요소입니다. 하지만 각 플레이의 득점 가치는 상황에 따라 다르며, 이를 정량적으로 분석하는 것은 팀 전략 수립에 중요한 역할을 합니다. 이번 글에서는 안타, 홈런, 도루의 득점 가치를 심층적으로 분석하여, 이들이 경기에서 어떻게 활용될 수 있는지 알아보겠습니다.

 

다양한 안타의 득점 가치

야구 분석에서 득점 가치(run values)는 다양한 응용 분야를 가지고 있습니다. 이번 글에서는 득점 창출 관점에서 홈런과 단타의 가치를 살펴보겠습니다.

 

타율(batting average)에 대한 한 가지 비판은 네 가지 가능한 안타(단타, 2루타, 3루타, 홈런)에 동일한 가치를 부여한다는 점입니다. 안타의 가치를 구별하는 한 가지 방법은 도달한 베이스의 수를 할당하는 것입니다: 단타는 1, 2루타는 2, 3루타는 3, 홈런은 4로 할당합니다. 또 다른 방법으로는 총 베이스 수를 타수로 나눈 장타율(slugging percentage)을 사용하는 것입니다. 하지만 1, 2, 3, 4라는 값이 네 가지 가능한 안타의 가치를 합리적으로 측정하는지 명확하지 않습니다. 득점 가치를 사용하면 이러한 안타들의 중요성을 더 정확하게 측정할 수 있습니다.

홈런의 가치

이번에는 득점 관점에서 홈런의 가치에 집중해 보겠습니다. event_cd 플레이 이벤트 변수를 사용하여 데이터 프레임 retro2016에서 홈런 플레이를 추출합니다. event_cd 값이 23이면 홈런을 의미합니다. filter() 함수를 event_cd == 23 조건과 함께 사용하여 홈런 플레이를 포함한 새로운 데이터 프레임 home_runs를 생성합니다.

home_runs <- retro2016 |> 
  filter(event_cd == 23)

 

2016 시즌 동안 홈런이 발생한 주자/아웃 상태는 어떠했을까요? 이 질문에 답하기 위해 table() 함수를 사용합니다.

home_runs |>
  select(state) |>
  table()

prop.table() 함수를 사용하여 상대 빈도를 계산하고, round() 함수를 사용하여 값을 소수점 세 자리로 반올림합니다.

home_runs |>
  select(state) |>
  table() |>
  prop.table() |>
  round(3)

이 표에서 주자가 없는 상황에서 기록된 홈런의 비율이 0.273 + 0.171 + 0.151 = 0.595 임을 알 수 있습니다. 즉, 절반 이상의 홈런이 주자가 없는 상황에서 나옵니다.

 

그렇다면 전체적으로 홈런의 득점 가치는 얼마일까요? 이 질문에 답하기 위해, 데이터 프레임 home_runs에 있는 모든 홈런의 평균 득점 가치를 계산합니다.

mean_hr <- home_runs |>
  summarize(mean_run_value = mean(run_value))
mean_hr

이 홈런들의 득점 가치는 얼마일까요? 이미 알투베의 데이터 분석에서 주자가 없는 상황에서 홈런의 득점 가치는 1 임을 관찰했습니다. geom_histogram() 함수를 사용하여 모든 홈런의 득점 가치에 대한 히스토그램을 작성합니다.

ggplot(home_runs, aes(run_value)) +
  geom_histogram() + 
  geom_vline(
    data = mean_hr, aes(xintercept = mean_run_value), 
    color = "red", linewidth = 1.5
  ) +
  annotate(
    "text", 1.7, 2000, 
    label = "Mean Run\nValue", color = "red"
  )

이 그래프에서 대부분의 홈런(주자가 없는 상황에서의 홈런)은 득점 가치가 1임을 알 수 있습니다. 그러나 득점 가치가 1.5에서 2.0 사이인 홈런의 집단이 있으며, 득점 가치가 3을 초과하는 소수의 홈런도 있습니다.

 

어떤 주자/아웃 상황이 가장 가치 있는 홈런으로 이어질까요? arrange() 함수를 사용하여 가장 큰 득점 가치에 해당하는 데이터 프레임의 행을 표시합니다.

home_runs |> 
  arrange(desc(run_value)) |>
  select(state, new_state, run_value) |>
  slice_head(n = 1)

예상할 수 있듯이, 가장 가치 있는 홈런은 2아웃 만루 상황에서 발생합니다. 이 홈런의 득점 가치는 3.41입니다.

 

geom_vline() 함수를 사용하여 그래프에 평균 득점 가치를 나타내는 수직선을 그리고, 이 선에 라벨을 추가합니다. 이 평균 득점 가치는 2 아웃 만루 홈런의 가치에 비해 작지만, 이는 대부분의 홈런이 주자가 없는 상황에서 발생한다는 사실을 부분적으로 반영합니다.

단타의 가치

득점 가치는 단타의 이점을 평가하는 데에도 사용할 수 있습니다. 홈런과 달리, 단타의 득점 가치는 초기 상태(주자 및 아웃 수)와 최종 상태에 따라 달라집니다. 홈런의 경우 최종 상태는 항상 주자가 없는 상태가 되지만, 단타의 경우에는 주자가 베이스를 이동하는 방식에 따라 최종 상태가 달라집니다.

 

filter() 함수를 사용하여 event_cd가 20(단타에 해당하는 값)인 플레이를 선택합니다. 새로운 데이터 프레임은 singles라고 합니다. 다음 그래프에서는 2016 시즌의 모든 단타에 대한 득점 가치의 히스토그램을 작성합니다. 홈런의 경우와 마찬가지로, 단타의 평균 득점 가치를 계산하는 것은 간단합니다. 이 평균값을 그래프의 히스토그램에 표시합니다.

singles <- retro2016 |> 
  filter(event_cd == 20)

mean_singles <- singles |>
  summarize(mean_run_value = mean(run_value))

ggplot(singles, aes(run_value)) + 
  geom_histogram(bins = 40) +
  geom_vline(
    data = mean_singles, color = "red", 
    aes(xintercept = mean_run_value), linewidth = 1.5
  ) +
  annotate(
    "text", 0.8, 4000, 
    label = "Mean Run\nValue", color = "red"
  )

단타의 득점 가치 히스토그램을 보면, 0과 0.5 사이에 세 개의 큰 스파이크가 있습니다. 이 큰 스파이크는 시작 상태의 빈도 표를 구성하여 설명할 수 있습니다.

singles |>
  select(state) |>
  table()

단타의 대부분이 주자가 없는 상황에서 발생하며, 그래프에서 왼쪽에서 오른쪽으로 이동하면서 나타나는 세 개의 큰 스파이크는 주자가 없고 2아웃, 1 아웃, 그리고 무사인 상황에서의 단타에 해당합니다. 득점 가치가 0.5에서 2.0 사이인 작은 집단은 주자가 있는 상황에서의 단타에 해당합니다.

 

득점 가치 관점에서 가장 가치 있는 단타는 무엇일까요? arrange() 함수를 사용하여 가장 큰 득점 가치를 기록한 단타의 시작 상태와 종료 상태를 찾습니다.

singles |> 
  arrange(desc(run_value)) |>
  select(state, new_state, run_value) |>
  slice_head(n = 1)

이 특정 플레이에서 타자는 2아웃 만루 상황에서 타석에 들어섰고, 최종 상태는 2 아웃 3루 주자 상태였습니다. 단타로 어떻게 이런 상황이 발생할 수 있을까요? 데이터 프레임에는 이 플레이에 대한 간략한 설명이 포함되어 있습니다. 우리는 데이터 프레임에서 이 플레이가 2016년 6월 5일 오리올스와 양키스의 경기 8회 말에 발생했음을 확인했습니다. Baseball-Reference에서 다음과 같은 플레이 설명을 찾을 수 있습니다.

Single to CF (Ground Ball thru SS-2B); Trumbo Scores; Davis Scores; Pena Scores/unER/Adv on E8 (throw); to 3B/Adv on throw
중견수 방면으로 단타(유격수와 2루수 사이로 지나가는 땅볼); 트럼보 득점; 데이비스 득점; 페냐 득점(비자책점)/E8(중견수의 송구 실책으로 진루); 3루로 진루/송구 중 진루.

 

분명히, 중견수가 단타를 처리하는 과정에서 실책을 저질러 세 명의 주자가 모두 득점하고 타자가 3루까지 진루할 수 있게 했습니다.

 

반대의 경우로, arrange() 함수를 사용하여 가장 작은 득점 가치를 기록한 두 가지 플레이를 식별합니다.

singles |> 
  arrange(run_value) |>
  select(state, new_state, run_value) |>
  slice(1)

단타의 득점 가치가 어떻게 -0.6점일 수 있을까요? 추가 조사를 통해 각 경우에서 2루 주자가 타구에 맞아 아웃된 것을 발견했습니다.

 

이 경우, 단타의 평균 득점 가치는 주자가 없는 상황에서 무사에 단타가 기록된 경우의 득점 가치와 거의 동일합니다. 흥미롭게도 단타의 득점 가치는 1에서 2 사이로 크게 나타날 수 있습니다. 이러한 큰 득점 가치는 단타의 이전이 주자의 진루에 따라 달라지기 때문입니다.

도루의 가치

득점 기대 행렬(run expectancy matrix)은 도루의 이점을 이해하는 데도 유용합니다. 주자가 베이스를 도루하려고 시도할 때 두 가지 결과가 있습니다. 주자가 성공적으로 베이스를 훔치거나 도루 실패로 아웃되는 것입니다. 전체적으로 베이스 도루 시도는 순이익이 있을까요?

 

변수 event_cd는 플레이의 코드를 나타내며, 4와 6은 각각 도루 성공(SB)과 도루 실패(CS)를 나타냅니다. filter() 함수를 사용하여 도루 시도가 포함된 플레이만으로 구성된 새로운 데이터 프레임 stealing을 생성합니다.

stealing <- retro2016 |> 
  filter(event_cd %in% c(4, 6))

 

summarize() 함수와 n() 함수를 사용하여 도루 성공(SB)과 도루 실패(CS) 결과의 빈도를 구합니다.

stealing |> 
  group_by(event_cd) |> 
  summarize(N = n()) |> 
  mutate(pct = N / sum(N))

모든 도루 시도 중 도루 성공 비율은 2213 / (2213 + 713) = 0.756입니다.

 

도루 시도가 주로 발생하는 일반적인 주자/아웃 상황은 무엇일까요? 이 질문에 답하기 위해 state 변수에 대한 빈도 표를 작성합니다.

stealing |> 
  group_by(state) |> 
  summarize(N = n())

도루 시도는 주로 1루에만 주자가 있는 상황(state 100)에서 발생합니다. 하지만 주자들이 도루를 시도하는 다양한 상황이 존재합니다.

 

모든 도루 시도에는 해당하는 득점 가치(run_value)가 있으며, 이는 시도의 성공(SB) 또는 실패(CS)와 시도가 발생한 상황(주자 및 아웃 수)을 반영합니다. geom_histogram() 함수를 사용하여 모든 도루 시도에서 생성된 득점 가치의 히스토그램을 작성하고, 그래프에 나타냅니다. 막대의 색상은 시도의 성공 또는 실패를 나타냅니다.

ggplot(stealing, aes(run_value, fill = factor(event_cd))) + 
  geom_histogram() +
  scale_fill_manual(
    name = "event_cd",
    values = crc_fc,
    labels = c("Stolen Base (SB)", "Caught Stealing (CS)")
  )

일반적으로 성공적인 도루(SB)는 모두 긍정적인 득점 가치를 가지지만, 대부분의 값은 0에서 0.3 사이에 분포합니다. 반면에 실패한 도루 시도(CS)는 예상대로 음수 득점 가치를 가집니다. 추가 탐구에서 음수 득점 가치에 해당하는 세 개의 스파이크는 1루에 주자가 있는 상태에서 0, 1, 2 아웃일 때의 CS와 일치함을 알 수 있습니다.

 

특정 상황에서 도루 시도의 이점에 집중해 봅시다. 1아웃에 1루 주자가 있는 상황(state “100 1”)에서 도루 시도 데이터를 포함하는 새로운 데이터 프레임을 생성합니다.

stealing_1001 <- stealing |> 
  filter(state == "100 1")

 

event_cd 변수를 집계하여, 주자가 498 + 210번의 시도 중 498번 성공적으로 도루를 성공했음을 확인할 수 있으며, 성공률은 70.3%입니다.

stealing_1001 |> 
  group_by(event_cd) |> 
  summarize(N = n()) |> 
  mutate(pct = N / sum(N))

결과를 보는 또 다른 방법은 new_state 변수의 빈도를 확인하는 것입니다.

stealing_1001 |> 
  group_by(new_state) |> 
  summarize(N = n()) |> 
  mutate(pct = N / sum(N))

이 방식은 단순히 도루 성공 여부를 기록하는 것보다 더 많은 정보를 제공합니다. 457번의 경우 주자가 성공적으로 2루로 진루했습니다. 추가로 39번의 경우 주자가 3루까지 진루했습니다. 아마도 이 추가 진루는 포수의 나쁜 송구나 내야수의 실책 때문일 수 있습니다. 이러한 플레이의 세부 사항은 다른 변수를 추가로 조사하여 더 많이 알 수 있습니다.

 

우리가 가장 관심 있는 것은 이 상황에서 도루 시도의 가치입니다. 이를 위해 1 아웃 1루 상황에서 모든 도루 시도의 평균 득점 가치를 계산합니다.

stealing_1001 |> 
  summarize(Mean = mean(run_value))

도루 시도는 가치가 있으며, 전체적으로 시도당 약 0.007점의 득점 가치를 가지고 있습니다. 물론, 실제 이점은 도루 시도의 성공 여부와 도루가 시도된 상황(주자 및 아웃 수)에 따라 달라집니다.

반응형

'야구 분석/R'의 다른글

  • 현재글 안타와 도루의 가치

관련글