Skip to content

LOPES-HUFS/Data_Wrangling_for_KBO

Repository files navigation

Data Wrangling for KBO

이 스크립트는 KBO 자료를 수집, 정리하고 분석하고자 만들었습니다. 자료를 수집하는 링크는 다음과 같습니다.

https://www.koreabaseball.com/Schedule/GameCenter/Main.aspx

요구 사항

이 스크립트는 다음 것들이 컴퓨터에 설치되어 있어야 제대로 작동합니다. 설치 방법은 위키를 참고하세요. 아래 본 프로젝트의 위키를 참고하세요!

필요한 것들: 크롬(Chrome) 브라우져, Python 3.6 이상, 크롬드라이버, selenium, 파이선 패키지(requests, selenium, bs4, configparser, pandas, lxml)

사용법

우선 이 프로젝트를 다운 받습니다. 이 프로젝트의 메인 페이지 오른쪽 상단에 'Code' 버튼을 클릭 후 'Download ZIP' 버튼을 눌러 다운받으시면 됩니다. 만약, git을 설치하셨으면, git을 클론하시면 됩니다.

프로젝트를 다 받으셨으면, 터미널이나 윈도우 cmd에서 프로젝트를 받은 곳으로 이동합니다. 다음과 같은 파일이 있으면 잘 이동한 것입니다.

single_game.py
modifying_data.py
pasing_page.py

이 곳에서 파이선을 실행 합니다.

경기 자료를 다운 받는 방법

본격적을 경기 자료를 다운받아보겠습니다. 이제부터는 파이썬에서 실행하시면 됩니다. 필요한 것들을 import합니다.

import single_game
import modifying_data
import pandas as pd
import json

2020년 8월 1일, 한화 대 엘지 경기 자료를 다운받아 보겠습니다. 여기서 "20200801"은 경기 날짜, "HHLG0"은 한화, 엘지 1번째 경기를 뜻합니다. 파이썬에서 0부터 숫자를 시작해서 0이 첫번째 경기입니다.

HHLG_20200801 = single_game.get_data("20200801","HHLG0")

다운받은 경기 자료에 어떤 항목들이 있는지 살펴봅시다. 순서대로 확인하면 다음과 같습니다.

>>> list(HHLG_20200801['20200801_HHLG0'].keys())
['scoreboard', 'ETC_info', 'away_batter', 'home_batter', 'away_pitcher', 'home_pitcher']

그러면 다운받은 항목들을 하나 하나 살펴 봅시다.

>>> pd.DataFrame(HHLG_20200801['20200801_HHLG0']['scoreboard'])
     승패  1  2  3  4  5  6  7  8  9 10 11 12  R   H  E  B
0  한화    0  0  0  0  5  0  0  0  1  -  -  -  6   8  2  6
1  LG    6  0  0  1  0  0  0  2  -  -  -  -  9  15  0  2
>>> HHLG_20200801['20200801_HHLG0']['ETC_info']
{'결승타': '김현수(1회 2사 2루서 중전 안타)', '홈런': '노시환5호(5회3점 김윤식)', '2루타': ['김민성(1회', '홍창기(4회', '노수광(6회', '김현수(8회', '반즈(9회)'], '실책': ['노시환(1회', '하주석(3회)'], '도루': '노수광(9회)', '주루사': ['정주현(1회', '유장혁(3회', '홍창기(6회)'], '포일': '이해창(8회)', '폭투': ['서폴드2(1 2회', '김윤식(5회', '고우석(9회)'], '심판': ['정종수', '이계성', '박기택', '오훈규'], '구장': '잠실', '관중': '2,315', '개시': '18:00', '종료': '21:17', '경기시간': '3:17'}
>>> pd.DataFrame(HHLG_20200801['20200801_HHLG0']['away_batter']).head()
  포지션  선수명   1   2   3   4   5   6   7  8   9  타수  안타  타점  득점     타율   
