← 포트폴리오 홈

MLB 2025 · 데이터 분석 프로젝트 #1

이도류의 부하
오타니는 투수를 병행하면서
타격을 유지했는가?

2025년 오타니 쇼헤이는 팔꿈치 수술 후 투수로 복귀하면서 동시에 타자로 158경기 55홈런을 기록했다. 사람의 몸은 하나인데 두 개의 일을 했다. 그 부하가 타격에 흔적을 남겼는지, 경기 단위 · 선수 단위 · 투구 단위 데이터 3종을 모두 붙여서 확인했다. 아래 필터로 직접 잘라 보세요 — 모든 숫자가 그 조건으로 다시 계산됩니다.

① 경기 단위 · 시즌 집계 + 팀 경기 로그 ② 선수 단위 · 트래킹 측정값 4종 ③ 투구 단위 · Statcast

한 줄 결론

시즌 전체로는 유지했다.
다만 등판 다음날 하루가 눌렸다.

* 위 KPI의 시즌 집계는 경기 단위 데이터(정규시즌 기준)로 필터와 무관하게 고정입니다. 이하 차트는 모두 현재 필터로 다시 계산됩니다.

SECTION 01 — 부하의 크기

복귀는 한 번에 오지 않았다

첫 등판은 28구 1이닝. 마지막은 100구 7이닝. 18번의 등판은 계단처럼 설계되어 있었다. 구종 필터를 켜면 그 계단 안에서 어떤 공을 늘려갔는지가 보인다.

등판별 투구수

회색 트랙은 그 등판의 전체 투구수, 색칠된 부분이 현재 필터에 해당하는 투구수

ACWR — 급성 부하 ÷ 만성 부하 경기 구분만 적용

최근 7일 투구수를 최근 28일 평균 주간 투구수로 나눈 값. 스포츠 과학에서 0.8~1.3을 관리 구간으로 본다. 부하는 총량 개념이라 구종·타자 필터를 적용하지 않는다.

읽은 것

SECTION 02 — 어디로 던졌나

스트라이크 존 위에 찍은 모든 공

2025년에 던진 공을 전부 포수 시점의 스트라이크 존 위에 찍었다. 위 필터에서 구종을 하나만 켜고 좌타/우타를 바꿔보면, 같은 구종을 타자 유형에 따라 어디에 넣는지가 드러난다. 범례를 클릭하면 결과별로 켜고 끌 수 있다.

투구 로케이션

가로축 = 좌우 위치(ft), 세로축 = 높이(ft) · 사각형은 평균 스트라이크 존

구종별 헛스윙률

스윙 10회 이상인 구종만 · 헛스윙 ÷ 전체 스윙

읽은 것

SECTION 03 — 핵심 질문

등판 다음날, 방망이가 무거웠다

타석을 등판일로부터 며칠 지났는지로 나눠 묶었다. 비교를 공정하게 하려고 복귀 전 기간은 제외했다. 위 필터의 타격 지표를 바꾸면 같은 구간을 다른 눈으로 볼 수 있다 — 결과(wOBA)가 아니라 스윙의 질(배트 스피드 · 헛스윙률)로 보면 이야기가 조금 달라진다.

등판 시점별

⚠ 표본이 작다 — 정직하게 적어둔다

복귀 전 vs 복귀 후

투수를 병행하기 시작한 시점을 기준으로 시즌을 반으로 자른 비교

팀은 어땠나

경기 단위 데이터 · LA 다저스

SECTION 04 — 시간에 따라

투구 부하가 쌓이는 동안 타격은

같은 x축(월)을 두 개의 패널로 나눠 그렸다. 단위가 다른 두 지표를 하나의 그래프에 두 개의 y축으로 겹쳐 그리면 없는 상관관계가 보이기 때문에, 일부러 분리했다.

월별 투구수 (위) / 월별 (아래)

3월(개막)과 11월(월드시리즈)은 표본이 작아 해석에 주의

SECTION 05 — 다른 투수가 되어 돌아왔다

스위퍼의 투수에서 패스트볼의 투수로

부상 직전 시즌(2023)과 복귀 시즌(2025)의 구종 구성을 비교했다. 2023년은 비교 기준이라 필터를 적용하지 않고, 2025년만 현재 필터로 다시 계산한다.

