반응형
In [1]:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import matplotlib.font_manager as fm
import os
import seaborn as sns
from IPython.display import display
import numpy as np
import re
import json
pd.options.display.max_columns = None
%matplotlib inline
# 한글 폰트
plt.rcParams['font.family'] = 'NanumGothic'
# 마이너스 깨짐 방지
matplotlib.rcParams['axes.unicode_minus'] = False
# 나눔 폰트 경로 설정
font_path = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf' # 나눔 고딕의 경로
font_prop = fm.FontProperties(fname=font_path).get_name()
# 한글 폰트 설정
matplotlib.rcParams['font.family'] = font_prop
# 전역 팔레트 설정 (예: 'Set2' 팔레트 사용)
sns.set_palette('Set2')
In [8]:
os.listdir('./datas')
Out[8]:
['Pre_Season_Batter.csv', 'Regular_Season_Batter.csv', 'Regular_Season_Batter copy.csv', 'submission.csv', 'Regular_Season_Batter_Day_by_Day_b4.csv']
데이터 살펴보기¶
In [9]:
pd.read_csv('./datas/Regular_Season_Batter_Day_by_Day_b4.csv').head()
Out[9]:
batter_id | batter_name | date | opposing_team | avg1 | AB | R | H | 2B | 3B | HR | RBI | SB | CS | BB | HBP | SO | GDP | avg2 | year | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 3.24 | NC | 0.333 | 3 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.333 | 2018 |
1 | 0 | 가르시아 | 3.25 | NC | 0.000 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.143 | 2018 |
2 | 0 | 가르시아 | 3.27 | 넥센 | 0.200 | 5 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.167 | 2018 |
3 | 0 | 가르시아 | 3.28 | 넥센 | 0.200 | 5 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.176 | 2018 |
4 | 0 | 가르시아 | 3.29 | 넥센 | 0.250 | 4 | 0 | 1 | 0 | 0 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 1 | 0.190 | 2018 |
In [10]:
pd.read_csv('./datas/Pre_Season_Batter.csv').head(1)
Out[10]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.55 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
In [11]:
pd.read_csv('./datas/Regular_Season_Batter.csv').head(1)
Out[11]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.339 | 50 | 183 | 27 | 62 | 9 | 0 | 8 | 95 | 34 | 5 | 0 | 9 | 8 | 25 | 3 | 0.519 | 0.383 | 9 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.902 |
In [12]:
pd.read_csv('./datas/submission.csv').head()
Out[12]:
batter_id | batter_name | |
---|---|---|
0 | 1 | 강경학 |
1 | 2 | 강구성 |
2 | 3 | 강민국 |
3 | 4 | 강민호 |
4 | 5 | 강백호 |
프리시즌 데이터 확인¶
In [13]:
preseason = pd.read_csv('./datas/Pre_Season_Batter.csv')
print(preseason.shape)
print()
print(preseason.info())
preseason.head()
(1393, 29) <class 'pandas.core.frame.DataFrame'> RangeIndex: 1393 entries, 0 to 1392 Data columns (total 29 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 batter_id 1393 non-null int64 1 batter_name 1393 non-null object 2 year 1393 non-null int64 3 team 1393 non-null object 4 avg 1393 non-null object 5 G 1393 non-null int64 6 AB 1393 non-null int64 7 R 1393 non-null int64 8 H 1393 non-null int64 9 2B 1393 non-null int64 10 3B 1393 non-null int64 11 HR 1393 non-null int64 12 TB 1393 non-null int64 13 RBI 1393 non-null int64 14 SB 1393 non-null int64 15 CS 1393 non-null int64 16 BB 1393 non-null int64 17 HBP 1393 non-null int64 18 SO 1393 non-null int64 19 GDP 1393 non-null int64 20 SLG 1393 non-null float64 21 OBP 1393 non-null float64 22 E 1393 non-null int64 23 height/weight 1393 non-null object 24 year_born 1393 non-null object 25 position 1393 non-null object 26 career 1393 non-null object 27 starting_salary 1179 non-null object 28 OPS 1393 non-null float64 dtypes: float64(3), int64(18), object(8) memory usage: 315.7+ KB None
Out[13]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 |
In [14]:
# 데이터 기초 통계량
'''
1. 선수는 총 1393명이 있다.
2. 데이터는 2002년부터 2018년까지 존재한다.
'''
preseason.describe()
Out[14]:
batter_id | year | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 | 1393.000000 |
mean | 173.434314 | 2013.014358 | 8.705671 | 19.201723 | 2.679828 | 5.021536 | 0.954774 | 0.119885 | 0.391960 | 7.391960 | 2.430725 | 0.629576 | 0.291457 | 1.877961 | 0.330223 | 3.714286 | 0.447236 | 0.353497 | 0.312206 | 0.381910 | 0.665703 |
std | 94.716851 | 4.166757 | 5.562686 | 13.395946 | 2.637212 | 4.232584 | 1.196904 | 0.379976 | 0.748557 | 6.538787 | 2.698122 | 1.146854 | 0.595522 | 2.053392 | 0.642204 | 3.180884 | 0.723364 | 0.271998 | 0.155947 | 0.729521 | 0.393702 |
min | 0.000000 | 2002.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 99.000000 | 2010.000000 | 6.000000 | 9.000000 | 1.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 0.200000 | 0.240000 | 0.000000 | 0.459000 |
50% | 178.000000 | 2014.000000 | 9.000000 | 18.000000 | 2.000000 | 4.000000 | 1.000000 | 0.000000 | 0.000000 | 6.000000 | 2.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 3.000000 | 0.000000 | 0.333000 | 0.324000 | 0.000000 | 0.670000 |
75% | 254.000000 | 2017.000000 | 11.000000 | 28.000000 | 4.000000 | 8.000000 | 2.000000 | 0.000000 | 1.000000 | 11.000000 | 4.000000 | 1.000000 | 0.000000 | 3.000000 | 1.000000 | 5.000000 | 1.000000 | 0.476000 | 0.400000 | 1.000000 | 0.865000 |
max | 344.000000 | 2018.000000 | 119.000000 | 183.000000 | 35.000000 | 51.000000 | 11.000000 | 4.000000 | 5.000000 | 68.000000 | 24.000000 | 9.000000 | 4.000000 | 21.000000 | 4.000000 | 36.000000 | 5.000000 | 4.000000 | 1.000000 | 5.000000 | 5.000000 |
In [15]:
'''
프리시즌은 연습경기이기 때문에 많은 데이터가 0쪽에 가깝다.
예를 들어 1년에 일반적인 정규타석을 채우지 않았고, 득전, 타수, 안타 등이 0에 가깝다.
또한 데이터의 양은 과거보다 최근에 가까워 질수록 많아진다.
'''
preseason.hist(figsize=(20,9))
plt.tight_layout()
plt.show()
In [16]:
'''
데이터의 개수가 적어서 그런가 대부분의 연도의 OPS의 중앙값이 비슷하다.
'''
plt.figure(figsize=(20,5))
sns.boxplot(x='year', y='OPS', data=preseason, showfliers=False)
Out[16]:
<Axes: xlabel='year', ylabel='OPS'>
In [17]:
'''
몇 몇 선수들을 추출해 OPS를 비교해 보았다.
그러던 도중 결측값이 있는 것을 발견하였다.
'''
preseason_df_2002 = preseason[preseason['year'] == 2002]
preseason_df_2002
Out[17]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
228 | 62 | 김주찬 | 2002 | 롯데 | 0.107 | 9 | 28 | 2 | 3 | 0 | 0 | 0 | 3 | 0 | 2 | 0 | 1 | 1 | 7 | 1 | 0.107 | 0.167 | 1 | 183cm/94kg | 1981년 03월 25일 | 내야수(우투우타) | 충암초(장충리틀)-충암중-충암고-삼성-롯데 | 18000만원 | 0.274 |
260 | 67 | 김태균 | 2002 | 한화 | 0.235 | 11 | 34 | 7 | 8 | 5 | 0 | 1 | 16 | 4 | 1 | 0 | 4 | 1 | 14 | 0 | 0.471 | 0.333 | 2 | 185cm/110kg | 1982년 05월 29일 | 내야수(우투우타) | 천안남산초-천안북중-북일고-(대전대) | 16000만원 | 0.804 |
413 | 109 | 박기혁 | 2002 | 롯데 | 0.000 | 8 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 0 | 0.000 | 0.000 | 0 | 179cm/77kg | 1981년 06월 04일 | 내야수(우투우타) | 대구수창초-성광중-대구상고-롯데 | 8000만원 | 0.000 |
467 | 119 | 박용택 | 2002 | LG | - | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 185cm/90kg | 1979년 04월 21일 | 외야수(우투좌타) | 고명초-휘문중-휘문고-고려대 | 30000만원 | 0.000 |
511 | 131 | 박한이 | 2002 | 삼성 | 0.319 | 11 | 47 | 7 | 15 | 3 | 0 | 1 | 21 | 4 | 3 | 0 | 3 | 0 | 8 | 1 | 0.447 | 0.360 | 0 | 182cm/91kg | 1979년 01월 28일 | 외야수(좌투좌타) | 초량초-부산중-부산고-동국대 | 30000만원 | 0.807 |
824 | 202 | 이대호 | 2002 | 롯데 | 0.359 | 11 | 39 | 4 | 14 | 3 | 0 | 1 | 20 | 4 | 0 | 0 | 1 | 0 | 8 | 1 | 0.513 | 0.375 | 1 | 194cm/130kg | 1982년 06월 21일 | 내야수(우투우타) | 부산수영초-대동중-경남고-(영남사이버대)-롯데-시애틀 | 21000만원 | 0.888 |
844 | 207 | 이범호 | 2002 | 한화 | 0.167 | 7 | 6 | 2 | 1 | 1 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 0 | 1 | 0 | 0.333 | 0.167 | 0 | 183cm/96kg | 1981년 11월 25일 | 내야수(우투우타) | 대구수창초-경운중-대구고-한화 | 11000만원 | 0.500 |
892 | 283 | 조동찬 | 2002 | 삼성 | 0.000 | 4 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/80kg | 1983년 07월 27일 | 내야수(우투우타) | 공주중동초-공주중-공주고 | 12000만원 | 0.000 |
984 | 234 | 이진영 | 2002 | SK | 0.297 | 11 | 37 | 5 | 11 | 2 | 0 | 2 | 19 | 6 | 2 | 2 | 5 | 0 | 12 | 0 | 0.514 | 0.381 | 0 | 185cm/90kg | 1980년 06월 15일 | 외야수(좌투좌타) | 군산초-군산남중-군산상고-쌍방울-SK-LG | 10000만원 | 0.895 |
1115 | 269 | 정상호 | 2002 | SK | 0.158 | 13 | 19 | 3 | 3 | 1 | 0 | 1 | 7 | 2 | 0 | 0 | 3 | 1 | 5 | 0 | 0.368 | 0.304 | 5 | 187cm/100kg | 1982년 12월 24일 | 포수(우투우타) | 석천초-동인천중-동산고-SK-상무-SK | 45000만원 | 0.672 |
1128 | 270 | 정성훈 | 2002 | KIA | 0.267 | 12 | 30 | 5 | 8 | 4 | 0 | 0 | 12 | 4 | 0 | 0 | 0 | 0 | 3 | 1 | 0.400 | 0.258 | 0 | 182cm/83kg | 1980년 06월 27일 | 내야수(우투우타) | 송정동초-무등중-광주제일고-해태-KIA-현대-우리-LG | 15000만원 | 0.658 |
1327 | 319 | 최형우 | 2002 | 삼성 | 0.000 | 5 | 5 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0.000 | 0.167 | 0 | 180cm/106kg | 1983년 12월 16일 | 외야수(우투좌타) | 진북초-전주동중-전주고-삼성-경찰-삼성 | 5000만원 | 0.167 |
프리시즌 결측값 채우기¶
In [18]:
'''
결측값의 개수를 확인해 보았다.
'''
for i, j in preseason.isnull().sum().items():
if not j == 0:
print(i, j)
'''
nan 값이 있는 행만 추출해 보았다.
그 중에서 starting_salary부분이 nan인 경우는 제외 하였다.
'''
preseason[preseason.isna().any(axis=1) & preseason['starting_salary'].notna()].head()
starting_salary 214
Out[18]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS |
---|
In [19]:
'''
장타율과 출루율이 계산되지 않아 OPS가 NaN이 나온것을 확인 하였다.
검색해 보니 장타율 같은 경우에는 (3루타 + 2루타 + 홈런) / 안타 로 계산하는데 이것은 TBS식 장타율이라고 한다.
다른 데이터와 비교하여 TBS식 장타율로 계산되었으면 이것도 같은 방법으로 계산하여 채우려고 한다.
'''
# 3루타, 2루타, 홈런, 안타를 추출하여 계산
print(f'clac_SLG: {preseason.loc[0, ["3B", "2B", "HR"]].sum() / preseason.loc[0, ["H"]]}')
print(f'orig_SLG: {preseason.loc[0, "SLG"]}')
print()
'''
계산해 보니 다르다.
알고보니 이것은 일본 TBS 테레비의 루즈밸트 게임 드라마에서 나온 잘못된 계산식이라고 한다.
조금 더 찾아보니 다른 식을 찾을 수 있었다.
(1루타(단타) x 1 + 2루타 x 2 + 3루타 x 3 + 홈런 x 4) / 타수
'''
# 안타, 2루타, 3루타, 홈런을 추출하여 각각 1, 2, 3, 4씩 곱하고 이후 타수만큼 나눈다.
cnt = 1
SLG = 0
for i in preseason.loc[0, ['H', '2B', '3B', 'HR']]:
SLG += i * cnt
cnt += 1
SLG = SLG / preseason.loc[0, "AB"]
print(f'clac_SLG: {SLG}')
print(f'orig_SLG: {preseason.loc[0, "SLG"]}')
print()
'''
약 0.1 정도의 오차가 발생하였다.
어디서 잘못 되었나 좀 더 찾아보니
단타는 총 안타에서 2, 3루타 그리고 홈런을 제외한 숫자를 의미한다.
그러면 단타는 안타의 개수는 7이 아니라 2루타와 홈런을 제외한 5가 된다.
이것을 기반으로 다시 계산하였다.
'''
H = preseason.loc[0, 'H'] - preseason.loc[0, ['2B', '3B', 'HR']].sum()
cnt = 2
SLG = H
for i in preseason.loc[0, ['2B', '3B', 'HR']]:
SLG += i * cnt
cnt += 1
SLG = SLG / preseason.loc[0, "AB"]
print(f'clac_SLG: {SLG}')
print(f'orig_SLG: {preseason.loc[0, "SLG"]}')
clac_SLG: H 0.285714 Name: 0, dtype: object orig_SLG: 0.55 clac_SLG: 0.65 orig_SLG: 0.55 clac_SLG: 0.55 orig_SLG: 0.55
In [20]:
'''
이제 저 계산식을 기반으로 NaN 값을 채우려고 한다.
이제 SLG는 다 채웠으니 OBP만 채우면 된다.
'''
def SLG_calc(row):
# SLG가 NaN이 아닐 경우 그대로 반환
if pd.notna(row['SLG']):
return row['SLG']
# 타수가 없는 경우
if pd.notna(row['AB']) or row['AB'] == 0:
return 0
H = row['H'] - row[['2B', '3B', 'HR']].sum()
SLG = (H + row['2B'] * 2 + row['3B'] * 3 + row['HR'] * 4) / row['AB']
return SLG
display(preseason.head())
print()
preseason['SLG'] = preseason.apply(SLG_calc, axis=1)
display(preseason.head())
for i, j in preseason.isnull().sum().items():
if not j == 0:
print(i, j)
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 |
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 |
starting_salary 214
In [21]:
'''
출루율의 계산 공식은 아래와 같다.
(안타 + 볼넷 + 사구) / (타수 + 볼넷 + 사구 + 희생 플라이)
하지만 주어진 데이터에는 희생 플라이는 존재하지 않는다.
그렇기 때문에 일단 0으로 두고 계산해 보았다.
이것으로 한 번 계산해 보았다.
'''
print(f'clac_OPS: {preseason.loc[0, ["H", "BB", "HBP"]].sum() / preseason.loc[0, ["AB", "BB", "HBP"]].sum()}')
print(f'orig_OPS: {preseason.loc[0, ["OBP"]]}')
'''
제대로 된 계산식임을 확인 하였기에 위와 똑같이 채워 주었다.
'''
def OBP_calc(row):
# OBP가 NaN이 아닐 경우 그대로 반환
if pd.notna(row['OBP']):
return row['OBP']
denominator = row[['H', 'BB', 'HBP']].sum()
numerator = row[['AB', 'BB', 'HBP']].sum()
# 분모 또는 분자가 0 인경우
if denominator == 0 or numerator == 0:
return 0
OBP = denominator / numerator
return OBP
preseason['OBP'] = preseason.apply(OBP_calc, axis=1)
display(preseason.head())
for i, j in preseason.isnull().sum().items():
if not j == 0:
print(i, j)
clac_OPS: 0.4090909090909091 orig_OPS: OBP 0.409 Name: 0, dtype: object
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 |
starting_salary 214
In [23]:
# OPS 계산
# OPS = SLG + OPB
def OPS_clac(row):
return row[['SLG', 'OBP']].sum()
preseason['OPS'] = preseason.apply(OPS_clac, axis=1)
display(preseason)
for i, j in preseason.isnull().sum().items():
if not j == 0:
print(i, j)
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1388 | 342 | 황재균 | 2014 | 롯데 | 0.407 | 10 | 27 | 3 | 11 | 2 | 0 | 1 | 16 | 4 | 1 | 0 | 2 | 0 | 5 | 0 | 0.593 | 0.448 | 1 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 1.041 |
1389 | 342 | 황재균 | 2015 | 롯데 | 0.333 | 11 | 30 | 8 | 10 | 3 | 0 | 0 | 13 | 6 | 0 | 0 | 4 | 0 | 3 | 0 | 0.433 | 0.389 | 0 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.822 |
1390 | 342 | 황재균 | 2016 | 롯데 | 0.310 | 16 | 42 | 8 | 13 | 3 | 1 | 0 | 18 | 4 | 3 | 1 | 4 | 0 | 4 | 0 | 0.429 | 0.370 | 1 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.799 |
1391 | 342 | 황재균 | 2018 | KT | 0.250 | 6 | 16 | 3 | 4 | 1 | 0 | 1 | 8 | 4 | 0 | 0 | 2 | 0 | 6 | 0 | 0.500 | 0.333 | 3 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.833 |
1392 | 344 | 황진수 | 2014 | 롯데 | 0.000 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 0 | 181cm/82kg | 1989년 02월 15일 | 내야수(우투양타) | 석천초-대헌중-공주고 | 4000만원 | 0.000 |
1393 rows × 29 columns
starting_salary 214
데이터 저장¶
In [19]:
preseason.to_csv('./datas/Pre_Season_Batter.csv', encoding='utf-8-sig', index=False)
In [24]:
# 불러오기
preseason = pd.read_csv('./datas/Pre_Season_Batter.csv')
for i, j in preseason.isnull().sum().items():
if not j == 0:
print(i, j)
starting_salary 214
연도별 선수 수 확인¶
In [25]:
'''
그렇다면 각 연도별 등록된 선수의 수는 어떻게 될지 한 번 살펴보았다.
'''
batter_count_preseason = preseason.groupby('year')['batter_id'].count().rename('preseason').reset_index()
batter_count_preseason
batter_count_preseason.plot(kind='line', x='year', y='preseason')
'''
최근으로 올 수록 등록된 선수의 수가 늘어나는 것을 볼 수 있다.
''';
In [26]:
'''
이제 프리시즌과 정규시즌의 상관관계를 보려고 한다.
프리시즌의 성적이 정규시즌에 영향을 미치는가?
프리시즌에 성적이 좋았던 선수들은 과연 정규시즌에서도 성적이 좋은지 확인하려고 한다.
먼저 정규시즌을 불러오고 결측값을 확인한 후 채우는 작업을 한다.
'''
regular_season = pd.read_csv('./datas/Regular_Season_Batter.csv')
regular_season.head()
Out[26]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.339 | 50 | 183 | 27 | 62 | 9 | 0 | 8 | 95 | 34 | 5 | 0 | 9 | 8 | 25 | 3 | 0.519 | 0.383 | 9 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.902 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 1 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 |
2 | 1 | 강경학 | 2014 | 한화 | 0.221 | 41 | 86 | 11 | 19 | 2 | 3 | 1 | 30 | 7 | 0 | 0 | 13 | 2 | 28 | 1 | 0.349 | 0.337 | 6 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.686 |
3 | 1 | 강경학 | 2015 | 한화 | 0.257 | 120 | 311 | 50 | 80 | 7 | 4 | 2 | 101 | 27 | 4 | 3 | 40 | 5 | 58 | 3 | 0.325 | 0.348 | 15 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.673 |
4 | 1 | 강경학 | 2016 | 한화 | 0.158 | 46 | 101 | 16 | 16 | 3 | 2 | 1 | 26 | 7 | 0 | 0 | 8 | 2 | 30 | 5 | 0.257 | 0.232 | 7 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.489 |
In [27]:
'''
타율, SLG, OBP에서 각각 결측값이 발견되었다.
'''
regular_season.isna().sum()
Out[27]:
batter_id 0 batter_name 0 year 0 team 0 avg 26 G 0 AB 0 R 0 H 0 2B 0 3B 0 HR 0 TB 0 RBI 0 SB 0 CS 0 BB 0 HBP 0 SO 0 GDP 0 SLG 26 OBP 24 E 0 height/weight 802 year_born 0 position 802 career 0 starting_salary 1067 OPS 26 dtype: int64
정규시즌 결측값 채우기¶
In [28]:
'''
먼저 SLG, OBP는 앞서 사용한 방식을 이용하여 채워주었다.
'''
def SLG_calc(row):
# SLG가 NaN이 아닐 경우 그대로 반환
if pd.notna(row['SLG']):
return row['SLG']
# 타수가 없는 경우
if pd.notna(row['AB']) or row['AB'] == 0:
return 0
H = row['H'] - row[['2B', '3B', 'HR']].sum()
SLG = (H + row['2B'] * 2 + row['3B'] * 3 + row['HR'] * 4) / row['AB']
return SLG
def OBP_calc(row):
# OBP가 NaN이 아닐 경우 그대로 반환
if pd.notna(row['OBP']):
return row['OBP']
denominator = row[['H', 'BB', 'HBP']].sum()
numerator = row[['AB', 'BB', 'HBP']].sum()
# 분모 또는 분자가 0 인경우
if denominator == 0 or numerator == 0:
return 0
OBP = denominator / numerator
return OBP
# SLG 결측값 채우기
regular_season['SLG'] = regular_season.apply(SLG_calc, axis=1)
# OBP 결측값 채우기
regular_season['OBP'] = regular_season.apply(OBP_calc, axis=1)
# 결측값 확인
print(regular_season.isna().sum())
batter_id 0 batter_name 0 year 0 team 0 avg 26 G 0 AB 0 R 0 H 0 2B 0 3B 0 HR 0 TB 0 RBI 0 SB 0 CS 0 BB 0 HBP 0 SO 0 GDP 0 SLG 0 OBP 0 E 0 height/weight 802 year_born 0 position 802 career 0 starting_salary 1067 OPS 26 dtype: int64
In [29]:
'''
이제 SLG와 OBP를 채워주었으니 둘을 이용해 OPS를 채워주었다.
'''
def OPS_clac(row):
# OBP가 NaN이 아닐 경우 그대로 반환
if pd.notna(row['OPS']):
return row['OPS']
OPS = row[['SLG', 'OBP']].sum()
return OPS
# OPS 결측값 채우기
regular_season['OPS'] = regular_season.apply(OPS_clac, axis=1)
# 결측값 확인
regular_season.isna().sum()
Out[29]:
batter_id 0 batter_name 0 year 0 team 0 avg 26 G 0 AB 0 R 0 H 0 2B 0 3B 0 HR 0 TB 0 RBI 0 SB 0 CS 0 BB 0 HBP 0 SO 0 GDP 0 SLG 0 OBP 0 E 0 height/weight 802 year_born 0 position 802 career 0 starting_salary 1067 OPS 0 dtype: int64
In [ ]:
'''
이제 타율을 채워주도록 하자
타율은 안타 / 타수로
AVG = H / AB 로 계산한다.
먼저 제대로 맞는지 확인해 보자
'''
print(regular_season.loc[0, 'H'] / regular_season.loc[0, 'AB'])
print(regular_season.loc[0, 'avg'])
'''
셋째 자리엣 반올림 하면 될 것 같다.
공식을 이용해서 적용했다.
'''
def AVG_calc(row):
# avg가 이미 채워진 경우
if pd.notna(row['avg']):
return row['avg']
# 타수 또는 안타가 0인 경우
if row['AB'] == 0 or row['H'] == 0:
return 0
avg = regular_season.loc[0, 'H'] / regular_season.loc[0, 'AB']
avg = round(avg, 3)
return avg
regular_season['avg'] = regular_season.apply(AVG_calc, axis=1)
# 결측값 확인
print(regular_season.isna().sum())
# 데이터 저장
regular_season.to_csv('./datas/Regular_Season_Batter.csv', encoding='utf-8-sig', index=False)
# 다시 불러오기
regular_season = pd.read_csv('./datas/Regular_Season_Batter.csv')
0.33879781420765026 0.339
In [31]:
'''
먼저 정규시즌도 프리시즌과 똑같이 기초통계량을 확인해 보았다.
'''
display(regular_season.describe())
'''
정규 시즌은 프리시즌과 다르게 선수의 수가 매우 많다.
기록된 연도 또한 93년 부터 18년까지 존재한다.
그렇다면 프리시즌과 비교할 때 문제가 생길 수 있으므로 정규시즌과 프리시즌 두 곳에 모두 있는 선수과 연도만 사용해야할 것 같다.
''';
batter_id | year | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 |
mean | 178.079462 | 2011.614507 | 0.235042 | 72.535045 | 201.514670 | 29.912388 | 55.988183 | 9.863488 | 0.957620 | 5.504075 | 84.279136 | 27.788509 | 5.290139 | 2.335778 | 20.943765 | 3.424613 | 38.596985 | 4.603504 | 0.340183 | 0.303684 | 3.676447 | 0.643868 |
std | 97.557947 | 4.992833 | 0.100894 | 45.093871 | 169.537029 | 28.778759 | 52.253844 | 9.871314 | 1.647193 | 7.989380 | 82.854200 | 29.602966 | 9.088580 | 3.194045 | 21.206113 | 4.132614 | 31.801466 | 4.713531 | 0.166238 | 0.115253 | 4.585248 | 0.268184 |
min | 0.000000 | 1993.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 101.250000 | 2008.000000 | 0.200000 | 28.000000 | 38.250000 | 5.000000 | 8.000000 | 1.000000 | 0.000000 | 0.000000 | 10.000000 | 3.000000 | 0.000000 | 0.000000 | 3.000000 | 0.000000 | 10.000000 | 1.000000 | 0.263158 | 0.269000 | 0.000000 | 0.541000 |
50% | 183.000000 | 2013.000000 | 0.253000 | 79.000000 | 163.000000 | 21.000000 | 40.000000 | 7.000000 | 0.000000 | 2.000000 | 56.000000 | 17.000000 | 2.000000 | 1.000000 | 14.000000 | 2.000000 | 33.000000 | 3.000000 | 0.358000 | 0.328000 | 2.000000 | 0.686000 |
75% | 265.000000 | 2016.000000 | 0.290000 | 115.000000 | 357.500000 | 49.000000 | 100.000000 | 16.000000 | 1.000000 | 8.000000 | 146.000000 | 44.000000 | 6.000000 | 3.000000 | 34.000000 | 5.000000 | 60.000000 | 7.000000 | 0.434946 | 0.367000 | 5.000000 | 0.797000 |
max | 344.000000 | 2018.000000 | 1.000000 | 144.000000 | 600.000000 | 135.000000 | 201.000000 | 47.000000 | 17.000000 | 53.000000 | 377.000000 | 146.000000 | 84.000000 | 21.000000 | 108.000000 | 27.000000 | 161.000000 | 24.000000 | 3.000000 | 1.000000 | 30.000000 | 4.000000 |
In [32]:
'''
각 시즌에서 타자이름, 연도를 이용하여 새로운 인덱스를 만들고, 그 인덱스들을 교집합하여 나온 데이터를 이용하려고 한다.
'''
regular_season['new_index'] = regular_season['batter_name'] + regular_season['year'].apply(str)
preseason['new_index'] = preseason['batter_name'] + preseason['year'].apply(str)
intersection_batter = list(set(regular_season['new_index']).intersection(set(preseason['new_index'])))
print('교집합 된 선수의 수: ',len(intersection_batter))
print(sorted(intersection_batter))
'''
처음에 이상했다.
분명 앞에서 프리시즌을 확인 했을 때는 344명이였는데 왜 1358명이라는 교집합 수가 나올까
그래서 다시 확인 했는데 count 수를 보지 않고 max 수를 보았다....
''';
교집합 된 선수의 수: 1358 ['가르시아2018', '강경학2011', '강경학2014', '강경학2015', '강경학2016', '강경학2017', '강구성2013', '강구성2016', '강구성2017', '강민국2014', '강민국2015', '강민국2016', '강민호2005', '강민호2006', '강민호2007', '강민호2008', '강민호2009', '강민호2010', '강민호2011', '강민호2012', '강민호2013', '강민호2014', '강민호2015', '강민호2016', '강민호2017', '강민호2018', '강백호2018', '강상원2017', '강상원2018', '강승호2016', '강승호2017', '강승호2018', '강진성2017', '강진성2018', '강한울2014', '강한울2015', '강한울2017', '강한울2018', '고종욱2011', '고종욱2015', '고종욱2016', '고종욱2017', '고종욱2018', '구자욱2015', '구자욱2016', '구자욱2017', '구자욱2018', '국해성2012', '국해성2015', '국해성2016', '국해성2017', '국해성2018', '권정웅2016', '권정웅2017', '권정웅2018', '권희동2013', '권희동2014', '권희동2017', '권희동2018', '김강민2003', '김강민2004', '김강민2005', '김강민2006', '김강민2007', '김강민2008', '김강민2009', '김강민2010', '김강민2011', '김강민2012', '김강민2013', '김강민2014', '김강민2015', '김강민2016', '김강민2017', '김강민2018', '김동엽2016', '김동엽2017', '김동엽2018', '김동욱2013', '김동욱2015', '김동욱2016', '김동욱2017', '김동욱2018', '김동한2013', '김동한2016', '김동한2017', '김동한2018', '김문호2007', '김문호2008', '김문호2011', '김문호2012', '김문호2013', '김문호2014', '김문호2015', '김문호2016', '김문호2017', '김문호2018', '김민성2007', '김민성2009', '김민성2010', '김민성2011', '김민성2012', '김민성2013', '김민성2014', '김민성2015', '김민성2016', '김민성2017', '김민성2018', '김민수2014', '김민수2018', '김민식2015', '김민식2016', '김민식2017', '김민식2018', '김민하2014', '김민하2015', '김사훈2012', '김사훈2013', '김사훈2014', '김사훈2017', '김사훈2018', '김상수2009', '김상수2010', '김상수2011', '김상수2012', '김상수2013', '김상수2014', '김상수2015', '김상수2016', '김상수2017', '김상수2018', '김상호2012', '김상호2016', '김상호2017', '김선빈2008', '김선빈2009', '김선빈2010', '김선빈2011', '김선빈2012', '김선빈2013', '김선빈2014', '김선빈2017', '김선빈2018', '김성욱2013', '김성욱2014', '김성욱2015', '김성욱2016', '김성욱2017', '김성욱2018', '김성현2006', '김성현2007', '김성현2008', '김성현2011', '김성현2012', '김성현2013', '김성현2014', '김성현2015', '김성현2016', '김성현2017', '김성현2018', '김성훈2018', '김용의2008', '김용의2012', '김용의2013', '김용의2014', '김용의2015', '김용의2016', '김용의2017', '김용의2018', '김응민2014', '김인태2017', '김재성2015', '김재율2012', '김재율2015', '김재율2017', '김재율2018', '김재현2014', '김재현2015', '김재현2016', '김재호2004', '김재호2005', '김재호2008', '김재호2009', '김재호2010', '김재호2011', '김재호2012', '김재호2013', '김재호2014', '김재호2015', '김재호2016', '김재호2017', '김재호2018', '김재환2008', '김재환2011', '김재환2014', '김재환2015', '김재환2016', '김재환2017', '김재환2018', '김주찬2002', '김주찬2003', '김주찬2004', '김주찬2008', '김주찬2009', '김주찬2010', '김주찬2011', '김주찬2012', '김주찬2013', '김주찬2014', '김주찬2015', '김주찬2016', '김주찬2017', '김주찬2018', '김주형2004', '김주형2005', '김주형2006', '김주형2007', '김주형2008', '김주형2011', '김주형2013', '김주형2014', '김주형2016', '김주형2017', '김주형2018', '김지수2010', '김지수2014', '김지수2015', '김지수2016', '김지수2017', '김지수2018', '김진곤2015', '김태균2002', '김태균2003', '김태균2004', '김태균2005', '김태균2006', '김태균2007', '김태균2008', '김태균2009', '김태균2012', '김태균2013', '김태균2014', '김태균2015', '김태균2016', '김태균2017', '김태균2018', '김태연2018', '김태완2006', '김태완2007', '김태완2008', '김태완2009', '김태완2010', '김태완2013', '김태완2014', '김태완2016', '김태완2017', '김태완2018', '김태진2016', '김하성2015', '김하성2016', '김하성2017', '김하성2018', '김헌곤2011', '김헌곤2012', '김헌곤2013', '김헌곤2014', '김헌곤2017', '김헌곤2018', '김현수2007', '김현수2008', '김현수2009', '김현수2010', '김현수2011', '김현수2012', '김현수2013', '김현수2014', '김현수2015', '김현수2018', '김혜성2017', '김혜성2018', '김회성2009', '김회성2010', '김회성2014', '김회성2015', '김회성2017', '나경민2017', '나경민2018', '나성범2014', '나성범2015', '나성범2016', '나성범2017', '나성범2018', '나원탁2017', '나원탁2018', '나종덕2017', '나종덕2018', '나주환2003', '나주환2004', '나주환2005', '나주환2006', '나주환2007', '나주환2008', '나주환2009', '나주환2010', '나주환2014', '나주환2015', '나주환2017', '나주환2018', '나지완2008', '나지완2009', '나지완2010', '나지완2012', '나지완2013', '나지완2014', '나지완2015', '나지완2016', '나지완2017', '나지완2018', '남태혁2016', '남태혁2017', '남태혁2018', '노수광2015', '노수광2016', '노수광2017', '노수광2018', '노진혁2013', '노진혁2014', '노진혁2015', '노진혁2018', '도태훈2016', '도태훈2017', '도태훈2018', '러프2017', '러프2018', '로맥2018', '로하스2018', '류지혁2016', '류지혁2017', '류지혁2018', '모창민2008', '모창민2009', '모창민2010', '모창민2013', '모창민2014', '모창민2015', '모창민2017', '모창민2018', '문규현2004', '문규현2007', '문규현2009', '문규현2010', '문규현2011', '문규현2012', '문규현2013', '문규현2014', '문규현2015', '문규현2016', '문규현2017', '문규현2018', '문선재2013', '문선재2014', '문선재2015', '문선재2016', '문선재2017', '민병헌2006', '민병헌2007', '민병헌2008', '민병헌2009', '민병헌2010', '민병헌2013', '민병헌2014', '민병헌2015', '민병헌2016', '민병헌2017', '민병헌2018', '박건우2013', '박건우2014', '박건우2015', '박건우2016', '박건우2017', '박건우2018', '박경수2003', '박경수2004', '박경수2005', '박경수2006', '박경수2007', '박경수2008', '박경수2009', '박경수2010', '박경수2011', '박경수2015', '박경수2016', '박경수2017', '박경수2018', '박광열2015', '박광열2016', '박광열2017', '박광열2018', '박기혁2002', '박기혁2003', '박기혁2004', '박기혁2005', '박기혁2006', '박기혁2007', '박기혁2008', '박기혁2009', '박기혁2010', '박기혁2013', '박기혁2015', '박기혁2016', '박기혁2017', '박기혁2018', '박동원2013', '박동원2014', '박동원2015', '박동원2016', '박동원2017', '박동원2018', '박민우2013', '박민우2014', '박민우2015', '박민우2016', '박민우2018', '박병호2005', '박병호2006', '박병호2009', '박병호2010', '박병호2012', '박병호2013', '박병호2014', '박병호2015', '박병호2018', '박석민2004', '박석민2008', '박석민2009', '박석민2010', '박석민2011', '박석민2012', '박석민2013', '박석민2014', '박석민2015', '박석민2016', '박석민2017', '박석민2018', '박세혁2012', '박세혁2013', '박세혁2016', '박세혁2017', '박세혁2018', '박승욱2013', '박승욱2017', '박승욱2018', '박용택2002', '박용택2003', '박용택2004', '박용택2005', '박용택2006', '박용택2007', '박용택2008', '박용택2009', '박용택2010', '박용택2011', '박용택2012', '박용택2013', '박용택2014', '박용택2015', '박용택2016', '박용택2017', '박용택2018', '박정권2004', '박정권2007', '박정권2008', '박정권2009', '박정권2010', '박정권2011', '박정권2012', '박정권2013', '박정권2014', '박정권2015', '박정권2016', '박정권2017', '박정권2018', '박정음2016', '박정음2017', '박준태2014', '박준태2015', '박지규2015', '박지규2018', '박찬도2014', '박찬도2015', '박한이2002', '박한이2003', '박한이2004', '박한이2005', '박한이2006', '박한이2007', '박한이2008', '박한이2009', '박한이2010', '박한이2011', '박한이2012', '박한이2013', '박한이2014', '박한이2015', '박한이2016', '박한이2018', '박해민2015', '박해민2016', '박해민2017', '박해민2018', '박헌도2013', '박헌도2014', '박헌도2015', '박헌도2016', '박헌도2017', '박헌도2018', '배영섭2011', '배영섭2012', '배영섭2013', '배영섭2016', '배영섭2017', '배영섭2018', '배정대2015', '배정대2016', '백민기2015', '백민기2018', '백상원2013', '백상원2014', '백상원2015', '백상원2016', '백상원2017', '백용환2014', '백용환2015', '백용환2016', '백용환2018', '백창수2010', '백창수2014', '백창수2015', '백창수2016', '백창수2018', '버나디나2017', '버나디나2018', '번즈2017', '번즈2018', '서건창2012', '서건창2013', '서건창2014', '서건창2015', '서건창2016', '서건창2017', '서건창2018', '서동욱2003', '서동욱2004', '서동욱2005', '서동욱2008', '서동욱2009', '서동욱2010', '서동욱2011', '서동욱2012', '서동욱2013', '서동욱2014', '서동욱2015', '서동욱2016', '서동욱2017', '서동욱2018', '서상우2016', '서상우2017', '손시헌2004', '손시헌2005', '손시헌2006', '손시헌2009', '손시헌2010', '손시헌2011', '손시헌2012', '손시헌2013', '손시헌2014', '손시헌2015', '손시헌2016', '손시헌2017', '손시헌2018', '손아섭2007', '손아섭2008', '손아섭2009', '손아섭2010', '손아섭2011', '손아섭2013', '손아섭2014', '손아섭2015', '손아섭2016', '손아섭2017', '손아섭2018', '손주인2005', '손주인2008', '손주인2009', '손주인2010', '손주인2011', '손주인2012', '손주인2013', '손주인2014', '손주인2015', '손주인2016', '손주인2017', '손주인2018', '송광민2006', '송광민2007', '송광민2008', '송광민2009', '송광민2010', '송광민2014', '송광민2015', '송광민2017', '송광민2018', '송민섭2015', '송민섭2018', '송성문2017', '스크럭스2017', '스크럭스2018', '신본기2012', '신본기2013', '신본기2014', '신본기2017', '신본기2018', '신성현2016', '신성현2017', '신성현2018', '신종길2003', '신종길2004', '신종길2011', '신종길2012', '신종길2013', '신종길2014', '신종길2015', '신종길2017', '심우준2015', '심우준2016', '심우준2017', '심우준2018', '안익훈2015', '안익훈2016', '안익훈2018', '안중열2015', '안중열2016', '안치홍2009', '안치홍2010', '안치홍2011', '안치홍2012', '안치홍2013', '안치홍2014', '안치홍2017', '안치홍2018', '양석환2015', '양석환2016', '양석환2017', '양석환2018', '양성우2012', '양성우2016', '양성우2017', '양성우2018', '양의지2010', '양의지2011', '양의지2012', '양의지2013', '양의지2014', '양의지2015', '양의지2016', '양의지2017', '양의지2018', '양종민2009', '양종민2010', '양종민2014', '양종민2015', '양종민2018', '엄태용2014', '오선진2008', '오선진2009', '오선진2010', '오선진2011', '오선진2012', '오선진2013', '오선진2016', '오선진2017', '오선진2018', '오재원2008', '오재원2009', '오재원2010', '오재원2011', '오재원2012', '오재원2013', '오재원2014', '오재원2015', '오재원2016', '오재원2017', '오재원2018', '오재일2009', '오재일2010', '오재일2011', '오재일2012', '오재일2013', '오재일2014', '오재일2015', '오재일2016', '오재일2017', '오재일2018', '오정복2010', '오정복2011', '오정복2014', '오정복2015', '오정복2016', '오정복2017', '오정복2018', '오지환2009', '오지환2010', '오지환2011', '오지환2012', '오지환2013', '오지환2014', '오지환2015', '오지환2017', '오지환2018', '오태곤2014', '오태곤2015', '오태곤2016', '오태곤2017', '오태곤2018', '유강남2012', '유강남2015', '유강남2016', '유강남2017', '유강남2018', '유민상2018', '유재신2008', '유재신2009', '유재신2012', '유재신2013', '유재신2014', '유재신2015', '유재신2016', '유재신2018', '유한준2005', '유한준2006', '유한준2007', '유한준2010', '유한준2011', '유한준2013', '유한준2014', '유한준2015', '유한준2016', '유한준2017', '유한준2018', '윤병호2015', '윤병호2016', '윤병호2017', '윤병호2018', '윤석민2005', '윤석민2007', '윤석민2011', '윤석민2012', '윤석민2014', '윤석민2015', '윤석민2016', '윤석민2017', '윤석민2018', '윤수강2012', '윤수강2018', '윤정우2011', '윤정우2012', '윤정우2016', '윤진호2011', '윤진호2012', '윤진호2015', '윤진호2016', '윤진호2018', '이대수2004', '이대수2005', '이대수2006', '이대수2007', '이대수2008', '이대수2009', '이대수2010', '이대수2011', '이대수2012', '이대수2013', '이대수2014', '이대수2015', '이대수2016', '이대수2017', '이대형2003', '이대형2005', '이대형2006', '이대형2007', '이대형2008', '이대형2009', '이대형2010', '이대형2011', '이대형2012', '이대형2013', '이대형2014', '이대형2015', '이대형2016', '이대형2017', '이대호2002', '이대호2004', '이대호2005', '이대호2006', '이대호2007', '이대호2008', '이대호2009', '이대호2010', '이대호2011', '이대호2017', '이대호2018', '이동훈2016', '이동훈2017', '이동훈2018', '이명기2010', '이명기2013', '이명기2015', '이명기2016', '이명기2017', '이명기2018', '이범호2002', '이범호2003', '이범호2004', '이범호2005', '이범호2006', '이범호2007', '이범호2008', '이범호2009', '이범호2011', '이범호2012', '이범호2013', '이범호2014', '이범호2015', '이범호2016', '이범호2017', '이범호2018', '이상호2013', '이상호2014', '이상호2017', '이상호2018', '이성곤2014', '이성곤2017', '이성곤2018', '이성열2005', '이성열2006', '이성열2007', '이성열2008', '이성열2009', '이성열2010', '이성열2011', '이성열2012', '이성열2013', '이성열2014', '이성열2015', '이성열2016', '이성열2017', '이성열2018', '이성우2008', '이성우2009', '이성우2010', '이성우2011', '이성우2013', '이성우2015', '이성우2016', '이성우2017', '이성우2018', '이영욱2008', '이영욱2009', '이영욱2010', '이영욱2011', '이영욱2015', '이영욱2016', '이영욱2017', '이영욱2018', '이용규2004', '이용규2005', '이용규2006', '이용규2007', '이용규2008', '이용규2009', '이용규2010', '이용규2011', '이용규2012', '이용규2013', '이용규2014', '이용규2015', '이용규2016', '이용규2018', '이우성2016', '이원석2005', '이원석2006', '이원석2007', '이원석2008', '이원석2009', '이원석2010', '이원석2011', '이원석2012', '이원석2014', '이원석2017', '이원석2018', '이인행2015', '이인행2017', '이재원2006', '이재원2007', '이재원2008', '이재원2010', '이재원2014', '이재원2015', '이재원2016', '이재원2017', '이재원2018', '이재율2016', '이재율2017', '이재율2018', '이정후2017', '이정후2018', '이종욱2006', '이종욱2007', '이종욱2008', '이종욱2009', '이종욱2010', '이종욱2011', '이종욱2012', '이종욱2013', '이종욱2014', '이종욱2015', '이종욱2016', '이종욱2018', '이준수2012', '이준수2013', '이준수2018', '이준호2011', '이준호2012', '이준호2013', '이지영2009', '이지영2012', '이지영2013', '이지영2014', '이지영2015', '이지영2016', '이지영2017', '이지영2018', '이진영2002', '이진영2003', '이진영2004', '이진영2005', '이진영2006', '이진영2007', '이진영2008', '이진영2009', '이진영2010', '이진영2011', '이진영2012', '이진영2013', '이진영2014', '이진영2015', '이진영2016', '이진영2017', '이진영2018', '이천웅2016', '이천웅2017', '이천웅2018', '이택근2003', '이택근2004', '이택근2005', '이택근2006', '이택근2007', '이택근2008', '이택근2009', '이택근2010', '이택근2012', '이택근2013', '이택근2014', '이택근2015', '이택근2016', '이택근2017', '이해창2011', '이해창2016', '이해창2017', '이해창2018', '이형종2016', '이형종2017', '이호신2007', '이호신2009', '이호신2015', '이호신2016', '이호신2017', '이흥련2014', '이흥련2015', '이흥련2016', '임병욱2015', '임병욱2016', '임병욱2017', '임병욱2018', '임종혁2014', '임훈2004', '임훈2010', '임훈2011', '임훈2012', '임훈2013', '임훈2014', '임훈2015', '임훈2016', '임훈2017', '임훈2018', '장민석2008', '장민석2010', '장민석2011', '장민석2012', '장민석2013', '장민석2014', '장민석2015', '장민석2016', '장민석2017', '장성우2009', '장성우2010', '장성우2011', '장성우2014', '장성우2015', '장성우2017', '장성우2018', '장승현2018', '장시윤2016', '장영석2009', '장영석2010', '장영석2011', '장영석2015', '장영석2016', '장영석2018', '장준원2016', '장진혁2018', '전병우2018', '전준우2008', '전준우2009', '전준우2010', '전준우2011', '전준우2012', '전준우2013', '전준우2014', '전준우2017', '전준우2018', '정경운2018', '정근우2005', '정근우2006', '정근우2007', '정근우2008', '정근우2009', '정근우2010', '정근우2011', '정근우2012', '정근우2013', '정근우2014', '정근우2016', '정근우2018', '정범모2006', '정범모2007', '정범모2008', '정범모2012', '정범모2013', '정범모2014', '정범모2018', '정병곤2013', '정병곤2017', '정상호2002', '정상호2003', '정상호2007', '정상호2008', '정상호2010', '정상호2011', '정상호2012', '정상호2014', '정상호2015', '정상호2016', '정상호2017', '정상호2018', '정성훈2002', '정성훈2003', '정성훈2004', '정성훈2005', '정성훈2006', '정성훈2007', '정성훈2008', '정성훈2009', '정성훈2010', '정성훈2011', '정성훈2012', '정성훈2013', '정성훈2014', '정성훈2015', '정성훈2016', '정성훈2017', '정성훈2018', '정수빈2009', '정수빈2010', '정수빈2011', '정수빈2012', '정수빈2013', '정수빈2014', '정수빈2015', '정수빈2016', '정의윤2005', '정의윤2006', '정의윤2008', '정의윤2011', '정의윤2013', '정의윤2014', '정의윤2015', '정의윤2016', '정의윤2017', '정의윤2018', '정주현2009', '정주현2010', '정주현2012', '정주현2013', '정주현2016', '정주현2018', '정주후2016', '정진기2011', '정진기2012', '정진기2017', '정진기2018', '정진호2011', '정진호2012', '정진호2015', '정진호2016', '정진호2017', '정진호2018', '정현2017', '정현2018', '정훈2011', '정훈2012', '정훈2013', '정훈2014', '정훈2015', '정훈2016', '정훈2017', '정훈2018', '조동찬2002', '조동찬2003', '조동찬2004', '조동찬2005', '조동찬2006', '조동찬2007', '조동찬2008', '조동찬2009', '조동찬2010', '조동찬2011', '조동찬2012', '조동찬2013', '조동찬2016', '조동찬2017', '조동찬2018', '조동화2005', '조동화2006', '조동화2007', '조동화2008', '조동화2009', '조동화2010', '조동화2011', '조동화2013', '조동화2014', '조동화2015', '조동화2016', '조수행2016', '조수행2017', '조수행2018', '조윤준2012', '조윤준2013', '조윤준2014', '조윤준2015', '조윤준2017', '조평호2013', '조평호2015', '조평호2016', '조평호2017', '조홍석2013', '조홍석2014', '조홍석2015', '조홍석2018', '주효상2016', '주효상2018', '지석훈2005', '지석훈2006', '지석훈2007', '지석훈2008', '지석훈2011', '지석훈2012', '지석훈2013', '지석훈2014', '지석훈2015', '지석훈2016', '지석훈2017', '지석훈2018', '지성준2015', '지성준2018', '채은성2014', '채은성2015', '채은성2016', '채은성2017', '채은성2018', '채태인2009', '채태인2010', '채태인2011', '채태인2012', '채태인2013', '채태인2014', '채태인2015', '채태인2016', '채태인2017', '채태인2018', '초이스2018', '최승준2014', '최승준2015', '최승준2016', '최승준2018', '최영진2013', '최영진2014', '최영진2017', '최영진2018', '최원제2017', '최원준2016', '최원준2017', '최원준2018', '최윤석2010', '최윤석2011', '최윤석2012', '최윤석2013', '최윤석2016', '최윤석2017', '최윤석2018', '최재훈2012', '최재훈2013', '최재훈2015', '최재훈2016', '최재훈2017', '최재훈2018', '최정2005', '최정2006', '최정2007', '최정2008', '최정2009', '최정2010', '최정2011', '최정2012', '최정2013', '최정2014', '최정2015', '최정2016', '최정2017', '최정2018', '최정민2015', '최정민2016', '최주환2008', '최주환2009', '최주환2012', '최주환2013', '최주환2014', '최주환2015', '최주환2016', '최주환2017', '최주환2018', '최준석2005', '최준석2006', '최준석2007', '최준석2008', '최준석2009', '최준석2010', '최준석2011', '최준석2012', '최준석2013', '최준석2014', '최준석2015', '최준석2016', '최준석2017', '최준석2018', '최진행2004', '최진행2005', '최진행2008', '최진행2009', '최진행2010', '최진행2011', '최진행2012', '최진행2013', '최진행2014', '최진행2015', '최진행2016', '최진행2017', '최진행2018', '최항2017', '최항2018', '최형우2002', '최형우2004', '최형우2008', '최형우2009', '최형우2010', '최형우2011', '최형우2012', '최형우2013', '최형우2014', '최형우2015', '최형우2016', '최형우2017', '최형우2018', '하주석2012', '하주석2013', '하주석2016', '하주석2017', '하주석2018', '하준호2015', '하준호2016', '하준호2017', '한동민2013', '한동민2014', '한동민2017', '한동민2018', '한동희2018', '한승택2013', '한승택2016', '한승택2017', '한승택2018', '허경민2012', '허경민2013', '허경민2014', '허경민2015', '허경민2016', '허경민2017', '허경민2018', '허도환2012', '허도환2013', '허도환2014', '허도환2016', '허도환2017', '허도환2018', '호잉2018', '홍성갑2015', '홍성갑2016', '홍성갑2017', '홍재호2011', '홍재호2012', '홍재호2013', '홍재호2017', '홍재호2018', '홍창기2016', '황윤호2015', '황윤호2016', '황윤호2017', '황윤호2018', '황재균2008', '황재균2009', '황재균2010', '황재균2011', '황재균2012', '황재균2013', '황재균2014', '황재균2015', '황재균2016', '황재균2018', '황진수2014']
In [33]:
# 교집합 된 데이터만 불러오기
regular_season_intersection = regular_season.loc[regular_season['new_index'].apply(lambda x : x in intersection_batter)]
regular_season_intersection.describe()
Out[33]:
batter_id | year | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 | 1358.000000 |
mean | 173.113402 | 2013.027246 | 0.251213 | 85.333579 | 247.955081 | 37.701767 | 70.106775 | 12.269514 | 1.252577 | 6.874080 | 105.503682 | 34.873343 | 6.684831 | 2.841679 | 25.645066 | 4.265096 | 47.424153 | 5.558910 | 0.368104 | 0.321404 | 4.497054 | 0.689507 |
std | 94.678257 | 4.146897 | 0.083251 | 42.605644 | 171.813382 | 29.875080 | 54.131282 | 10.299543 | 1.907663 | 8.647075 | 85.927861 | 31.309521 | 10.007186 | 3.455258 | 21.855439 | 4.501909 | 33.018225 | 4.887141 | 0.137024 | 0.093583 | 4.913211 | 0.218710 |
min | 0.000000 | 2002.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 99.000000 | 2010.000000 | 0.222000 | 51.000000 | 78.000000 | 10.000000 | 17.000000 | 3.000000 | 0.000000 | 0.000000 | 23.000000 | 7.000000 | 0.000000 | 0.000000 | 6.000000 | 1.000000 | 18.000000 | 1.000000 | 0.301250 | 0.291000 | 1.000000 | 0.600500 |
50% | 178.000000 | 2014.000000 | 0.265000 | 99.000000 | 257.000000 | 33.000000 | 65.000000 | 11.000000 | 0.000000 | 3.000000 | 93.000000 | 28.000000 | 3.000000 | 2.000000 | 22.000000 | 3.000000 | 47.000000 | 5.000000 | 0.379500 | 0.337000 | 3.000000 | 0.718000 |
75% | 252.000000 | 2016.750000 | 0.299000 | 122.000000 | 407.000000 | 60.000000 | 116.000000 | 20.000000 | 2.000000 | 10.000000 | 173.000000 | 56.000000 | 8.750000 | 4.000000 | 41.000000 | 7.000000 | 71.000000 | 9.000000 | 0.451000 | 0.372000 | 7.000000 | 0.818000 |
max | 344.000000 | 2018.000000 | 1.000000 | 144.000000 | 600.000000 | 135.000000 | 201.000000 | 47.000000 | 17.000000 | 53.000000 | 377.000000 | 146.000000 | 66.000000 | 21.000000 | 108.000000 | 27.000000 | 161.000000 | 24.000000 | 1.000000 | 1.000000 | 30.000000 | 2.000000 |
In [34]:
regular_season_intersection.sort_values(by='new_index').reset_index(drop=True)
Out[34]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | new_index | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.339 | 50 | 183 | 27 | 62 | 9 | 0 | 8 | 95 | 34 | 5 | 0 | 9 | 8 | 25 | 3 | 0.519 | 0.383 | 9 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.902 | 가르시아2018 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 1 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 | 강경학2011 |
2 | 1 | 강경학 | 2014 | 한화 | 0.221 | 41 | 86 | 11 | 19 | 2 | 3 | 1 | 30 | 7 | 0 | 0 | 13 | 2 | 28 | 1 | 0.349 | 0.337 | 6 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.686 | 강경학2014 |
3 | 1 | 강경학 | 2015 | 한화 | 0.257 | 120 | 311 | 50 | 80 | 7 | 4 | 2 | 101 | 27 | 4 | 3 | 40 | 5 | 58 | 3 | 0.325 | 0.348 | 15 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.673 | 강경학2015 |
4 | 1 | 강경학 | 2016 | 한화 | 0.158 | 46 | 101 | 16 | 16 | 3 | 2 | 1 | 26 | 7 | 0 | 0 | 8 | 2 | 30 | 5 | 0.257 | 0.232 | 7 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.489 | 강경학2016 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1353 | 342 | 황재균 | 2014 | 롯데 | 0.321 | 128 | 486 | 66 | 156 | 33 | 3 | 12 | 231 | 76 | 17 | 10 | 53 | 3 | 86 | 8 | 0.475 | 0.388 | 16 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.863 | 황재균2014 |
1354 | 342 | 황재균 | 2015 | 롯데 | 0.290 | 144 | 534 | 95 | 155 | 41 | 2 | 26 | 278 | 97 | 11 | 10 | 48 | 4 | 122 | 14 | 0.521 | 0.350 | 16 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.871 | 황재균2015 |
1355 | 342 | 황재균 | 2016 | 롯데 | 0.335 | 127 | 498 | 97 | 167 | 26 | 5 | 27 | 284 | 113 | 25 | 10 | 49 | 4 | 66 | 10 | 0.570 | 0.394 | 15 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.964 | 황재균2016 |
1356 | 342 | 황재균 | 2018 | KT | 0.296 | 142 | 530 | 76 | 157 | 41 | 3 | 25 | 279 | 88 | 14 | 7 | 49 | 4 | 120 | 5 | 0.526 | 0.358 | 17 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.884 | 황재균2018 |
1357 | 344 | 황진수 | 2014 | 롯데 | 0.000 | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 0 | 181cm/82kg | 1989년 02월 15일 | 내야수(우투양타) | 석천초-대헌중-공주고 | 4000만원 | 0.000 | 황진수2014 |
1358 rows × 30 columns
In [35]:
# 프리시즌도 똑같이 작업했다.
preseason_intersection = preseason.loc[preseason['new_index'].apply(lambda x: x in intersection_batter)].copy()
preseason_intersection.sort_values(by='new_index').reset_index(drop=True)
Out[35]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | new_index | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.350 | 7 | 20 | 1 | 7 | 1 | 0 | 1 | 11 | 5 | 0 | 0 | 2 | 0 | 3 | 1 | 0.550 | 0.409 | 1 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.959 | 가르시아2018 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 2 | 1 | 0 | 0.000 | 0.500 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.500 | 강경학2011 |
2 | 1 | 강경학 | 2014 | 한화 | - | 4 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 | 강경학2014 |
3 | 1 | 강경학 | 2015 | 한화 | 0.130 | 10 | 23 | 3 | 3 | 0 | 0 | 0 | 3 | 1 | 3 | 0 | 4 | 1 | 9 | 0 | 0.130 | 0.286 | 2 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.416 | 강경학2015 |
4 | 1 | 강경학 | 2016 | 한화 | 0.188 | 14 | 32 | 4 | 6 | 1 | 1 | 0 | 9 | 2 | 1 | 2 | 0 | 1 | 10 | 0 | 0.281 | 0.212 | 0 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.493 | 강경학2016 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1353 | 342 | 황재균 | 2014 | 롯데 | 0.407 | 10 | 27 | 3 | 11 | 2 | 0 | 1 | 16 | 4 | 1 | 0 | 2 | 0 | 5 | 0 | 0.593 | 0.448 | 1 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 1.041 | 황재균2014 |
1354 | 342 | 황재균 | 2015 | 롯데 | 0.333 | 11 | 30 | 8 | 10 | 3 | 0 | 0 | 13 | 6 | 0 | 0 | 4 | 0 | 3 | 0 | 0.433 | 0.389 | 0 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.822 | 황재균2015 |
1355 | 342 | 황재균 | 2016 | 롯데 | 0.310 | 16 | 42 | 8 | 13 | 3 | 1 | 0 | 18 | 4 | 3 | 1 | 4 | 0 | 4 | 0 | 0.429 | 0.370 | 1 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.799 | 황재균2016 |
1356 | 342 | 황재균 | 2018 | KT | 0.250 | 6 | 16 | 3 | 4 | 1 | 0 | 1 | 8 | 4 | 0 | 0 | 2 | 0 | 6 | 0 | 0.500 | 0.333 | 3 | 183cm/96kg | 1987년 07월 28일 | 내야수(우투우타) | 사당초-이수중-경기고-현대-우리-히어로즈-넥센-롯데-샌프란시스코 | 6000만원 | 0.833 | 황재균2018 |
1357 | 344 | 황진수 | 2014 | 롯데 | 0.000 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 0 | 181cm/82kg | 1989년 02월 15일 | 내야수(우투양타) | 석천초-대헌중-공주고 | 4000만원 | 0.000 | 황진수2014 |
1358 rows × 30 columns
In [36]:
# 이제 교집합이 분류가 되었는지 확인해 보았다.
print(regular_season_intersection.shape, preseason_intersection.shape)
pd.DataFrame([regular_season_intersection['new_index'], preseason_intersection['new_index']])
'''
이름이 다르고 Nan인 부분이 굉장히 많았다.
분명 교집합으로 추출한거 같지만 실상 확인해 보니 교집합이 안되었다.
''';
(1358, 30) (1358, 30)
In [37]:
'''
그래서 다른 방법으로 시도해 보았다.
교집합을 만드는 과정부터 다시해 보았다.
'''
regular_season['new_index'] = regular_season['batter_name'] + regular_season['year'].apply(str)
preseason['new_index'] = preseason['batter_name'] + preseason['year'].apply(str)
intersection_batter = list(set(regular_season['new_index']).intersection(set(preseason['new_index'])))
print('교집합 된 선수의 수: ',len(intersection_batter))
regular_season_intersection = regular_season.loc[regular_season['new_index'].apply(lambda x : x in intersection_batter)]
regular_season_intersection = regular_season_intersection.sort_values(by='new_index').reset_index(drop=True)
preseason_intersection = preseason.loc[preseason['new_index'].apply(lambda x: x in intersection_batter)].copy()
preseason_intersection = preseason_intersection.sort_values(by='new_index').reset_index(drop=True)
print(regular_season_intersection.shape, preseason_intersection.shape)
print(sum(regular_season_intersection['new_index'] == preseason_intersection['new_index']))
'''
이유를 알아내었다.
regular_season_intersection.sort_values(by='new_index').reset_index(drop=True)
이 부분을 실행할 때 나는 다른 변수에 할당하지 않아도 변경되는줄 알았지만 다른 변수에 할당하여야 했다...
''';
교집합 된 선수의 수: 1358 (1358, 30) (1358, 30) 1358
In [38]:
# 상관관계 알아보기
correlation = regular_season_intersection['OPS'].corr(preseason_intersection['OPS'])
sns.scatterplot(x=regular_season_intersection['OPS'], y=preseason_intersection['OPS'])
plt.title('correlation(상관계수): '+ str(np.round(correlation, 2)), fontsize=20)
plt.xlabel('정규시즌 OPS', fontsize=12)
plt.ylabel('프리시즌 OPS', fontsize=12)
'''
scatter가 선형적인 모습을 보이지 않는 것으로 보아 상관관계는 거의 없다고 본다.
''';
정규시즌 데이터 확인¶
In [39]:
'''
데이터는 위에 결측값을 채워둔 프레임을 가져와서 사용하려고 한다.
'''
display(regular_season.describe())
print(regular_season.shape)
regular_season.isna().sum()
'''
2454개의 행과 30개의 열을 가지고 있고,
선수 수는 2454명, 데이터는 1993년 부터 2018년까지 있다.
앞서 살펴본 프리시즌에 비해 데이터 양이 더 많은 것을 알 수 있다.
'''
batter_id | year | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | OPS | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 | 2454.000000 |
mean | 178.079462 | 2011.614507 | 0.235042 | 72.535045 | 201.514670 | 29.912388 | 55.988183 | 9.863488 | 0.957620 | 5.504075 | 84.279136 | 27.788509 | 5.290139 | 2.335778 | 20.943765 | 3.424613 | 38.596985 | 4.603504 | 0.340183 | 0.303684 | 3.676447 | 0.643868 |
std | 97.557947 | 4.992833 | 0.100894 | 45.093871 | 169.537029 | 28.778759 | 52.253844 | 9.871314 | 1.647193 | 7.989380 | 82.854200 | 29.602966 | 9.088580 | 3.194045 | 21.206113 | 4.132614 | 31.801466 | 4.713531 | 0.166238 | 0.115253 | 4.585248 | 0.268184 |
min | 0.000000 | 1993.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 101.250000 | 2008.000000 | 0.200000 | 28.000000 | 38.250000 | 5.000000 | 8.000000 | 1.000000 | 0.000000 | 0.000000 | 10.000000 | 3.000000 | 0.000000 | 0.000000 | 3.000000 | 0.000000 | 10.000000 | 1.000000 | 0.263158 | 0.269000 | 0.000000 | 0.541000 |
50% | 183.000000 | 2013.000000 | 0.253000 | 79.000000 | 163.000000 | 21.000000 | 40.000000 | 7.000000 | 0.000000 | 2.000000 | 56.000000 | 17.000000 | 2.000000 | 1.000000 | 14.000000 | 2.000000 | 33.000000 | 3.000000 | 0.358000 | 0.328000 | 2.000000 | 0.686000 |
75% | 265.000000 | 2016.000000 | 0.290000 | 115.000000 | 357.500000 | 49.000000 | 100.000000 | 16.000000 | 1.000000 | 8.000000 | 146.000000 | 44.000000 | 6.000000 | 3.000000 | 34.000000 | 5.000000 | 60.000000 | 7.000000 | 0.434946 | 0.367000 | 5.000000 | 0.797000 |
max | 344.000000 | 2018.000000 | 1.000000 | 144.000000 | 600.000000 | 135.000000 | 201.000000 | 47.000000 | 17.000000 | 53.000000 | 377.000000 | 146.000000 | 84.000000 | 21.000000 | 108.000000 | 27.000000 | 161.000000 | 24.000000 | 3.000000 | 1.000000 | 30.000000 | 4.000000 |
(2454, 30)
Out[39]:
'\n2454개의 행과 30개의 열을 가지고 있고,\n선수 수는 2454명, 데이터는 1993년 부터 2018년까지 있다.\n앞서 살펴본 프리시즌에 비해 데이터 양이 더 많은 것을 알 수 있다.\n'
In [40]:
regular_season.hist(figsize=(20,8))
plt.tight_layout()
plt.show()
'''
이 데이터를 살펴 보았는데, 여전히 0쪽에 가까운 부분들이 있다.
R(득점), 안타(H), 홈런(HR) 등 타자 쪽 기록이 왼쪽에 가깝다.
하지만 반대로 투수 쪽 기록인 삼진(SO)이 꽤나 높은 것으로 보인다.
안타는 적은데 삼진이나 병살이 좀 높다, 그리고 볼넷도 좀 낮은 것으로 보아
투고타저가 좀 있다고 생각한다.
하지만 원래 투고타저를 확인해 보려면 투수의 성적을 보아야 하지만 없으므로 정확하지는 않다.
또한 정확히 나눌 수 있는 지표가 따로 없기 때문에 개인의 의견에 따라 갈리는 듯 하다.
''';
In [41]:
# OPS 박스 플롯 그리기
plt.figure(figsize=(20,6))
plt.subplot(1,2,1) # 1행 2열에서 1번째 그래프
f = sns.boxplot(x='year', y='OPS', data = regular_season, showfliers=False)
f.set_title('연도별 OPS', size=20)
f.set_xticklabels(f.get_xticklabels(), rotation=90) # x라벨 90회전
plt.subplot(1,2,2)
plt.plot(regular_season.groupby('year')['OPS'].median())
plt.title('연도별 OPS 중앙값', size=20)
plt.show()
'''
박스 플롯은 대부분 비슷한 그림이다.
하지만 연도별 중앙값에서는 93년 초방부터 00년까지 데이터에서 큰 변동폭을 보인다.
93년부터 00년까지의 데이터를 추출해 확인해 보려고 한다.
''';
/tmp/ipykernel_8701/3071742329.py:7: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. f.set_xticklabels(f.get_xticklabels(), rotation=90) # x라벨 90회전
In [42]:
'''
각 연도별 선수의 수를 확인해 보았다.
큰 변동 폭을 보였던 연도에는 역시나 선수의 수가 10명도 아닌 3명도 채 되지 않았다.
'''
pd.crosstab(regular_season['year'], 'count').T
Out[42]:
year | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
col_0 | ||||||||||||||||||||||||||
count | 1 | 2 | 1 | 7 | 8 | 10 | 14 | 20 | 32 | 43 | 54 | 68 | 73 | 85 | 98 | 115 | 124 | 130 | 151 | 174 | 194 | 186 | 207 | 213 | 217 | 227 |
In [43]:
'''
이제 팀별 OPS를 비교해 보려고 한다.
'''
display(regular_season.pivot_table(index=['team'], columns='year', values='OPS', aggfunc='median'))
'''
몇 가지 문제가 있는데
1. 팀 명이 바뀌는 경우에는 하나로 되어있지 않고 따로 되어있다.
(현대 - 우리 - 히어로즈 - 넥센, OB - 두산과 같이 팀이 인수되어 변경되었지만 여기서는 반영되지 않고 다른 팀으로 분류되었다.)
2. 그러자니 창단부터 이어진 팀(삼성, 롯데)도 98년 이전에는 데이터가 없다.
3. 전신팀이 없는 경우도 있다.
(한화 전신 빙그레, LG 전신 MBC)
그렇기 때문에 전신까지 모두 합쳐서 새로운 데이터프레임을 만들어볼 생각이다.
''';
year | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
team | ||||||||||||||||||||||||||
KIA | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.849337 | 0.829906 | 0.681098 | 0.710473 | 0.583333 | 0.717176 | 0.546944 | 0.644474 | 0.707000 | 0.725000 | 0.680963 | 0.615420 | 0.679763 | 0.751196 | 0.598500 | 0.777000 | 0.774500 | 0.7320 |
KT | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.643561 | 0.634500 | 0.710791 | 0.6550 |
LG | NaN | 0.000000 | NaN | 0.476212 | 0.679836 | 0.509843 | 0.637173 | 0.609035 | 0.698978 | 0.645345 | 0.674368 | 0.594543 | 0.657000 | 0.588000 | 0.661500 | 0.622015 | 0.704000 | 0.677000 | 0.626000 | 0.552501 | 0.653000 | 0.680809 | 0.678000 | 0.729000 | 0.703000 | 0.7040 |
NC | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.652500 | 0.636742 | 0.703500 | 0.703000 | 0.705000 | 0.6000 |
OB | NaN | NaN | NaN | NaN | 0.615797 | 0.727667 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
SK | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.000000 | 0.596491 | 0.000000 | 0.636000 | 0.494000 | 0.601000 | 0.682591 | 0.723500 | 0.693000 | 0.822977 | 0.689176 | 0.710000 | 0.666953 | 0.689636 | 0.751500 | 0.714000 | 0.798500 | 0.764796 | 0.7685 |
넥센 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.636166 | 0.623000 | 0.589000 | 0.671000 | 0.792032 | 0.753000 | 0.800500 | 0.743500 | 0.7650 |
두산 | NaN | NaN | NaN | NaN | NaN | NaN | 0.853830 | 0.739755 | 0.695808 | 0.762809 | 0.643209 | 0.672643 | 0.699575 | 0.639750 | 0.654608 | 0.679000 | 0.701938 | 0.780000 | 0.733000 | 0.660000 | 0.781500 | 0.702330 | 0.760000 | 0.763000 | 0.685000 | 0.8230 |
롯데 | NaN | NaN | NaN | NaN | NaN | NaN | 1.124961 | 0.453390 | 0.577847 | 0.577000 | 0.638655 | 0.662041 | 0.673000 | 0.586748 | 0.615000 | 0.725455 | 0.675000 | 0.653000 | 0.676715 | 0.581845 | 0.564000 | 0.689000 | 0.622000 | 0.704000 | 0.728000 | 0.7100 |
삼성 | NaN | NaN | NaN | NaN | NaN | NaN | 0.575549 | 0.369048 | 0.830124 | 0.742000 | 0.428000 | 0.579137 | 0.741419 | 0.717275 | 0.597875 | 0.669774 | 0.684000 | 0.771000 | 0.677606 | 0.614071 | 0.712500 | 0.737000 | 0.724000 | 0.717500 | 0.684000 | 0.6515 |
쌍방울 | NaN | NaN | NaN | NaN | NaN | NaN | 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
우리 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.645375 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
한화 | NaN | NaN | NaN | 0.890260 | 0.768116 | 0.456915 | 0.512775 | 0.478132 | 0.870676 | 0.741000 | 0.719601 | 0.677000 | 0.577151 | 0.624351 | 0.665602 | 0.560294 | 0.672000 | 0.628597 | 0.649190 | 0.583929 | 0.615692 | 0.717000 | 0.673000 | 0.653409 | 0.675500 | 0.6950 |
해태 | 0.764912 | 1.036198 | 0.987261 | 0.781758 | 0.836358 | 0.844605 | 0.492506 | 0.463483 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
현대 | NaN | NaN | NaN | 0.833385 | 0.783934 | 0.547971 | 0.794743 | 0.838528 | 0.828384 | 0.673733 | 0.819444 | 0.716000 | 0.721571 | 0.677000 | 0.613006 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
히어로즈 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.598059 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
In [44]:
'''
전신 팀의 경우, 팀 이름은 현재 팀으로 간주한다.
ex) 해태, 기아 -> 기아
'''
team_list = set(regular_season['team']) # 팀 목록
new_team_dict = {
'해태': 'KIA',
'OB': '두산',
'현대': '넥센',
'우리': '넥센',
'히어로즈': '넥센',
}
regular_season['team'] = regular_season['team'].map(new_team_dict).fillna(regular_season['team'])
# 피벗 테이블 만들기
regular_season_pivot = regular_season.pivot_table(index=['team'], columns='year', values='OPS', aggfunc='median')
display(regular_season_pivot)
year | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
team | ||||||||||||||||||||||||||
KIA | 0.764912 | 1.036198 | 0.987261 | 0.781758 | 0.836358 | 0.844605 | 0.492506 | 0.463483 | 0.849337 | 0.829906 | 0.681098 | 0.710473 | 0.583333 | 0.717176 | 0.546944 | 0.644474 | 0.707000 | 0.725000 | 0.680963 | 0.615420 | 0.679763 | 0.751196 | 0.598500 | 0.777000 | 0.774500 | 0.7320 |
KT | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.643561 | 0.634500 | 0.710791 | 0.6550 |
LG | NaN | 0.000000 | NaN | 0.476212 | 0.679836 | 0.509843 | 0.637173 | 0.609035 | 0.698978 | 0.645345 | 0.674368 | 0.594543 | 0.657000 | 0.588000 | 0.661500 | 0.622015 | 0.704000 | 0.677000 | 0.626000 | 0.552501 | 0.653000 | 0.680809 | 0.678000 | 0.729000 | 0.703000 | 0.7040 |
NC | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.652500 | 0.636742 | 0.703500 | 0.703000 | 0.705000 | 0.6000 |
SK | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.000000 | 0.596491 | 0.000000 | 0.636000 | 0.494000 | 0.601000 | 0.682591 | 0.723500 | 0.693000 | 0.822977 | 0.689176 | 0.710000 | 0.666953 | 0.689636 | 0.751500 | 0.714000 | 0.798500 | 0.764796 | 0.7685 |
넥센 | NaN | NaN | NaN | 0.833385 | 0.783934 | 0.547971 | 0.794743 | 0.838528 | 0.828384 | 0.673733 | 0.819444 | 0.716000 | 0.721571 | 0.677000 | 0.613006 | 0.645375 | 0.598059 | 0.636166 | 0.623000 | 0.589000 | 0.671000 | 0.792032 | 0.753000 | 0.800500 | 0.743500 | 0.7650 |
두산 | NaN | NaN | NaN | NaN | 0.615797 | 0.727667 | 0.853830 | 0.739755 | 0.695808 | 0.762809 | 0.643209 | 0.672643 | 0.699575 | 0.639750 | 0.654608 | 0.679000 | 0.701938 | 0.780000 | 0.733000 | 0.660000 | 0.781500 | 0.702330 | 0.760000 | 0.763000 | 0.685000 | 0.8230 |
롯데 | NaN | NaN | NaN | NaN | NaN | NaN | 1.124961 | 0.453390 | 0.577847 | 0.577000 | 0.638655 | 0.662041 | 0.673000 | 0.586748 | 0.615000 | 0.725455 | 0.675000 | 0.653000 | 0.676715 | 0.581845 | 0.564000 | 0.689000 | 0.622000 | 0.704000 | 0.728000 | 0.7100 |
삼성 | NaN | NaN | NaN | NaN | NaN | NaN | 0.575549 | 0.369048 | 0.830124 | 0.742000 | 0.428000 | 0.579137 | 0.741419 | 0.717275 | 0.597875 | 0.669774 | 0.684000 | 0.771000 | 0.677606 | 0.614071 | 0.712500 | 0.737000 | 0.724000 | 0.717500 | 0.684000 | 0.6515 |
쌍방울 | NaN | NaN | NaN | NaN | NaN | NaN | 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
한화 | NaN | NaN | NaN | 0.890260 | 0.768116 | 0.456915 | 0.512775 | 0.478132 | 0.870676 | 0.741000 | 0.719601 | 0.677000 | 0.577151 | 0.624351 | 0.665602 | 0.560294 | 0.672000 | 0.628597 | 0.649190 | 0.583929 | 0.615692 | 0.717000 | 0.673000 | 0.653409 | 0.675500 | 0.6950 |
In [45]:
'''
비교적 최근에 창단되어 데이터가 없는 팀(NC, KT)은 데이터가 적기 때문에 제외한다.
또한 데이터가 없는 팀(쌍방울)도 제외한다.
그리고 모든 팀이 데이터가 있는 01년부터 OPS 값을 살펴보려고 한다.
'''
# 01년 이후 NaN 값이 있는 팀은 제외
team_list = regular_season_pivot.loc[:, 2001:].isna().sum(axis=1) <= 0
plt.figure(figsize=(20,9))
plt.plot(regular_season_pivot.loc[team_list, 2001:].T)
plt.legend(regular_season_pivot.loc[team_list, 2001:].T.columns, loc='center left', bbox_to_anchor=(1, 0.5))
plt.xticks(regular_season_pivot.loc[team_list, 2001:].columns)
plt.title('팀별 성적')
plt.show()
'''
하지만 위에서 봤듯이 OPS가 그나마 중앙에 모여있는 2005년 이전(사실 대부분)의 데이터의 수가 매우 적다.
''';
키와 몸무게로 분석¶
In [46]:
'''
키와 몸무게 그리고 포지션에서도 결측치가 존재한다.
'''
# 결측치 확인
print(regular_season.isna().sum())
regular_season.head(2)
batter_id 0 batter_name 0 year 0 team 0 avg 0 G 0 AB 0 R 0 H 0 2B 0 3B 0 HR 0 TB 0 RBI 0 SB 0 CS 0 BB 0 HBP 0 SO 0 GDP 0 SLG 0 OBP 0 E 0 height/weight 802 year_born 0 position 802 career 0 starting_salary 1067 OPS 0 new_index 0 dtype: int64
Out[46]:
batter_id | batter_name | year | team | avg | G | AB | R | H | 2B | 3B | HR | TB | RBI | SB | CS | BB | HBP | SO | GDP | SLG | OBP | E | height/weight | year_born | position | career | starting_salary | OPS | new_index | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 가르시아 | 2018 | LG | 0.339 | 50 | 183 | 27 | 62 | 9 | 0 | 8 | 95 | 34 | 5 | 0 | 9 | 8 | 25 | 3 | 0.519 | 0.383 | 9 | 177cm/93kg | 1985년 04월 12일 | 내야수(우투우타) | 쿠바 Ciego de Avila Maximo Gomez Baez(대) | NaN | 0.902 | 가르시아2018 |
1 | 1 | 강경학 | 2011 | 한화 | 0.000 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0.000 | 0.000 | 1 | 180cm/72kg | 1992년 08월 11일 | 내야수(우투좌타) | 광주대성초-광주동성중-광주동성고 | 10000만원 | 0.000 | 강경학2011 |
In [ ]:
'''
키/몸무게/포지션을 채워주려고 한다.
선수 키/몸무게/포지션은 KBO 홈페이지에서, 그리고 우투우타, 좌투우타와 같은 경우는 위키백과에서 검색하여 가져왔다.
'''
'./batter_height_weight_position_insert.ipynb';
from batter_height_weight_position_insert import crawl_hand
df_path = './datas/Regular_Season_Batter.csv'
# regular_season = pd.read_csv(df_path)
crawl_hand(regular_season, df_path)
regular_season = pd.read_csv(df_path)
['강병식', '강봉규', '강정호', '고도현', '고동진', '고메즈', '고영민', '권용관', '김경모', '김경언', '김광연', '김대륙', '김동주', '김연훈', '김원석', '김원섭', '김종찬', '김종호', '나바로', '나성용', '대니돈', '로메로', '로사리오', '로티노', '마낙길', '모상기', '문우람', '박계현', '박기남', '박노민', '박상규', '박용근', '박재상', '박재홍', '박준서', '박진만', '박진원', '백승룡', '성의준', '손용석', '송지만', '스나이더', '신경현', '신명철', '신현철', '안치용', '알드리지', '양영동', '연경흠', '오재필', '용덕한', '우동균', '유선정', '유재혁', '윤완주', '윤요섭', '이명환', '이민재', '이승재', '이양기', '이여상', '이우민', '이인구', '이정식', '이종범', '이종환', '이태원', '이현곤', '이홍구', '이희근', '임재철', '장성호', '전현태', '정보명', '정상교', '정수성', '정현석', '정형식', '조성환', '조영훈', '조인성', '조중근', '지재옥', '진갑용', '차일목', '채상병', '최경철', '최동수', '최민구', '최선호', '최훈락', '칸투', '테임즈', '피에', '한상훈', '한윤섭', '현재윤', '홍성흔', '황목치승', '황선일', '황정립'] 총 101
/home/apic/python/kbo_ops_predict/batter_height_weight_position_insert.py:121: FutureWarning: Passing literal html to 'read_html' is deprecated and will be removed in a future version. To read from a literal string, wrap it in a 'StringIO' object. df = pd.read_html(str(handle))[0]
batter_name | height/weight | position | |
---|---|---|---|
1337 | 유재혁 | 182cm/70kg | 외야수() |
starting_salary 1067 가져오지 못한 데이터는 일일이 넣기
In [ ]:
def insert_batter_info(row, batter_height_weight_position):
name = row['batter_name']
if name in batter_height_weight_position:
info = batter_height_weight_position[name]
height_weight, position = info.split('/')[0] + '/' + info.split('/')[1], info.split('/')[2]
row['height/weight'] = height_weight
row['position'] = position
# display(row[['height/weight', 'position']])
return row
with open('output_data.json', 'r', encoding='utf-8') as json_file:
data = json.load(json_file)
print(type(data))
# data
# apply를 사용하여 각 행에 대해 업데이트 적용
regular_season = regular_season.apply(insert_batter_info, axis=1, batter_height_weight_position=data)
display(regular_season[regular_season['batter_name'] == '유재혁'][['batter_name', 'height/weight', 'position']])
regular_season.isna().sum()
<class 'dict'>
batter_name | height/weight | position | |
---|---|---|---|
1337 | 유재혁 | 182cm/70kg | 외야수(우투우타) |
Out[ ]:
batter_id 0 batter_name 0 year 0 team 0 avg 0 G 0 AB 0 R 0 H 0 2B 0 3B 0 HR 0 TB 0 RBI 0 SB 0 CS 0 BB 0 HBP 0 SO 0 GDP 0 SLG 0 OBP 0 E 0 height/weight 0 year_born 0 position 0 career 0 starting_salary 1067 OPS 0 new_index 0 dtype: int64
In [ ]:
'''
키 대비 몸무게 계산
높다면 몸무게가 무거워 힘이 세고, 적으면 그만큼 가벼워 스피드가 빠를 것이라 예상
힘은 연관된 장타율과, 스피드는 연관된 출루율과 상관관계를 분석
'''
# 키와 몸무게를 분리
regular_season['height'] = regular_season['height/weight'].apply(lambda x: int(re.findall('\d+', x.split('/')[0])[0]))
regular_season['weight'] = regular_season['height/weight'].apply(lambda x: int(re.findall('\d+', x.split('/')[1])[0]))
# 키 대비 몸무게
regular_season['weight_per_height'] = regular_season['weight'] / regular_season['height']
plt.figure(figsize=(20,9))
plt.subplot(1,2,1)
obp_weight_corr = regular_season['weight_per_height'].corr(regular_season['OBP'])
slg_weight_corr = regular_season['weight_per_height'].corr(regular_season['SLG'])
sns.scatterplot(x = regular_season['weight_per_height'], y = regular_season['OBP'])
plt.title('몸무게/키 OBP 상관관계, corr: '+str(round(obp_weight_corr, 2)))
plt.ylabel('정규리그 OBP')
plt.xlabel('몸무게/키')
plt.subplot(1,2,2)
sns.scatterplot(x = regular_season['weight_per_height'], y = regular_season['SLG'])
plt.title('몸무게/키 SLG 상관관계, corr: '+str(round(slg_weight_corr, 2)))
plt.ylabel('정규리그 SLG')
plt.xlabel('몸무게/키')
plt.show()
'''
선형적인 모습이 나오지 않는 것을 보아 상관관계는 없는 것 같다.
''';
In [ ]:
'''
포지션, 타수별 OPS의 관계를 알아보자
'''
regular_season['position']
Out[ ]:
0 내야수(우투우타) 1 내야수(우투좌타) 2 내야수(우투좌타) 3 내야수(우투좌타) 4 내야수(우투좌타) ... 2449 내야수(우투양타) 2450 내야수(우투양타) 2451 내야수(우투양타) 2452 내야수(우투양타) 2453 내야수(우투양타) Name: position, Length: 2454, dtype: object
In [ ]:
# 포지션, 타수 분리
regular_season['pos'] = regular_season['position'].apply(lambda x: x.split('(')[0])
regular_season['hand'] = regular_season['position'].apply(lambda x: x.split('(')[1].replace(')', '')[2:])
regular_season[['pos', 'hand']]
Out[ ]:
pos | hand | |
---|---|---|
0 | 내야수 | 우타 |
1 | 내야수 | 좌타 |
2 | 내야수 | 좌타 |
3 | 내야수 | 좌타 |
4 | 내야수 | 좌타 |
... | ... | ... |
2449 | 내야수 | 양타 |
2450 | 내야수 | 양타 |
2451 | 내야수 | 양타 |
2452 | 내야수 | 양타 |
2453 | 내야수 | 양타 |
2454 rows × 2 columns
In [ ]:
plt.figure(figsize=(20,9))
plt.subplot(1,2,1)
# 포지션별 OPS
ax = sns.boxplot(x='pos', y='OPS', data=regular_season, showfliers = False)
plt.title('포지션별 OPS')
# 포지션별 중앙값
median = regular_season.groupby('pos')['OPS'].median().to_dict()
# 박스 중앙에 글자 표시
for patch, value in zip(ax.patches, median.values()):
# 박스 좌표
x_min, y_min, x_max, y_max = patch.get_path().get_extents().bounds
x_center = (x_max / 2) + x_min
# x_center = (x_min + x_max) / 2
# 중앙값 텍스트 추가
ax.text(x_center, value, f'중앙값: {value:.1f}',
ha='center', va='center', fontsize=15, color='white')
plt.subplot(1,2,2)
ax = sns.boxplot(x='hand', y = 'OPS', data = regular_season, showfliers = False)
plt.title('타석 방향별 OPS')
# 타석 방향별 중앙값
median = regular_season.groupby('hand')['OPS'].median().to_dict()
# 박스 중앙에 글자 표시
for patch, value in zip(ax.patches, median.values()):
x_min, y_min, x_max, y_max = patch.get_path().get_extents().bounds
x_center = (x_max / 2) + x_min # 박스의 중앙 x좌표 계산
# 중앙값 가져오기
hand = patch.get_x() # 각 박스에 해당하는 x값 (hand 값을 얻기)
value = median.get(hand, None) # 해당 hand 값에 대한 중앙값 가져오기
if value is not None: # 중앙값이 존재하는 경우에만 텍스트 추가
ax.text(x_center, value, f'중앙값: {value:.1f}',
ha='center', va='center', fontsize=15, color='white')
plt.show()
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[127], line 35 33 x_center = (x_max / 2) + x_min # 박스의 중앙 x좌표 계산 34 # 중앙값 가져오기 ---> 35 hand = patch.get_x() # 각 박스에 해당하는 x값 (hand 값을 얻기) 36 value = median.get(hand, None) # 해당 hand 값에 대한 중앙값 가져오기 38 if value is not None: # 중앙값이 존재하는 경우에만 텍스트 추가 AttributeError: 'PathPatch' object has no attribute 'get_x'
In [ ]:
ax = sns.boxplot(x='pos', y='OPS', data=regular_season, showfliers = False)
for label in ax.get_xticklabels():
print(label.get_text())
내야수 외야수 포수
반응형
'아무거나 만들어 봄 > KBO 타자 OPS 예측 경진대회' 카테고리의 다른 글
KBO 타자 OPS 예측 경진대회 - 7 (0) | 2024.12.05 |
---|---|
KBO 타자 OPS 예측 경연대회 - 5 (0) | 2024.12.03 |
KBO 타자 OPS 예측 경연대회 - 4 (0) | 2024.12.03 |
KBO 타자 OPS 예측 경연대회 - 3 (0) | 2024.12.03 |
KBO 타자 OPS 예측 경연대회 - 2 (0) | 2024.12.01 |