문제 2
투수들의 연봉 및 데이터를 2010년대 투수들의 연봉 및 데이터로 매핑(mapping)하기
즉, 우리는 최동원이 현재 존재한다면 그 연봉을 알고 싶은 것이니, 최동원의 데이터만 현재 선수에 맞춰 변환할 것인지, 혹은, 당시 선수들의 데이터를 모두 변환해서 검토할 것인지 결정해야 할 것 입니다.
저의 해답 : 저는 1982 ~ 1988 년의 연봉 데이터를 찾을 수 없을 뿐더러 최동원의 선수 데이터만 있으면 충분할 것이라 생각되었으며 2010년~2015년의 선수들의 연봉 데이터는 충분한 기록이 있으므로 해당 데이터만 사용하여 연봉을 예측할 것입니다.
코드
1. Statiz 홈페이지에서 선수들의 연봉 가져오기
url3 = "https://statiz.sporki.com/stats/?m=main&m2=pitching"
birth_2015 = []
names_2015 = []
salary_2015 = []
driver = webdriver.Chrome()
driver.get(url3)
driver.maximize_window()
select_year = driver.find_element(By.ID, 'select_year')
select_year.click()
time.sleep(1.5)
year_option = driver.find_element(By.XPATH, f"//li[@value='2015']")
year_option.click()
driver.execute_script("window.scrollTo(0,700)")
time.sleep(1.5)
tbody = driver.find_element(By.TAG_NAME, 'tbody')
rows = tbody.find_elements(By.TAG_NAME, 'tr')
for i in range(2, len(rows)):
time.sleep(2)
name_cells = rows[i].find_elements(By.TAG_NAME, 'td')
if name_cells:
player_name = name_cells[1].text
name_cells[1].click()
time.sleep(1)
page = driver.page_source
soup = BeautifulSoup(page, 'html.parser')
# Get birth date
birth_span = soup.find('span', text='생년월일 :')
if birth_span:
birth_value = birth_span.next_sibling.strip() # <span> 바로 다음 텍스트
birth_2015.append(birth_value)
salary_select = driver.find_element(By.CLASS_NAME, 'p_won')
salary_select.click()
page = driver.page_source
soup = BeautifulSoup(page, 'html.parser')
rows2 = soup.find('tbody').find_all('tr')
for row in rows2:
cells = row.find_all('td')
if cells and cells[0].text == '2015':
salary = cells[1].text
break
if salary:
salary_2015.append(salary)
else:
salary_2015.append(np.nan)
names_2015.append(player_name)
time.sleep(1)
driver.back()
else:
time.sleep(1)
continue
driver.back()
time.sleep(1)
해당 코드를 2015~2020년까지 각각 적용했습니다.(원래 반복문을 통해 만들었었으나, 크롤링이 계속 중간에 멈추는 바람에 포기하고 각각 만들어서 제작했습니다.)
하지만 해당 코드 또한 sleep을 이용해 천천히 수집하도록 만들었으나, 코드 사용 시 몇 번 정도 오류가 발생합니다.
그래도 몇 번 반복하면 수집은 완벽하게 되어 일단 코드는 오류가 있지만 사용하는 것으로 했습니다.
그리고 해당 코드는 선수들의 연봉과 나이를 가져오기 위한 코드이며, 기존에 크롤링한 선수들 데이터와 합치기(merge) 위해 이름도 같이 가져왔습니다.
2. 데이터 모두 합치고 연봉데이터를 정수형으로 변경 후 10000 곱하기
merge_stats_2015 = player_salary_2015.merge(stats_table_2015, on='선수명', how='inner')
merge_stats_2016 = player_salary_2016.merge(stats_table_2016, on='선수명', how='inner')
merge_stats_2017 = player_salary_2017.merge(stats_table_2017, on='선수명', how='inner')
merge_stats_2018 = player_salary_2018.merge(stats_table_2018, on='선수명', how='inner')
merge_stats_2019 = player_salary_2019.merge(stats_table_2019, on='선수명', how='inner')
merge_stats_2020 = player_salary_2020.merge(stats_table_2020, on='선수명', how='inner')
baseball = pd.concat([merge_stats_2015, merge_stats_2016, merge_stats_2017, merge_stats_2018, merge_stats_2019, merge_stats_2020], axis=0)
baseball.reset_index(drop=True, inplace=True)
baseball['연봉'] = baseball['연봉'].str.replace(',', '').astype('int64')
baseball['연봉'] = baseball['연봉'] * 10000
merge를 통해 모든 연도에 있는 값끼리 이름을 기준으로 합쳐줍니다. 그리고 concat도 활용하여 연봉과 생년월일이 추가된 데이터프레임들을 합쳐주었습니다.
연봉 데이터는 기존에 예를 들어 1400만원으로 되어있었습니다. 이 것을 숫자로 바꾸기 위해 정수형으로 바꿔 준 뒤 10000을 곱해주었습니다.
여기서 알아야 하는 점!!!
-> astype을 사용할 때 'int'로 하면 안되고 'int64'로 해야 합니다. 왜냐하면 연봉을 int형으로만 바꿔줄 경우 21억인 연봉이 있다면 해당 연봉은 int형의 최대 숫자를 넘어서게 되어 음수로 변하게 되고 그 상태로 저장되버립니다.(이것 때문에 잘못된 예측을 여러번 하였습니다....)
그러므로 int64로 해줘야 맞습니다.
3. WAR(Wins Above Replacement) 값 크롤링하기.
우선 WAR라는 것은 팀에 승리한 기여도를 뜻합니다. 해당 값이 있으면 훨씬 연봉 예측하는데에 도움이 될 것 같아 추가해주도록 하겠습니다.
url3 = "https://statiz.sporki.com/stats/?m=main&m2=pitching"
driver = webdriver.Chrome()
driver.get(url3)
driver.maximize_window()
war = []
names = []
for i in range(2015, 2021):
select_year = driver.find_element(By.ID, 'select_year')
select_year.click()
time.sleep(1.5)
year_option = driver.find_element(By.XPATH, f"//li[@value='{i}']")
year_option.click()
driver.execute_script("window.scrollTo(0,700)")
page = driver.page_source
soup = BeautifulSoup(page, 'html.parser')
time.sleep(1.5)
tbody = driver.find_element(By.TAG_NAME, 'tbody')
rows = tbody.find_elements(By.TAG_NAME, 'tr')
for i in range(2, len(rows)): # 2행부터 시작
Wars = rows[i].find_elements(By.TAG_NAME, 'td') # 현재 행의 td 요소 찾기
if Wars:
player_name = Wars[1].text
names.append(player_name)
# 현재 행에서 스타일이 지정된 td 요소 찾기
war_elements = rows[i].find_elements(By.CSS_SELECTOR, "td[style*='background-color:#F3F3F3; font-weight:bold;']")
for war_element in war_elements:
war.append(war_element.text) # 모든 WAR 값 추가
else:
continue
driver.close()
data = {
'WAR' : war
}
pd.DataFrame(data)
baseball['WAR'] = war
해당 코드는 반복문을 사용하여도 매우 잘되어 한 번에 긁어올 수 있었고 순서대로 WAR값에 넣어주게 된다면 각 선수에 맞는 WAR값이 추가됩니다.
(저는 처음에 merge를 할 때 기준을 대충 잡고 하여서 WAR값을 추가했을 때 해당 각 선수에 맞지 않는 WAR값이 추가되었었습니다. 그래서 merge할 때 기준을 명확하게 잡고 해주면 위의 코드에서 합치는 것처럼 선수명 긁어올 필요없이 그냥 넣어주면 됩니다. 순서는 해당 사이트에 테이블에 긁어온 순서이므로...)
4. 해당 컬럼마다의 상관계수 확인하기
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.figure(figsize=(16, 10))
sns.heatmap(baseball.corr(numeric_only=True), annot=True)
plt.show()
저희는 연봉 컬럼의 값만 확인하면 됩니다. 우선 0.3이상의 값을 확인해보면 나이, 순위, 승리수, 이닝 수, 삼진 수가 연봉에 영향이 있는 것으로 보입니다.
즉, 다른 컬럼들 없이 확인해도 될 것이란 판단이 듭니다.
5. 최동원 선수 데이터 확인하기
최동원 선수 데이터를 확인해본 결과 해당 데이터에서는 'CG'(완투), 'SHO'(완봉), 'TBF'(타자수) 값들이 더 있으며, WHIP값이 없는 것으로 확인 되었습니다.
여기서 완투 완봉 타자수는 추가하지 않고 하도록 하겠습니다.(이렇게 했던 이유는 이유 없이 했었습니다. 그래서 현재 후회 중이고 다시 할 때는 baseball 테이블에 해당 값들도 추가해봐야 할 것 같습니다.)
해당 데이터에도 WAR값이 없으므로 각 년도에 맞게 직접 찾아서 넣어주었습니다.
WHIP 값은 (피안타 + 볼넷) / 이닝 수 계산을 해주어서 구해서 추가해주었습니다.
이렇게 저는 EDA를 끝냈습니다. (아쉬운 부분이 너무 많았던 것 같습니다...)
다음 글에서는 여러 방법으로 최동원 선수의 연봉을 예측해보겠습니다.
이번 글을 쓰면서 느낀 것이 있었습니다. 확실히 결과는 좋지 않게 나왔었습니다. 해당 결과가 좋지 않게 나온 이유는 확실히 제가 이유없이 컬럼을 넣고 안넣고 결정했었던 것이 매우 큰 것 같다는 느낌을 받았습니다.
해당 문제를 해결 하려면 컬럼의 값이 별로 중요하지 않을 것 같다는 제 판단만 믿고 해서는 안될 것 같다는 것을 배웠습니다.
이 글은 제로베이스 데이터 분석 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.
다음 글에서는 위에서 계획 세운 내용들을 적용해보겠습니다.
이상입니다.
'Project > Machine Learning' 카테고리의 다른 글
Project - Instacart 데이터 물품 재구매 예측하기(RFM) (0) | 2024.11.19 |
---|---|
Project - Instacart 데이터 물품 재구매 예측하기 (0) | 2024.11.18 |
[Zero-base] 최동원 선수 연봉 예측하기 - 보충 (1) | 2024.10.29 |
[Zero-base] 최동원 선수 연봉 예측하기 - 3 (1) | 2024.10.21 |
[Zero-base] 최동원 선수 연봉 예측하기 - 1 (1) | 2024.10.18 |