0     이용규  4   0  유직   0  중안  1   0  0  우비   4   1   0   1  0.274  한화
1     정은원  1   0   0  1  4  삼진   0  0  삼진   4   0   0   1  0.250  한화
2      반즈  우비   0   0  중안  4   0  삼진  0  좌2   4   2   1   0  0.268  한화
3     김태균  중비   0   0  우비  좌안   0  3  0  중비   5   1   1   0  0.234  한화
4     최진행   0  3   0  우비  4   0  2  0   0   3   0   1   0  0.237  한화
>>> pd.DataFrame(HHLG_20200801['20200801_HHLG0']['home_batter']).head()
  포지션  선수명         1   2  3   4  5    6   7    8  9  타수  안타  타점  득점     타율   
0     홍창기  좌중안\/ 우안   0  0  중2  0   좌안   0   2  0   5   4   1   2  0.259  LG
1     오지환        삼진  좌비  0  1  0  우중안   0   4  0   4   1   0   1  0.288  LG
2     채은성        유땅  좌안  0  좌안  0   삼진   0  우중안  0   5   3   2   1  0.279  LG
3     김현수        중안  3  0  우안  0   2   0   좌2  0   5   3   2   1  0.346  LG
4     김민성        좌2  삼진  0  삼진  0    0  사구    0  0   3   1   1   1  0.309  LG
>>> pd.DataFrame(HHLG_20200801['20200801_HHLG0']['away_pitcher'])
   선수명   등판 결과            이닝  타자  투구수  타수  피안타  홈런  4사구  삼진  실점  자책  평균자책점   
0  서폴드   선발    5  9  0  3 1\/3  21   74  21   10   0    0   2   7   5   4.96  한화
1  김진욱  4.4  0  1  1  0  1 2\/3   6   26   6    1   0    0   2   0   0   3.95  한화
2  안영명  6.1  0  1  1  0       1   4   12   4    2   0    0   1   0   0   6.48  한화
3  윤대경  7.5  0  0  0  0       1   4   16   3    0   0    1   2   0   0   2.33  한화
4  강재민  8.9  0  0  0  0    2\/3   5   27   4    2   0    1   0   2   2   3.24  한화
5  김종수  8.5  0  0  0  0    1\/3   1    6   1    0   0    0   0   0   0   5.09  한화
>>> pd.DataFrame(HHLG_20200801['20200801_HHLG0']['home_pitcher'])
   선수명   등판  결과            이닝  타자  투구수  타수  피안타  홈런  4사구  삼진  실점  자책  평균자책점   
0  김윤식   선발   0  0  1  0  4 1\/3  22   90  17    5   1    5   1   5   5   7.52  LG
1  이정용  5.5     1  0  0  1 2\/3   7   31   6    1   0    1   3   0   0   2.70  LG
2  정우영  7.3  홀드  2  1  5       2   6   22   6    0   0    0   3   0   0   2.90  LG
3  고우석  9.9   0  0  0  2       1   5   24   5    2   0    0   1   1   1  10.29  LG

지금까지 single_game.get_data()을 이용하여 다운받은 자료를 살펴본 코드를 정리하면 다음과 같습니다.

# 경기 자료 다운
HHLG_20200801 = single_game.get_data("20200801","HHLG0")
# 어떤 테이블이 있는지 key 값을 통해 확인합니다.
list(HHLG_20200801['20200801_HHLG0'].keys())
# 도루를 비롯한 구장, 관중 수 등의 정보를 확인합니다.
HHLG_20200801['20200801_HHLG0']['ETC_info']
#경기의 점수 상황 판을 판다스를 이용해서 테이블로 확인합니다.
pd.DataFrame(HHLG_20200801['20200801_HHLG0']['scoreboard'])
# 원정팀 타자 정보를 테이블로 확인합니다.
pd.DataFrame(HHLG_20200801['20200801_HHLG0']['away_batter'])
# 홈팀 타자 정보를 테이블로 확인합니다.
pd.DataFrame(HHLG_20200801['20200801_HHLG0']['home_batter'])
# 원정팀 투수 정보를 테이블로 확인합니다.
pd.DataFrame(HHLG_20200801['20200801_HHLG0']['away_pitcher'])
# 원정팀 투수 정보를 테이블로 확인합니다.
pd.DataFrame(HHLG_20200801['20200801_HHLG0']['home_pitcher'])

