이름 하나로 시작한다. 그런데 이름만으로는 선수를 못 가린다. 랜디 존슨은 셋이다.
빅유닛이라 불린 좌완 랜디 존슨이 있고, 1980년대에 뛴 내야수 랜디 존슨이 있고, 1982년에 한 시즌만 뛴 랜디 존슨이 또 있다. 이름으로 조회하면 셋이 다 걸린다.
그래서 이름을 받으면 먼저 id 하나로 좁힌다. Lahman의 People 테이블을 뒤져 후보를 뽑고, 생년과 데뷔 연도, 소속팀, 대표 기록을 나란히 놓고 어느 쪽인지 고른다. id가 확정되지 않으면 다음 단계로 안 간다. 쓰는 키는 Chadwick 레지스터의 key_bbref다.
그 뒤로는 id만 쓴다. 위키데이터에서 외부 식별자를 가져올 때도 이름으로 묻지 않고 key_bbref 역조회로만 묻는다. 이름으로 물으면 동명이인 문서가 섞여 들어오고, 거기서 가져온 생몰 연도는 다른 사람 것이다.
처리할 선수는 data/roster.json에 {id, slug} 한 줄로 적는다. 이 한 줄을 적어야 엔진이 다음에 돌 때 그 선수를 집는다. 목록이 없거나 모양이 틀리면 실행이 멈춘다. 폴백을 두지 않았다. 목록을 못 읽었는데 0명 처리했다며 초록으로 끝나면 더 곤란하다.
투수는 통산 이닝으로 가른다
투수인지 야수인지 가르는 일이 생각보다 까다로웠다.
등판 수로 재면 안 된다. 대패한 경기에서 한 이닝 던지는 야수가 흔하다. 통산 1이닝에서 10이닝 사이인 선수가 2,029명이다. 호너스 와그너도 8이닝을 던졌고 호세 칸세코도 1이닝을 던졌다. 그들을 투수라고 할 수는 없다.
시즌 단위로도 해 봤다. 그해 등판이 야수 출장보다 많으면 투수로 본다는 식이다. 그러면 오타니가 빠진다. 그는 던진 날보다 친 날이 훨씬 많다.
결국 통산 투구 이닝으로 갔다. 문턱은 50이닝이다. 선발의 3분의 1 시즌이고 구원의 한 시즌 남짓이다. 재 보니 소일 등판과 진짜 투수 사이가 그 근처에서 비어 있었다. 와그너 8, 칸세코 1, 그리고 오타니 349, 루스 1,221, 존슨 4,135.
야수는 시즌으로 가른다
야수 쪽은 다른 걸 봐야 했다.
타석 수로는 안 갈린다. 지명타자가 없던 시대의 투수는 매 경기 타석에 섰다. 그레그 매덕스의 통산 타수가 1,591이다. 그가 타자였다는 말이 아니고 그 시대 규칙이 그랬다는 말이다.
루스는 다르다. 같은 몸으로 어떤 해는 마운드에 섰고 어떤 해는 우익수로 섰다. 갈리는 건 타석이 아니라 시즌이었다. 야수로 보낸 시즌이 한 해라도 있나를 묻는다. 루스 18시즌, 와그너 21시즌, 오타니 5시즌, 존슨과 매덕스는 0이다.
/** 투수인가 — **통산 투구 이닝**으로 잰다. */
export function isPitcher({ ipouts = 0 } = {}) {
return ipouts / 3 >= ROLES.PITCHER_MIN_IP
}
/** 야수인가 — **야수로 보낸 시즌이 한 해라도 있었나.** */
export function isBatter(seasons = []) {
return seasons.some((s) => (s.fielded ?? 0) > (s.pitched ?? 0))
}
포지션은 점유율로 고른다
지면의 신원 줄에 적는 포지션은 많이 선 자리부터 차례로 더해 가며 전체의 85%를 채울 때까지 고른다.
경기 수를 그대로 자르면 커리어 길이에 따라 뜻이 달라진다. 2,800경기를 뛴 선수의 200경기와 400경기를 뛴 선수의 200경기는 다른 이야기다. 점유율로 재면 그 선수 안에서 얼마나 자주 섰나가 나온다. 와그너는 유격수와 우익수와 1루수로 잡히고, 3루에서 뛴 209경기는 7.5%라 빠진다. 몰리나는 포수 하나, 조브리스트는 2루수와 우익수와 유격수로 잡힌다.
투수는 이 계산에 맡기지 않는다. 루스가 마운드에 선 163경기는 전체의 6.7%라 여기서 탈락한다. 그런데 그가 투수였다는 건 점유율로 따질 일이 아니다. 따로 판정해서 붙인다.
떨어진 재적은 쌍을 둘로 적는다
프론트매터의 teams[].years는 구간 쌍을 나열한다. 한 팀에 두 번 있었으면 쌍이 둘이다. 존슨의 애리조나가 [1999, 2004, 2007, 2008]인 게 그래서다.
min에서 max로 접으면 1999년부터 2008년까지 애리조나였다고 읽힌다. 그런데 같은 지면이 2005년과 2006년 소속을 뉴욕이라 적고 있다. 한 지면에 앞뒤가 안 맞는 이력이 같이 실린다. 그 사이에 찍은 사진도 애리조나 시절로 잡혀서 팀 등급이 어긋난다.
이렇게 떨어진 재적이 데이터베이스에 3,854쌍 있다. 예외가 아니다. 흔한 일이다.
구단은 혈통 코드로 가른다
팀 코드는 Lahman의 franchID를 쓴다. 프랜차이즈 혈통을 가리키는 값이라, 연고지를 옮긴 구단은 하나로 모이고 해체된 구단은 자기 코드를 지킨다. 세네터스와 트윈스의 모든 시즌이 MIN이고, 루이빌 콜로넬스는 LOU로 남는다.
이름이나 도시로 가르면 걸리는 자리가 여럿이다. 1901년의 볼티모어 오리올스는 아메리칸리그 팀이었는데 지금의 볼티모어가 아니라 양키스가 됐다. 1901년의 밀워키는 볼티모어로 이어졌고 지금의 밀워키와는 무관하다. 아메리칸리그 세인트루이스 브라운스도 볼티모어가 됐다. 1969년의 시애틀 파일럿츠는 매리너스와 이어지지 않고 밀워키가 됐다. 캔자스시티 애슬레틱스의 혈통은 오클랜드로 간다. 로열스는 다른 구단이다.
그해 소속팀이 어디인가에 지면 여러 곳이 기대고 있다. 발자취의 그 해 잉크 색, 날짜 블록에 붙는 팀 배지, 사진이 대표팀 시절 것인지. 그래서 재적 구간을 짓는 함수와 읽는 함수를 한 파일에 뒀다. 쓰는 모양과 읽는 모양이 갈리면 지면이 그해 소속팀을 틀리게 적는다.