구종 구사 비율 2025만 필터 적용

점의 이동이 곧 변화량 · 구종 필터에서 꺼진 구종은 흐리게 표시

읽은 것

SECTION 06 — 그래서 얼마나 잘한 건가

부하를 지고도 리그 최상위였다

선수 단위 트래킹 데이터로 리그 안에서의 위치를 확인했다. 백분위 100 = 리그 1위. 이 절이 있어야 "유지했다"가 "그저 안 떨어졌다"가 아니라 "최상위를 지켰다"가 된다.

타자 오타니의 리그 백분위 시즌 집계 · 필터 영향 없음

선수 단위 측정 데이터 4종 · 각 지표의 리그 표본 수는 지표 아래에 표기

투수 오타니의 리그 백분위 시즌 집계 · 필터 영향 없음

경기 단위 시즌 집계 · 10선발 이상 투수들 안에서의 위치

SECTION 07 — 방법과 한계

어떻게 만들었고, 무엇을 믿으면 안 되는가

파이프라인

수집 → 적재 → 정제 → 가공 → 제공

  • 수집·적재 — 제공된 CSV 3계층 11개 파일을 원본 그대로 data/에 보관. 원본은 수정하지 않았다.
  • 정제 — 시범경기(game_type=S)와 연습경기(E, F)를 제외하고 정규시즌 · 포스트시즌만 남겼다.
  • 가공analyze.py가 집계값과 함께 '원자 데이터'data.json으로 떨어뜨린다. 필터가 동작하려면 미리 합쳐놓은 숫자가 아니라 더 이상 못 쪼개는 단위(투구 1개, 경기 1개)가 필요하다.
  • 제공build.py가 그 JSON을 HTML에 심는다. 필터를 바꾸면 브라우저가 그 자리에서 다시 집계한다. 서버도, 외부 차트 라이브러리도 쓰지 않는다.

검증 — 차트의 숫자를 원본과 대조했다

필터 로직도 같은 방식으로 확인한다 — 필터를 '전체'로 두면 브라우저의 재집계 결과가 파이썬 집계값과 소수점까지 일치해야 한다. 이 페이지는 로드할 때마다 그 대조를 실행하고 결과를 브라우저 콘솔에 남긴다([필터 자기검증]).

이 분석의 한계

  • 표본이 작다. 등판 다음날 타석은 39개다. 야구에서 39타석은 "경향"이라고 부를 수 있지만 "증명"이라고 부를 수는 없다. 필터를 좁힐수록 이 문제는 커지므로, 모든 차트에 표본 수를 함께 적었고 25타석 미만 구간은 상단에 경고를 띄운다.
  • 등판 다음날이 9경기뿐이다. 등판 후 휴식일 · 원정 이동이 겹쳐 다음날에 경기가 없는 경우가 많았다. 이 그룹은 애초에 특수한 날들의 모음이다.
  • 인과가 아니다. 등판 다음날 성적이 낮은 것이 피로 때문인지, 상대 선발 · 구장 때문인지 이 데이터만으로는 분리할 수 없다.
  • 선수 단위 arm_strength는 쓰지 못했다. 야수 송구만 측정하는 데이터라 투수 오타니의 기록이 아예 없다(투수 0명). 있을 것 같은 데이터가 없는 것도 결과의 일부라서 적어둔다.
  • 배트 스피드 평균이 두 종류다. 선수 단위 데이터는 75.8mph(정상 스윙만), 투구 단위에서 직접 계산하면 74.1mph(전체 스윙)이다. 정의가 다르므로 서로 비교하지 않고 각각의 맥락에서만 썼다.
  • 좌우 위치는 포수 시점이다. 로케이션 차트 가로축의 부호는 중계 화면 기준이 아니라 측정 좌표계 기준이다.

다음에 해볼 것

  • 등판 다음날 상대 선발의 수준을 통제한 뒤 다시 비교하기
  • 2021~2023년(이도류 전성기)에도 같은 D+1 패턴이 있었는지 확인 — 있으면 피로 가설이 강해진다
  • ACWR을 투구수 대신 고강도 투구수(97mph 이상)로 계산해 부하의 질을 반영하기
  • 로케이션을 존 9칸으로 나눠 구종별 제어력 지표를 만들고, 등판 회차에 따라 좋아졌는지 보기