카테고리 없음

02.파이썬 선형 회귀분석 (1)

풍요 평화 만땅 연구원 2020. 11. 11. 15:10

picher_stats_2017.csv
0.01MB

1.회귀분석이란?

 

  선 주위에는 빨간 점들이 있는데, 이 점들을 이용해 파란 선의 함수를 예측하는 것이 회귀 분석입니다.

 즉 회귀분석은 빨간 점들이 가지는 좌표 데이터를 기반으로 y=ax를 구하고, 향후 다른 빨간 점들이 어디에 위치할 것인지를 예측할 수 있는 분석 방법입니다.

 

2.

<실습>

목표 : 어떤 속성이 연봉에 영향을 끼치는지 분석하고, 2017년 데이터를 학습하여 2018년 연봉을 예측하기.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import warnings
warnings.filterwarnings("ignore")

picher_file_path = '../data/picher_stats_2017.csv'
picher = pd.read_csv(picher_file_path)

데이터를 읽습니다. warnings는 경고메시지를 무시하기 위해 사용했는데, 없어도 무방합니다

 

그럼 데이터의 형식을 살펴보겠습니다.

picher.head()

각 선수별 경기 정보와 승률, 연봉이 적혀있습니다.

 

칼럼도 살펴보겠습니다.

picher.columns

그럼 여기서 우리는 y는 무엇인지, x는 무엇인지 알 수 있습니다.

 

y=연봉(2018)

x=그 외

 

어떤 x가 중요한지 찾는 과정을 진행하겠습니다.

우선 각 칼럼명이 한글이기에, 한글폰트로 설정하여 에러를 방지하겠습니다.

저는 '바탕'이 익숙하여 바탕으로 선택했습니다.

import matplotlib as mpl
# 현재 OS 내에 설치된 폰트를 확인합니다.
set(sorted([f.name for f in mpl.font_manager.fontManager.ttflist]))

mpl.rc('font', family='Batang')

 

예측 대상인 연봉에 대한 정보를 보겠습니다.

picher['연봉(2018)'].hist(bins=100)

주로 분포는 5억이하로 분포하고 있는데, 몇몇 고연봉 선수가 있기도 합니다.

 

이를 다른 형식으로도 봐보겠습니다.

picher.boxplot(column=['연봉(2018)'])

평균 박스권은 아래에 위치하지만 몇몇 값이 확 위로 튀는 걸 알 수 있네요.

 

어떤 특징이 저런 차이를 내는지 알아보고, 모델링한다면 괜찮은 예측 모델을 만들 수 있을 겁니다.

.

.

.

제일 먼저 할 일은, 단위를 통일하는 것입니다.

현재 테이블은 각 특징(x)의 자릿수가 서로 다르기에, scaling작업을 통해 같은 범위로 압축시켜야 합니다.

# 피처 각각에 대한 scaling을 수행하는 함수를 정의합니다.
def standard_scaling(df, scale_columns):
    for col in scale_columns:
        series_mean = df[col].mean()
        series_std = df[col].std()
        df[col] = df[col].apply(lambda x: (x-series_mean)/series_std)
    return df

컨셉은 '각각의 평균을 표준편차로 나눈 값은 수렴한다'라고 생각됩니다.

확률과 통계에서 자주 나오는 표준편차와 분산은 보통 -1~+1 사이에서 분포한다는 점과 연관하여 생각해보았습니다.

 

그럼 scailing 결과를 보겠습니다.

# 피처 각각에 대한 scaling을 수행합니다.
scale_columns = ['승', '패', '세', '홀드', '블론', '경기', '선발', '이닝', '삼진/9',
       '볼넷/9', '홈런/9', 'BABIP', 'LOB%', 'ERA', 'RA9-WAR', 'FIP', 'kFIP', 'WAR', '연봉(2017)']
picher_df = standard_scaling(picher, scale_columns)
picher_df

각 칼럼들이(x들) 한자리 단위로 수렴하게 바뀌었습니다. 이제 각 특징들을 함께 묶어 비교해도 큰 문제가 없을 겁니다.

 

잠시 다음 단계로 넘어가기 전, y를 명확히 표시하기 위해 칼럼 : '연봉(2018)'의 이름을 'y'로 변경하겠습니다.

picher_df=picher_df.rename(columns={'연봉(2018)' : 'y'})
picher_df

 

