주제
우리는 다양한 선수들의 커리어를 비교함으로써, 그들이 어떻게 성장하고 변해왔는지를 살펴보았습니다. 이제 우리는 이러한 분석을 한 단계 더 발전시켜, ‘정점 나이’의 패턴을 집중적으로 살펴볼 것입니다. 정점 나이는 선수들의 커리어에서 가장 높은 성과를 기록하는 시점을 의미하며, 이는 선수의 경기력과 경과의 중요한 지표가 됩니다. 이번 글에서는 이러한 정점 나이가 시간에 따라 어떻게 변화해 왔는지, 그리고 이로부터 얻을 수 있는 중요한 통찰들을 분석해 보겠습니다
정점 나이의 일반적인 패턴
모든 피팅 궤적 계산하기
우리는 유사한 선수 그룹의 타격 커리어 궤적을 탐색했습니다. 이제 야구 역사에서 커리어 궤적이 어떻게 변화했는지 살펴보겠습니다. 선수의 정점 나이에 주목하고, 이것이 시간이 지남에 따라 어떻게 변화했는지 탐구할 것입니다. 또한, 정점 나이와 커리어 타석 수 사이의 관계를 조사할 것입니다.
우리는 현재 활동 중이지 않은 선수들에 집중하고자 하므로, People 데이터 프레임의 finalgame 변수를 사용하여 최종 경기가 2021년 11월 1일 이전인 선수들로 분석 대상을 제한합니다.
not_current_playerID <- People |>
filter(finalGame < "2021-11-01") |>
pull(playerID)
batting_2000 <- batting_2000 |>
filter(playerID %in% not_current_playerID)
각 선수에 대해 yearID 변수는 플레이한 시즌을 포함합니다. 우리는 Midyear라는 새로운 변수를 정의하여, 선수의 첫 시즌과 마지막 시즌의 평균을 구합니다. group_by()와 summarize() 함수를 사용하여 모든 선수의 Midyear를 계산하고, 이 새로운 변수를 inner_join() 함수를 사용하여 batting_2000 데이터 프레임에 추가합니다.
midcareers <- batting_2000 |>
group_by(playerID) |>
summarize(
Midyear = (min(yearID) + max(yearID)) / 2,
AB_total = first(AB_career)
)
batting_2000 <- batting_2000 |>
inner_join(midcareers, by = "playerID")
모든 커리어 궤적에 대해 2차 곡선을 맞추기 위해, purrr 패키지의 map() 함수를 다시 사용합니다. 먼저, group_split()을 적용하여 playerID를 그룹화 변수로 설정하고, 각 선수의 데이터에 대해 개별적으로 모델을 맞춥니다. 출력된 models는 모든 선수의 계수를 포함하는 데이터 프레임이며, 각 행은 특정 선수를 나타냅니다.
batting_2000_grouped <- batting_2000 |>
group_by(playerID)
ids <- batting_2000_grouped |>
group_keys() |>
pull(playerID)
models <- batting_2000_grouped |>
group_split() |>
map(~lm(OPS ~ I(Age - 30) + I((Age - 30)^2), data = .)) |>
map(tidy) |>
set_names(ids) |>
bind_rows(.id = "playerID")
$Peak_-age = 30 - B/(2C)$ 공식을 사용하여 모든 선수의 추정 정점 나이를 계산합니다. 새로운 변수 Peak_age를 데이터 프레임 beta_coefs에 추가합니다.
beta_coefs <- models |>
group_by(playerID) |>
summarize(
A = estimate[1],
B = estimate[2],
C = estimate[3]
) |>
mutate(Peak_age = 30 - B / 2 / C) |>
inner_join(midcareers, by = "playerID")
시간에 따른 정점 나이의 패턴
야구 역사에 따라 정점 나이가 어떻게 변하는지 조사하기 위해, ggplot() 함수를 사용하여 Peak_age를 Midyear에 대해 산점도로 구성합니다. 산점도만으로는 일반적인 패턴을 보기 어려우므로, geom_smooth() 함수를 사용하여 부드러운 곡선을 맞추고 이를 플롯에 추가합니다 (그림 1 참조).
age_plot <- ggplot(beta_coefs, aes(Midyear, Peak_age)) +
geom_point(alpha = 0.5) +
geom_smooth(color = "red", method = "loess") +
ylim(20, 40) +
xlab("Mid Career") + ylab("Peak Age")
age_plot
그림 1에서는 시간이 지남에 따라 정점 나이가 점진적으로 증가하는 것을 볼 수 있습니다. 평균 선수를 기준으로 할 때, 1880년에는 정점 나이가 약 27세였으며, 이 평균값이 1880년부터 2016년까지 점차 28세로 증가했습니다.
정점 나이와 커리어 타석 수
선수의 정점 나이와 커리어 타석 수 간에 관계가 있는지 알아보기 위해, ggplot2를 사용하여 Peak_age를 커리어 타석 수 변수 AB_career의 로그(밑 2)에 대해 그래프로 구성합니다. 타석 수를 로그 스케일로 표시하여 모든 가능한 값에 대해 점들이 더 고르게 퍼지도록 합니다. 다시 한번, 패턴을 확인하기 위해 LOESS 부드럽게 곡선을 그래프에 추가합니다 (그림 2 참조).
age_plot +
aes(x = log2(AB_total)) +
xlab("Log2 of Career AB")
여기서 명확한 관계를 볼 수 있습니다. 커리어가 상대적으로 짧고 2000개의 커리어 타석을 가진 선수들은 보통 약 27세에 정점을 찍는 경향이 있습니다. 반면, 커리어가 긴 선수들—예를 들어 9000개 이상의 타석을 가진 선수들은—정점 나이가 30세에 가까운 경향이 있습니다.
궤적과 수비 위치
선수들을 비교할 때, 일반적으로 동일한 수비 위치에 있는 선수들을 비교하고자 합니다. POS라는 주요 수비 위치 변수는 이미 정의되어 있으며, 이 변수를 사용하여 포지션에 따라 분류된 선수들의 정점 나이를 비교합니다.
1985년과 1995년 사이의 중간 경력에 있는 선수들을 고려한다고 가정해 보겠습니다. filter() 함수를 사용하여, 이러한 선수들만 포함된 새로운 데이터 프레임 Batting_2000a를 생성합니다.
batting_2000a <- batting_2000 |>
filter(Midyear >= 1985, Midyear <= 1995)
map() 함수를 또 한 번 사용하여 batting_2000a 데이터 프레임의 선수들에 대한 궤적 데이터에 2차 곡선을 맞추고, 이 2차 피팅 결과를 models 객체에 저장합니다. summarize()와 mutate() 함수를 사용하여 회귀 분석 결과를 요약합니다. 출력된 데이터 프레임 beta_estimates에는 추정된 계수 A, B, C, 선수의 추정 정점 나이 Peak_age, 그리고 수비 위치 Position이 포함됩니다.
batting_2000a_grouped <- batting_2000a |>
group_by(playerID)
ids <- batting_2000a_grouped |>
group_keys() |>
pull(playerID)
models <- batting_2000a_grouped |>
group_split() |>
map(~lm(OPS ~ I(Age - 30) + I((Age - 30)^2), data = .)) |>
map(tidy) |>
set_names(ids) |>
bind_rows(.id = "playerID")
beta_estimates <- models |>
group_by(playerID) |>
summarize(
A = estimate[1],
B = estimate[2],
C = estimate[3]
) |>
mutate(Peak_age = 30 - B / 2 / C) |>
inner_join(midcareers) |>
inner_join(Positions) |>
rename(Position = POS)
주요 수비 위치에 초점을 맞추되, 투수와 지명 타자는 제외합니다. filter() 함수를 사용하여 이러한 다른 위치를 제거합니다. 이후 inner_join() 함수를 사용하여 궤적 정보와 수비 정보, 그리고 People 정보를 결합하고, 결합된 정보를 beta_fielders 데이터 프레임에 저장합니다.
beta_fielders <- beta_estimates |>
filter(
Position %in% c("1B", "2B", "3B", "SS", "C", "OF")
) |>
inner_join(People)
수비 위치에 따른 선수들의 정점 나이를 그래프 화하기 위해 스트립 차트를 사용합니다 (그림 3 참조). 일부 정점 나이 추정치가 합리적이지 않은 값이므로, 수평 축의 범위는 20에서 40으로 설정합니다.
ggplot(beta_fielders, aes(Position, Peak_age)) +
geom_jitter(width = 0.2) + ylim(20, 40) +
geom_label_repel(
data = filter(beta_fielders, Peak_age > 37),
aes(Position, Peak_age, label = nameLast)
)
일반적으로, 1990년대 선수들의 경우, 모든 수비 위치에서 정점 나이는 27세에서 32세 사이에 위치하는 경향이 있습니다. 정점 나이 추정치의 변동성은 타자의 경력 궤적 형태가 다르다는 사실을 반영합니다. 특히, 정점 나이 추정치가 높은 세 명의 외야수와, 정점 나이 추정치가 두드러진 포수는 없습니다. 37세 이후에 정점을 찍은 여섯 명의 하이라이트 선수로는 안드레스 갈라라가, 랜디 레디, 에릭 데이비스, 토니 필립스, 짐 아이즌라익, 알바로 에스피노자가 있습니다.
'야구 분석 > R' 카테고리의 다른 글
R을 이용한 하프 이닝 시뮬레이션 2 (0) | 2024.09.29 |
---|---|
R을 이용한 하프 이닝 시뮬레이션 1 (0) | 2024.09.26 |
메이저리그 선수들의 커리어 살펴보기 (2) | 2024.09.08 |
미키 맨틀의 커리어 살펴보기 (0) | 2024.08.22 |
포수 프레이밍이란? (0) | 2024.08.14 |