728x90
반응형
아래 글에서 이어집니다.
2023 KBO 선발투수 HEATMAP 시각화 프로젝트 (1) - 데이터 크롤링 및 전처리
데이터 전처리 및 크롤링 수정
앞선 글에서 데이터 크롤링과 전처리를 수행했지만, 미흡했던 부분이 있어 그 부분을 수정하고 진행했다.
1. 데이터 전처리
KBO 사이트에서 받은 날짜를 (ex. 04.01) DATE의 형식 (ex. 2023-04-01) 으로 바꾸어주었다.
def float_to_date(date):
str_daily = str(date).split('.')
pdate = "2023-"+str_daily[0]+"-"+str_daily[1]
if len(str_daily[1]) == 1:
pdate += "0"
return pdate
df['날짜'] = df['날짜'].apply(float_to_date)
2. 로직 수정
기존 로직에서는 "이닝 종료"가 "이후상황"에 들어가있으면 이닝이 종료된 것으로 보고 해당 이닝에 대한 정보를 저장하는 방식으로 진행했다. 하지만, 몇몇 이닝들이 누락되는 문제가 있었고 이는 투수가 직접 "이닝 종료"를 하지 않은 경우들에 대해 누락되었던 것이다. (ex. 도루 실패 등)
따라서, 이닝이 바뀌었을 때 데이터를 저장하도록 로직을 수정하였다.
score_1 = ["0:0", 1]
top_or_bottom = "초"
inning = 1
home_team, away_team = "", ""
score = 0
for idx, row in tables[1].iterrows():
# 초 공격인지 / 말 공격인지 체크 & 이닝 체크
if "초" in row['이닝']:
top_or_bottom = "초"
inning = int(row['이닝'][0])
else:
top_or_bottom = "말"
inning = int(row['이닝'][0])
# 이닝 끝날 때마다 실점 갱신
if score_1[1] != inning:
score_2 = score_1[0].split(":")
score_3 = row['이전상황'].split(' ')[1].split(":")
if top_or_bottom == "초":
score = int(score_3[0]) - int(score_2[0])
home_team, away_team = team, row['상대']
else:
score = int(score_3[1]) - int(score_2[1])
home_team, away_team = row['상대'], team
score_1 = [row['이전상황'].split(' ')[1], inning]
db, score = db_append(db, row, inning-1, player, score, home_team, away_team, stadium_db)
승계 주자 처리 로직은 그대로 유지하였다.
데이터 시각화 (워크시트)
태블로를 활용하여 시각화를 진행한다. 일일이 다 설명할 수는 없어서 중요 포인트들만 정리해보았다.
1. 데이터 관계 설정
- 태블로에서는 데이터간의 관계를 설정함으로써 데이터를 결합시킬 수 있다. 위와 같이 필드를 정의한다.
2. 상단 막대
게임별로 실점을 얼마나 했는지 막대로 수치화하는 부분이다. 점을 통해 퀄리티 스타트 여부도 표시해준다.
- 열 : 각 게임을 구분하기 위해 날짜로 설정한다.
- 행 : 해당 경기의 실점을 나타내기 위해 R로 설정한다.
- 필터는 선수명과 구분을 통해 원하는 선수와 선발로 던진날짜가 나타나도록 설정한다.
- 퀄리티스타트시 점이 나타나도록 설정하기 위해 계산된 필드를 아래와 같이 정의한다.
- IP_to_float는 이닝을 소수형태로 바꾼 값
- R은 실점
IF [IP_to_float]>=6 AND [R] <=3 THEN '●' END
- 그 후, 레이블에 해당 필드를 마우스오버하면 위와 같이 표시된다.
- 커서를 가져다대면 해당 경기에 대한 정보가 나타나도록 설정한다.
- 따라서, 도구설명에 나타내고자 하는 정보들을 와와 같이 마우스오버한다.
- 구장, IP, ER, H, SO 등은 그대로 사용하면 된다.
- 사사구는 "BB+HBP"로 정의된 계산된 필드를 만든다.
- 상대_FULL_NAME은 IF ELSEIF 문을 활용해 일일이 매핑해준다.
IF [상대] == 'KT' THEN 'KT 위즈'
ELSEIF [상대] == '롯데' THEN '롯데 자이언츠'
ELSEIF [상대] == 'LG' THEN 'LG 트윈스'
ELSEIF [상대] == 'KIA' THEN '기아 타이거즈'
ELSEIF [상대] == 'SSG' THEN 'SSG 랜더스'
ELSEIF [상대] == '한화' THEN '한화 이글스'
ELSEIF [상대] == '삼성' THEN '삼성 라이온즈'
ELSEIF [상대] == '두산' THEN '두산 베어스'
ELSEIF [상대] == 'NC' THEN 'NC 다이노스'
ELSEIF [상대] == '키움' THEN '키움 히어로즈'
END
3. 하단 막대
히트맵에 해당하는 부분이다.
- 열: 상단막대와 동일하게 날짜를 설정한다.
- 행: 이닝을 설정한다.
- 그리고 필터를 통해 투수를 설정하고 색상에 실점을 마우스오버하여 실점의 수에 따라 색상이 변화하도록 설정한다.
4. 우측 막대
이닝별로 실점을 수치화한 부분이다.
- 열 : 실점을 나타낸다.
- 행 : 이닝을 나타낸다.
※ 주의할 점
일반적으로 선수마다 실점의 최댓값에 맞게 자동으로 축 범위가 설정되어 있다. 따라서 선수마다 비교가 가능하도록 상단 막대와 우측 막대의 축 범위를 따로 설정해주어야 한다.
5. 선수 기록
대시보드에 나타낼 선수 기록을 계산하는 시트를 생성한다. 수치를 일일이 적는 것보다 데이터를 통해 계산하는 것이 낫다고 판단했다.
- 삼진에 해당하는 SO를 제외하면 계산된 필드를 생성해주어야 한다.
- 승과 패는 아래처럼 계산하였다.
COUNT(IF [결과] == "승" then 1 END)
- 평균자책점과 WHIP를 계산하기 위해서는 총 이닝이 필요하다.
- 이닝에 해당하는 IP가 분수형태로 되어 있어서 IP_to_float를 정의했다. (ex. 5 3/1 -> 5.1)
- IP_to_float를 가지고 정확한 이닝을 나타내도록 수정하였다. (ex. 5.1 -> 5.3)
- 평균자책점과 WHIP 모두 정의된 식에 맞게 계산한다.
# 평균자책점
SUM([ER])*9/(INT([총 이닝]) +3*([총 이닝]- INT([총 이닝])))
# WHIP
(SUM([BB])+SUM([H]))/(INT([총 이닝]) +3*([총 이닝]- INT([총 이닝])))
- QS는 기존에 생성한 QS_DOT을 카운트한다.
데이터 시각화 (대시보드)
원하는 선수마다 시트를 생성했다면 대시보드를 생성할 차례이다.
1. 타이틀
- 제일 상단에 타이틀을 적는다.
- 왼쪽에 KBO 로고를 삽입한다.
- 기본 색상은 KBO를 상징하는 짙은 남색으로 설정했다.
2. 대시보드 설명
- 아래에 간단한 대시보드 설명을 적는다.
3. 팀별 선수 시각화
- 팀은 타이틀과 같은 형태로 표현한다.
- 팀의 로고 삽입하고 옆에 팀명을 적는다.
- 정규 시즌 순위순으로 팀을 배치였다.
- 선수 이미지와 기존에 생성한 시트를 알맞게 배치한다.
- 시각화 색상은 각 팀을 대표하는 색상으로 하였다. 위는 그에 대한 예시이다.
간단 분석
시각화를 했으면 분석을 해야하지 않겠는가... 간단히 분석해보았다.
1. LG 트윈스 & KT 위즈
- 임찬규 선수의 시즌 초반 페이스가 굉장히 좋다. 왜 불펜에서 시작해서 선발로 자리잡을 수 있었는지 알 수 있다.
- 한번씩 크게 무너지는데, 그래도 주기적(?)으로 무너져서 괜찮은 듯(?)하다.
- 고영표 선수는 수많은 퀄리티 스타트(21)이 눈에 띈다.
- 실점이 적은 편인데 그나마 많은 이닝은 1회와 5회이다. (경기 초반인 1회와 선발투수 요건이 갖춰지는 5회는 누구에게나 어렵다.)
2. SSG 랜더스 & NC 다이노스
- 김광현 선수는 경기 초반(1회~3회)까지 실점이 매우 적다.
- 이상하게도 4회에 가장 실점이 많다. 다른 선수들과 비교해도 유독 심하다.
- 이번 시즌 KBO를 폭격했던 페디 선수다.
- 정말 꾸준히 잘던졌다. 퀄리티스타트도 21번이나 된다.
- 저 4회에 가장 빛나는 지점은 정보근 선수의 투런이 터진 순간이다.
3. 두산 베어스 & 기아 타이거즈
- 알칸타라 선수도 꾸준히 잘던졌다.
- 한번씩 크게 맞을때 빼곤 정말 잘던졌다.
- 퀄리티 스타트도 21번이나 된다.
- 이의리 선수는 이닝소화력이 다소 아쉽다.
- 따라서 퀄리티 스타트도 5번밖에 안된다.
- 아마 이의리 챌린지(만루만들고 꾸역꾸역 막아서 투구수가 늘어나는...)영향이 크지 않을까 싶다.
- 그래도 던질때만큼은 크게 무너지지 않으므로 앞으로 성장이 기대되는 투수!
4. 롯데 자이언츠 & 삼성 라이온즈
- 롯데 자이언츠의 반즈 선수!
- 초반에 엄청흔들리고 퐁당퐁당에 후반기 좀 잘던졌다...라고 느꼈었는데...
- 확실히 후반기에는 정말 잘해주었다는 것이 드러난다. 퀄리티 스타트도 18번이나 된다.
- 일본간다는 소리가 있던데... 기복은 있었지만 잘해주어서 고마운 선수다. 가을야구를 함께 못해서 아쉬울 뿐. 앞으로 승승장구하시길.
- 삼성을 지탱해준 뷰캐넌 선수이다.
- 퀄리티 스타트도 18번이나 될정도로 꾸준했다.
- 수치상으로는 3회에 조금 약했다.
5. 한화 이글스 & 키움 히어로즈
- 한화 이글스의 미래 문동주선수이다.
- 신인급선수인만큼 구단에서 관리를 해주어서 이닝 소화 자체는 적었다.
- 3회에 다소 약한 모습이다.
- 앞으로가 기대되는 선수!
- 키움의 대장군 안우진 선수이다.
- 경기별로 이닝별로 정말 꾸준한 선수다. 기복이 크게 없다.
- 퀄리티 스타트도 16번이나 된다.
- 비교적 경기 후반에 실점이 조금 있었는데, 올해 팀 사정상 조금 더 던지다가 실점이 생기지 않았나 그런 예상을 해본다.
결과 및 후기
1. 결과
아래는 최종 완성본 링크이다.
2. 후기
- 관심있고 좋아하는 야구에 대해 프로젝트를 해서 즐겁고 재밌었다.
- 데이터를 준비하고 시각화를 위한 준비하는 시간보다 시각화하는 시간이 생각보다 오래 걸렸다. (
미적 감각이 없어서 그런듯...) - 비교적 간단한 시각화라서 다음에는 대시보드에 동작같은걸 추가한 인터랙티브 시각화를 하고 싶다.
- 보완할 점
- 색상 강도에 일관성이 살짝 없는 점 (두산, 롯데 색상이 타 팀에 비해 좀 짙어보인다.)
- 디자인 (감이 안옴)
728x90
반응형
'프로젝트' 카테고리의 다른 글
ChatGPT에게 야구 지식 가르쳐주기 (1) - LangChain을 활용하여 RAG 구성하기 (0) | 2023.12.23 |
---|---|
KBO 타자 대시보드 시각화 프로젝트 (1) | 2023.12.02 |
2023 KBO 선발투수 HEATMAP 시각화 프로젝트 (1) - 데이터 크롤링 및 전처리 (1) | 2023.11.14 |
[프로젝트] numpy, pandas로 신경망 구현하여 프로야구 순위 예측하기 (0) | 2022.07.19 |