다음 단계는 숫자가 아닌 값들을 숫자로 전처리하는 과정을 진행합니다.

 

테이블을 보시면 현재 선수명과 팀명이 숫자 값이 아닌데, 변경이 필요합니다.

 

1. 선수명 : 선수의 이름은 연봉에 영향을 끼치지 않을 것이므로 삭제

picher_df = picher_df.drop('선수명', axis=1)

 

2. 팀명 : 해당 행(row)이 어떤 팀이냐를 0 or 1로 구분

각 선수가 어떤 팀인지 0과 1로 표현하기 위해선 pd.get_dummies() 함수를 사용합니다.

team_encoding = pd.get_dummies(picher_df['팀명'])

#더미로 만든 뒤 '팀명'은 필요가 없어졌으니 삭제
picher_df = picher_df.drop('팀명', axis=1)
team_encoding

해당 팀 = 1

그럼 이 데이터를 picher_df에 붙입니다.

picher_df = picher_df.join(team_encoding)
picher_df

현재 picher_df는 152개의 데이터를 가지고 있습니다.

여기서 회귀분석을 위해 두 가지로 나누어야 하는데, [학습 데이터]와 [검증 데이터]로 나누어야 합니다.

 

학습데이터 : 모형 추정 즉, 학습을 위한 데이터.

검증데이터 : 성능 검증을 위한 데이터.

 

Q>왜 멀쩡한 테이블을 두 가지로 나누는 걸까요?

→이유는 교차검증을 위해서입니다.

예측모델이 잘 만들어졌는지 보려면 알고 있는 정답과 비교를 해봐야 합니다. 그래서 보통 80%의 데이터만으로 학습을 하고, 나머지 20%는 학습한 모형이 잘 작동하는지 확인하기 위한 검증 데이터로 뺍니다.

 

그럼 학습을 진행해보겠습니다.

from sklearn import linear_model
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from math import sqrt

# 학습 데이터와 테스트 데이터로 분리합니다.
#'선수명', 'y'를 제외한 칼럼들은 X
X = picher_df[picher_df.columns.difference(['선수명', 'y'])]

y = picher_df['y']

#검증(test)는 20%로 한다. = test_size=0.2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)

여기서 random_state는 난수를 발생시키는 것인데, 20%의 검증 데이터를 변형시켜서 학습을 진행할 때 '과적합'에 빠지지 않도록 도와주는 역할을 합니다.

과적합 : 학습 데이터 대상 적중률은 100%에 가깝지만 새로운 외부 데이터는 적중률이 현저히 낮은 현상

 

이제 회귀 분석 계수를 학습하고 출력해봅니다.

 

# 회귀 분석 객체 생성(선형 회귀 모델 생성)
lr = linear_model.LinearRegression()

#fit()는 기울기와 절편을 전달하기 위함.
model = lr.fit(X_train, y_train)

# 학습된 계수를 출력합니다.
print(lr.coef_)

# 상수항을 출력합니다.
print(lr.intercept_)

해당 계수들을 보면, '51584.44818152'가 가장 영향도가 높다고 나옵니다.

 

그럼 완성한 모델을 이용해 새로운 값을 가지고 예측을 해봅니다.

#새로운 데이터x에 대한 y결과를 예측할 수 있다.
y_predicted=model.predict(x_predicted)

 

여기선 새로운 값을 가지고 오기 귀찮으니 test용으로 썼던 'X_test'를 사용하겠습니다.

x_new=X_test
y_new=model.predict(x_new)

여기서 y_new가 각각의 x_new에 대한 2018년 연봉을 예측했을 겁니다.

print(type(y_test))

타입이 Series네요. DataFrame으로 고쳐서 실제 y_test와 비교를 해보겠습니다.

y_compare={'y_test':y_test, 'y_predicted':y_new}
pd.DataFrame(y_compare)

일부 확인해보니 3번째 행이나 4번째 행은 거의 유사하고, 2번째 행은 오히려 마이너스가 나옵니다.

뭔가 맞는듯하면서도 어떤 값들이 방해를 하는 것 같군요.

 

하지만 꽤 많은 값이 들어맞았습니다!

 

차트로 한 번 보겠습니다.

mpl.rc('font', family='Batang')
y_compare.plot(y=['y_test', 'y_predicted'], kind="bar")

파란색이 실제값이고 주황색이 예측값인데, 상당히 유사한 모양을 보이네요