2018 시즌 한화의 전체 경기 자료 데이터를 다운 받기

앞에서 살펴본 single_game.get_data()은 한 경기만을 다운받는 함수입니다. 그러면 이 함수를 이용해서 2018 시즌 한화의 전체 경기 자료 데이터를 다운받아 보겠습니다. 저희 팀에서 이미 이 데이터를 자료를 받기 위한 리스트를 Hanhwa_game_id_sample.csv에 정리해두었습니다. 이 파일을 가지고 2018 시즌 한화의 전체 경기 자료 데이터를 받아보겠습니다. 다음 코드로 다운받을 리스트를 가져오겠습니다. 리스트의 길이가 144이니 전체 경기수는 144경기가 되겠습니다. 2018 시즌 한 팀 당 경기 수가 144경기였습니다. 이 숫자는 시즌마다 변경되기도 합니다.

>>> import pandas as pd
>>> sample = pd.read_csv("./data/Hanhwa_game_id_sample.csv")
>>> sample.date = sample.date.astype(str)
>>> sample.date
0      20180324
1      20180325
2      20180327
3      20180328
4      20180329
         ...
139    20181004
140    20181006
141    20181009
142    20181010
143    20181013
Name: date, Length: 144, dtype: object

그러면 파이썬을 이용해서 받아보겠습니다. 시간이 생각보다 오래걸립니다.

>>> temp_full ={}
>>> for i in range(0,len(sample)):
...     temp_data = single_game.get_data(sample.date[i],sample.gameid[i])
...     temp_data = single_game.modify_data(temp_data)
...     temp_full.update(temp_data)
...
>>> len(temp_full.keys())
144

지금까지 다운받은 1년치 한화 경기를 json 파일로 저장해 줍니다.

temp_file_name = "./sample/Hanhwa_normalseason_2018.json"

with open(temp_file_name, 'w') as outfile:  
    json.dump(temp_full, outfile)

지금까지 '2018 시즌 한화의 전체 경기 자료 데이터'를 다운받기 위해 사용한 코드를 정리하면 다음과 같습니다.

sample=pd.read_csv("./data/Hanhwa_game_id_sample.csv")
sample.date=sample.date.astype(str)

temp_full ={}

for i in range(0,len(sample)):
    temp_data = main.get_data(sample.date[i],sample.gameid[i])
    temp_data = main.modify_data(temp_data)
    temp_full.update(temp_data)

temp_file_name = "./sample/Hanhwa_normalseason_2018.json"

with open(temp_file_name, 'w') as outfile:  
    json.dump(temp_full, outfile)

이제까지 한 것을 정리하여 한번에 다운받는 함수를 만들었습니다. 그리고 정리하는 함수도 만들었습니다. 이것들을 사용해 봅시다. 한화 경기 전체를 다운받는 것으로 연습하는 것은 무리가 있어서 2 경기만 다운 받는 것으로 바꿨습니다.

import pandas as pd

sample = pd.read_csv("./data/Hanhwa_game_id_sample.csv")
sample.date = sample.date.astype(str)

import games

temp = games.get_data(sample[0:2])
# 시간을 줄이이기 위해서 우선 2개만 다운받아서 처리하는 것으로 변경했습니다.
# 한화 경기 전체를 다운 받고 처리하려면 윗 줄 대신 아랫 줄 코드를 사용하세요
# temp = games.get_data(sample)
Hanhwa_scoreboard = games.making_scoreboard(temp)
Hanhwa_batter = games.making_batter(temp)
Hanhwa_pitcher = games.making_pitcher(temp)

지금까지 만든 것을 바로 사용해도 좋지만, 저장해서 필요할 때 사용하는 경우가 많으니 이를 저장하도록 하겠습니다. 보통 이런 자료는 csv형식으로 저장하는데, 실제로 KBO 전제 자료를 저장하다 보면, 너무 커지기 때문에, 자료를 압축하고 효율적으로 저장하는 parquet형식으로 저장하겠습니다. 이 형식에 관련 내용은 Apache Parquet을 보시면 됩니다. pandas에 이 형식으로 저장하는 기능, pandas.read_parquet을 제공하고 있으니 이를 사용해서 저장하보겠습니다.

pip3 install pyarrow

Hanhwa_scoreboard.to_parquet('./sample/Hanhwa_scoreboard_data_2018.parquet')
Hanhwa_batter.to_parquet('./sample/Hanhwa_batter_data_2018.parquet')
Hanhwa_pitcher.to_parquet('./sample/Hanhwa_pitcher_data_2018.parquet')

KBO 전체 경기 자료 데이터를 다운 받기

지금까지 배운 것을 토대로 KBO 전체 경기 자료 데이터를 다운받아 보겠습니다. 이 방법은 공식 홈페이지에 있는 자료를 다운받는 것입니다. 그렇기 때문에 수집한 자료이기 때문에 공식자료와는 다를 수 있습니다.

kbo_id_full=pd.read_csv("./data/KBO_gameid_full.csv")
kbo_id_full.date=kbo_id_full.date.astype(str)

full_data={}
for i in range(0,len(kbo_id_full)):
    temp_data = main.get_data(kbo_id_full.date[i],kbo_id_full.gameid[i])
    temp_data = main.modify_data(temp_data)
    full_data.update(temp_data)

temp_file_name = "./data/KBO_normalseason_full.json"

with open(temp_file_name, 'w') as outfile:  
    json.dump(full_data, outfile)

저장한 자료를 이용하는 몇 가지 방법

앞에서 만든 것을 이용하여 데이터를 살펴보는 방법을 몇 가지 소개해보겠습니다. 더 많은 방법은 다음 링크를 참고하세요! R과 python에서 pandas를 사용하는 방법을 소개하고자 합니다.

스코어보드를 이용한 자료 다루기

앞의 과정을 거쳐 만든 스코어보드 자료를 열어서 2010년부터 최근까지 두산, 한화, LG 에러 숫자를 비교하는 그래프를 그려보겠습니다. 우선 자료를 가져오겠습니다. 자료 확인 할 겸 2019년 승수를 뽑아보겠습니다.

import pandas as pd

scoreboard = pd.read_parquet('./sample/scoreboard.parquet')
scoreboard
sum(scoreboard[(scoreboard['팀'] == "한화")&(scoreboard['year'] == 2019)]["승패"] == "승")

그러면 본격적으로 세 팀의 에러 숫자를 뽑아봅아 pandas를 이용해서 정리해보겠습니다.

team_1 = []
team_2 = []
team_3 = []

for i in range(2010, 2021):
    temp = sum(scoreboard[(scoreboard['팀'] == "두산")&(scoreboard['year'] == i)]["R"])
    team_1.append(temp)
    temp = sum(scoreboard[(scoreboard['팀'] == "한화")&(scoreboard['year'] == i)]["R"])
    team_2.append(temp)
    temp = sum(scoreboard[(scoreboard['팀'] == "LG")&(scoreboard['year'] == i)]["R"])
    team_3.append(temp)

teams = {"Dusan": team_1, "hanhwa": team_2, "LG": team_3}
teams = pd.DataFrame(teams, index=list(range(2010, 2021)))
teams

이렇게 정리한 것을 Matplotlib을 이용하여 도표로 그려보겠습니다.

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline

teams.plot()