programming/Python

야구선수 연봉 예측하기_회귀 분석,사이킷런 미래 예측_파이썬/데이터분석

Jofresh 2023. 7. 3. 21:11
728x90
반응형

 

안녕하세요.

조신선입니다.

회귀 분석은 이전에도 많이 다루어보았습니다.

예측 분석에서 가장 대표적인 방법이 회귀 분석이죠.

이번에는 조금 더 실용성있는 예제와 함께 회귀 분석에 대해서 알아보겠습니다.

 

데이터 분석은 늘 VSCODE로 작업하고, 일반 파이썬 환경이 아닌 쥬피터 환경이라 부르는 환경에서 진행합니다.

 

확장자가 'PY'가 아닌 'IPYNB'입니다. 

 

회귀 분석이란??
독립변수(x)와 종속변수(y) 간의 관계를 찾아내는 것입니다. 

일차 방정식 ( Y = wX+b)로 예를 들 수 있습니다.
선형 회귀 분석과 일차 방정식

위 그래프에서 회귀 분석이란 실제 데이터(파란점)와 거리가 최소가 되는 방정식(선)을 찾아내는 것입니다.

 

즉, 방정식의 계수 w와 b를 잘 정의하는 것이라고 할 수 있습니다.

 

그렇다면 회귀 분석을 데이터 분석에 어떻게 적용하는 것일까요?? 이를 우리에게 익숙한 표 형태로 알아볼게요.

 

아래의 표에서 '나이', '몸무게' 피처는 방정식의 x1,x2 그리고 '키'는 방정식 y로 정의할 수 있습니다.

여기에 x1의 영향력을 나타내는 w1, x2의 영향력을 나타내는 w2, 상수 b를 사용하면 y =w1x1+w2x2+b라는 방정식이 완성됩니다.

 

인덱스 x1 나이 x2 몸무게 y 키
1 23 65 175
2 14 32 141
3 17 71 166
4 18 63  ???

 

회귀 분석은 표 1~3번 데이터로 계수를 추정하는 것이라고 할 수 있습니다.

이를 통해 방정식의 계수를 추정하면 아직 알려지지 않은 4번의 키 데이터를 예측할 수 있습니다.

 

 

여기서 1~3번 데이터는 학습 전용 데이터셋, 그리고 4번 데이터는 테스트 전용 데이터셋이라고 합니다.

그리고 이러한 학습 방식을 지도 학습이라고 합니다. (이것 또한 머신러닝 편에서 다룬적이 있습니다.ㅎㅎ)

2023.05.28 - [programming/파이썬으로 배우는 통계학] - [파이썬/머신러닝] 통계학과 머신러닝 - 이론적인 부분들 (라소회귀, 릿지회귀)

 

자 그럼 본격적으로 살펴보겠습니다.

 


프로야구 연봉 데이터 살펴보기

(해당 데이터는 http://www.statiz.co.kr/에서 가져왔습니다.)

아래 데이터를 사용해서 분석을 해보겠습니다.

타자.csv
0.02MB
투수.csv
0.01MB

 

 

 

프로야구 연봉 데이터셋의 기본 정보

import pandas as pd

# Data Source : http://www.statiz.co.kr/

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

picher.columns
picher.head()

#출력 결과

프로야구 연봉 데이터셋 출력 결과

 

이번 예제에서 예측할 회귀 분석의 목표(Y값0는 데이터의 '연봉(2018)' 피처입니다. 다음 코드는 이를 히스토그램과 BOX PLOT으로 시각화 한 것입니다.

picher.boxplot(column=['연봉(2018)']) # 연봉의 Boxplot을 출력합니다.
picher['연봉(2018)'].hist(bins=100) # 2018년 연봉 분포를 출력합니다.

#출력 결과

BOX PLOT 출력 결과

 

HIST(분포도) 출력 결과

 

히스토그램에서는 연봉의 분포를 파악할 수 있으며, 상자 그림에서는 연봉의 일반적인 범주를 파악할 수 있습니다.

수십억대 연봉을 받는 선수는 그리 많지 않으며, 5억원 미만의 연봉이 일반적인 것으로 보이네요.

 

다음으로 각각의 투수들은 어떤 특성을 가지고 있는지 살펴봅시다.

 

아래 코드는 투수의 모든 수치형 피처의 분포를 시각화 한 것입니다. 이를 통해 몇몇 투수들은 매우 불균형한 분포를 가지고 있다는 것을 알 수 있습니다.

 

picher_features_df = picher[['승', '패', '세', '홀드', '블론', '경기', '선발', '이닝', '삼진/9',
       '볼넷/9', '홈런/9', 'BABIP', 'LOB%', 'ERA', 'RA9-WAR', 'FIP', 'kFIP', 'WAR',
       '연봉(2018)', '연봉(2017)']]
       
       # 피처 각각에 대한 histogram을 출력합니다.
def plot_hist_each_column(df):
    plt.rcParams['figure.figsize'] = [20, 16]
    fig = plt.figure(1)
    
    # df의 column 갯수 만큼의 subplot을 출력합니다.
    for i in range(len(df.columns)):
        ax = fig.add_subplot(5, 5, i+1)
        plt.hist(df[df.columns[i]], bins=50)
        ax.set_title(df.columns[i])
    plt.show()
    
    plot_hist_each_column(picher_features_df)

 

# 출력 결과

투수 수치형 피처 코드 출력 결과

또 한 가지 그래프를 통해 알 수 있는 것은 각 피처 간의 단위가 다르다는 것입니다. 예를 들어 '홈런/9'라는 피처는 X축이 0~8사이 값인 반면, '이닝'이라는 피처는 0~200 사이의 값을 포함하고 있습니다.

 

이러한 데이터는 피처의 정규화 혹은 스케일링이 되어있지 않은 데이터입니다.

반응형
피처의 정규화란?
데이터 분석에서 피처 정규화(Feature Scaling)는 피처(데이터의 열 또는 속성)의 값 범위를 조정하는 프로세스를 말합니다. 일반적으로, 데이터 세트에는 여러 피처가 있으며, 이러한 피처들은 서로 다른 값 범위를 가질 수 있습니다. 피처 정규화는 이러한 값 범위의 차이를 줄여서 알고리즘이 올바르게 작동하도록 도와줍니다.

피처 정규화의 주요 목적은 다음과 같습니다:

모든 피처가 동일한 스케일을 갖도록 만듭니다. 이렇게 하면 피처 간의 상대적인 중요도를 쉽게 비교할 수 있습니다.
경사 하강법 등과 같은 일부 머신 러닝 알고리즘은 피처의 스케일에 민감할 수 있으므로, 정규화를 통해 알고리즘이 수렴하는 데 도움을 줄 수 있습니다.
일반적으로 사용되는 두 가지 주요 피처 정규화 기법은 다음과 같습니다:

Min-Max 정규화:
이 방법은 피처 값을 특정 범위로 변환합니다(일반적으로 0과 1 사이로 조정). 수식은 다음과 같습니다:

X_new = (X - X_min) / (X_max - X_min)

여기서 X는 원래 값, X_new는 정규화된 값, X_min은 피처의 최소값, X_max는 피처의 최대값입니다.

표준화 (Standardization):
이 방법은 피처 값을 평균이 0이고 표준 편차가 1인 표준 정규 분포로 변환합니다. 수식은 다음과 같습니다:

X_new = (X - 평균) / 표준 편차

이 방법은 데이터가 정규 분포를 따를 때 특히 유용합니다.

피처 정규화는 일반적으로 데이터 전처리 단계에서 수행되며, 주어진 데이터와 알고리즘의 특성에 따라 적절한 방법을 선택해야 합니다. 피처 정규화는 데이터 분석에서 중요한 단계이며, 모델의 성능 향상에 기여할 수 있습니다.


쉽게 설명하자면,,

'키'와 '몸무게'라는 2개의 피처로 나이를 예측하는 회귀 분석을 한다고 할 때, 회귀 분석은 '키를 나타내는 상수'에 의해 더 큰 영향을 받을 수 있습니다. 같은 단위라면 몸무게보다는 키의 값이 더 크기 때문입니다. 이러한 피처들의 단위를 0~1 사이 혹은 상대적 값을 표현할 수 있는 수치로 맞춰주는 것이 피처 스케일링입니다. 

 

 


투수의 연봉 예측하기

다음 코드에서는 여러 가지 피처 스케일링의 방법 중 표준화 방법을 적용하였습니다. 표준화는 정규 분포에서의 z-값을 구하는 과정과 동일합니다.

 

# pandas 형태로 정의된 데이터를 출력할 때, scientific-notation이 아닌 float 모양으로 출력되게 해줍니다.
pd.options.mode.chained_assignment = None

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

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

 

 

#출력 결과

피처 스케일링 코드 출력 결과

연속형이 아닌 범주형 피처들은 어떻게 정규화 할 수 있을까요?
범주형 피처에는 [원-핫 인코딩]이라는 방법을 적용해야 합니다.
판다스에서는 get_dummies() 함수로 간단하게 원-핫인코딩을 적용할 수 있습니다.

 

다음 실행 결과는 원-핫인코딩의 적용 결과입니다.

# 팀명 피처를 one-hot encoding으로 변환합니다.
team_encoding = pd.get_dummies(picher_df['팀명'])
picher_df = picher_df.drop('팀명', axis=1)
picher_df = picher_df.join(team_encoding)

team_encoding.head(5)

 

# 출력 결과

원-핫 인코딩 코드 출력 결과

 

이제 회귀 분석 모델을 학습시킬 준비가 거의 완료되었습니다. 한 가지 더 준비할 것은 모델의 학습 데이터셋과 테스트 데이터셋으로 데이터를 분리하는 것입니다.

 

이전에 다뤘던 사이킷런(sklearn)이라는 모듈의 train_test_split() 함수로 이를 수행할 수 있습니다.

2023.06.13 - [programming/파이썬으로 배우는 통계학] - [파이썬/머신러닝] 사이킷런을 활용한 신경망 머신러닝

 

다음 코드의 실행 결과, (X_train, y_train):(X_test, y_test) = 8:2 비율로 총 4개의 데이터가 생성됩니다.

 

X_train과 y_train은 학습 데이터셋, 그리고 X_test와 y_test는 테스트 데이터셋을 의미합니다.

 

 

from sklearn import linear_model
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error #평균 제곱 오차 계산
from math import sqrt #제곱근 계산

# 학습 데이터와 테스트 데이터로 분리합니다.
X = picher_df[picher_df.columns.difference(['선수명', 'y'])] #데이터프레임에서 선수명과 y열을 제외한 모든열을 지정
y = picher_df['y'] #y열은 y로 지정
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)
#x는 입력데이터, y는 출력 데이터 / 테스트 사이즈는 20% / 데이터를 무작위로 섞을 때 사용할 시드는 19로 지정

 

이제 회귀 분석을 수행하는 코드를 살펴보겠습니다. 다음 코드에서는 사이킷런 라이브러리의 LinearRegression()으로 모델 오브젝트를 선언한 뒤, 해당 오브젝트에 model = lr.fit(X_train, y_train) 코드를 실행시킵니다.

그러면 아래의 결과처럼 학습이 완료된 회귀식의 계수를 출력할 수 있습니다.

 

**위에서 2018년도 연봉은 y로 이름을 변경해주었었죠? 잊지마세용!!

# 회귀 분석 계수를 학습합니다 (회귀 모델 학습)
lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)

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

#출력 결과

회귀 분석 계수 학습&학습된 계수 출력 결과

 

 

 

 


예측 모델 평가하기

회귀 분석은 statsmodel 라이브러리의 OLS 클래스로도 실행이 가능합니다. OLS 클래스의 summary()함수를 사용하면 다음의 실행 결과처럼 계수에 대한 자세한 분석 내용을 살펴볼 수 있습니다.

 

pip install statsmodels
#statsmodels 설치해주시구요!

 

 

  • #1. 예측 모델 평가하기: statsmodels
import statsmodels.api as sm

# statsmodel 라이브러리로 회귀 분석을 수행합니다.
X_train = sm.add_constant(X_train)
model = sm.OLS(y_train, X_train).fit()
model.summary()

 

 

# 출력 결과

statsmodel 코드 출력 결과

 

위 실행 결과에서 결정계수(R-squared), 수정 결정 계수(Adj. R-squared)라는 점수를 눈여겨 봐야 합니다.

 

이 점수들은 회귀 분석이 얼마나 잘 되었는지 평가하는 지표이며, 회귀 분석으로 추정한 모델이 주어진 데이터를 얼마나 잘설명하는가?에 대한 점수입니다. 

 

이 점수들이 1에 가까울수록 데이터를 잘 설명하는 모델이라고 할 수 있습니다.

 

다음으로 F통계량(F-statistic)이라는 수치를 살펴봅니다. F 통계량은 회귀식의 유의성 검정에 사용되는 값으로 F 통계량에 대한 p-value인 Prob(F-statistics) 수치와 함께 살펴봐야 합니다. 일반적으로 p-value가 0.05이하면 'F 통계량이 유의미한 의미를 가진다'라는 결론을 내려도 무방하고 이는 회귀 분석이 유의미한 결과를 가진다는 것 입니다.

 

또한 표의 P>|t|라는 정보는 각 피처의 검정 통계량(t-statistics)이 얼마나 유의미한지에 대한 p-value를 나타내는 것입니다. 

 

위 분석에서는 'WAR','연봉(2017)', '한화' 3개의 피처의 p-value가 0.05미만으로 나타났기 때문에

회귀분석에서 유의미한 피처들이라는 결론을 내릴 수 있습니다. 

 

 

 

결과를 상세 해석을 확인하시려면 더보기 클릭해주세요.

더보기

Dep. Variable: y

종속 변수 (목표 변수)의 이름은 'y'입니다.
R-squared: 0.928

결정 계수 (R-squared)는 0.928입니다. 이 값은 모델이 종속 변수의 변동성의 약 92.8%를 설명한다는 것을 의미합니다. 높은 결정 계수는 모델이 데이터에 잘 적합되었음을 나타냅니다.
Adj. R-squared: 0.907

조정된 결정 계수 (Adjusted R-squared)는 0.907입니다. 이 값은 독립 변수의 수와 데이터의 크기를 고려한 결정 계수입니다. 일반적으로 독립 변수의 수가 많아질수록 결정 계수는 증가하지만, 조정된 결정 계수는 증가하지 않습니다. 따라서, 0.907의 값은 모델이 독립 변수에 과적합되지 않고 일반화되었음을 나타냅니다.
F-statistic: 44.19

F-통계량은 44.19입니다. 이 값은 회귀 모델이 통계적으로 유의미한지를 평가하는데 사용됩니다. F-통계량은 독립 변수와 종속 변수 사이의 선형 관계의 유의성을 평가합니다. 유의한 F-통계량은 모델이 종속 변수를 예측하는 데 유의미한 독립 변수를 포함하고 있다는 것을 의미합니다.
Prob (F-statistic): 7.70e-42

F-통계량에 대한 p-value입니다. 이 값은 F-통계량이 유의미한지를 나타냅니다. p-value가 매우 작은 값인 7.70e-42이므로, 모델의 F-통계량은 유의미하다고 할 수 있습니다.
coef, std err, t, P>|t|, [0.025, 0.975]

회귀 모델의 각 독립 변수에 대한 추정된 계수(coef), 표준 오차(std err), t-통계량(t), p-value(P>|t|), 95% 신뢰 구간([0.025, 0.975])이 제공됩니다. 이 정보들은 각 독립 변수의 회귀 계수의 유의성을 평가하는데 사용됩니다. p-value가 작은 값인 변수들은 유의미한 독립 변수로 간주됩니다.

 

Omnibus: 28.069

Omnibus 검정은 잔차의 정규성을 평가하는데 사용됩니다. Omnibus 값이 클수록 잔차가 정규분포에서 벗어난다는 것을 의미합니다.
Durbin-Watson: 2.025

Durbin-Watson 검정은 잔차 간의 자기상관을 평가하는데 사용됩니다. Durbin-Watson 값이 2에 가까울수록 잔차 간에 자기상관이 없다는 것을 나타냅니다. 이 경우, 2.025는 자기상관이 거의 없다는 것을 의미합니다.
Jarque-Bera (JB): 194.274

Jarque-Bera 검정은 잔차의 정규성을 평가하는데 사용됩니다. JB 값이 클수록 잔차가 정규분포에서 벗어난다는 것을 의미합니다.
Skew: -0.405

Skewness(왜도)는 분포의 비대칭 정도를 나타내는 통계적 척도입니다. 음수인 경우 왼쪽으로 비대칭되어 있음을 나타냅니다. 따라서, -0.405는 왼쪽으로 약간 비대칭된 분포를 의미합니다.
Kurtosis: 9.155

Kurtosis(첨도)는 분포의 꼬리의 두께를 나타내는 통계적 척도입니다. Kurtosis 값이 3보다 크면 분포가 보다 중심으로부터 더 꼬리가 두꺼운 형태를 나타냅니다. 따라서, 9.155는 분포가 꼬리가 두꺼운 형태를 의미합니다.
Cond. No.: 3.47e+16

조건 수 (Condition Number)는 다중공선성(multicollinearity)을 평가하는데 사용됩니다. 조건 수가 매우 큰 경우, 다중공선성 문제가 발생할 가능성이 있습니다. 이 경우, 3.47e+16은 다중공선성의 가능성이 높음을 나타냅니다.
위의 결과를 종합하면, 회귀 모델은 종속 변수를 예측하는 데 좋은 성능을 보이고 있습니다. R-squared 값이 높고, F-통계량의 p-value가 매우 작으며, 독립 변수들 중 일부는 유의미한 계수를 가지고 있습니다. 하지만, 일부 독립 변수들은 p-value가 크거나 95% 신뢰 구간이 0을 포함하고 있어 유의미하지 않을 수 있습니다.

 

다음으로 지금까지 학습한 coef(계수) 값들을 시각화 하여 살펴봅시다.

 

# 한글 출력을 위한 사전 설정 단계입니다.
mpl.rc('font', family='Malgun Gothic')
plt.rcParams['figure.figsize'] = [20, 16]

# 회귀 계수를 리스트로 반환합니다.
coefs = model.params.tolist()
coefs_series = pd.Series(coefs)

# 변수명을 리스트로 반환합니다.
x_labels = model.params.index.tolist()

# 회귀 계수를 출력합니다.
ax = coefs_series.plot(kind='bar')
ax.set_title('feature_coef_graph')
ax.set_xlabel('x_features')
ax.set_ylabel('coef')
ax.set_xticklabels(x_labels)

 

# 출력 결과

 

coef(계수) 값들을시각화 코드 출력 결과

 

이에 대한 위 코드의 시각화 결과, FIP, WAR, 홈런, 작년 연봉 피처가 가장 영향력이 큰것으로 보입니다. 현재까지의 피처 탐색결과는 다음과 같이 정리할 수 있습니다.

 

피처명 유의미한 피처인가? 영향력이 큰 피처인가?
FIP X O
WAR O O
홈런 X O
작년 연봉 O O
팀(한화) O

 

 

 

다음으로 아래의 출력 결과는 수정 결정 계수(R2 score)를 사이킷런의 LinearRegression 클래스로 출력해볼게요.

 

  • #2. 예측 모델 평가하기 : R2 score
# 학습 데이터와 테스트 데이터로 분리합니다.
X = picher_df[picher_df.columns.difference(['선수명', 'y'])]
y = picher_df['y']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)

# 회귀 분석 모델을 학습합니다.
lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)

# 회귀 분석 모델을 평가합니다.
print(model.score(X_train, y_train)) # train R2 score를 출력합니다.
print(model.score(X_test, y_test)) # test R2 score를 출력합니다.

 

 

# 출력 결과

R2 score 코드 출력 결과

 

실행 결과의 각 값은 학습 데이터셋과 테스트 데이터셋에 대한 평가 점수를 의미하며, 이 두 점수는 최대한 벌어지지 않는 것이 좋습니다. 만약 학습 점수가 테스트 점수에 비해 높다면 과적합이 발생한 것입니다.

 

모의고사에만 특화된 공부를 한 나머지, 실제 시험의 새로운 유형에 적응하지 못 하는 경우라고 비유 할 수 있어요.

 

  • #3. 예측 모델평가: RMSE score

이 점수는 실제값과 예측값의 차이를 절대적인 수치로 나타낸 것입니다. 이 값이 높으면 높을수록 예측이 부정확하다는 것을 의미합니다. 

 

다음 코드는 RMSE socre를 출력한 것입니다.

 

# 회귀 분석 모델을 평가합니다.
y_predictions = lr.predict(X_train)
print(sqrt(mean_squared_error(y_train, y_predictions))) # train RMSE score를 출력합니다.
y_predictions = lr.predict(X_test)
print(sqrt(mean_squared_error(y_test, y_predictions))) # test RMSE score를 출력합니다.

 

#출력 결과

RMSE score 코드 출력 결과

 

RMSE는 실제 값과 예측 값 간의 차이를 평균화하고, 제곱하여 제곱근을 취한 값으로, 모델의 예측 정확도를 평가하는 지표입니다. 일반적으로 RMSE가 낮을수록 모델의 예측이 더 정확하다고 볼 수 있습니다.

위의 결과를 해석하면 다음과 같습니다:

학습 데이터의 RMSE score: 7282.718684746374

학습 데이터에 대한 회귀 모델의 예측 값과 실제 값의 차이를 평균화하고 제곱근을 취한 값은 약 7282.72입니다. 즉, 학습 데이터에 대한 회귀 모델의 평균 예측 오차는 약 7282.72입니다.
테스트 데이터의 RMSE score: 14310.696436889137

테스트 데이터에 대한 회귀 모델의 예측 값과 실제 값의 차이를 평균화하고 제곱근을 취한 값은 약 14310.70입니다. 즉, 테스트 데이터에 대한 회귀 모델의 평균 예측 오차는 약 14310.70입니다.
RMSE를 비교하면, 학습 데이터의 오차가 테스트 데이터의 오차보다 작으므로, 모델이 학습 데이터에 대해 좀 더 잘 예측하는 경향이 있습니다. 그러나 테스트 데이터의 오차가 학습 데이터의 오차에 비해 큰 경우, 모델이 테스트 데이터에 대해 과적합(Overfitting)되었을 수 있습니다. 따라서, 모델의 일반화 능력을 향상시키기 위해 조정이 필요할 수 있습니다.

 

 

그렇다면 위 평가 결과는 테스트 데이터가 과적합되었다고 볼 수 있는건가?

 

위의 평가 결과만으로는 테스트 데이터가 과적합되었다고 단정짓기는 어렵습니다. RMSE 값 자체로는 모델의 예측 정확도를 판단하는 데에 제한적인 정보를 제공하기 때문입니다.

과적합 여부를 판단하기 위해서는 다른 평가 지표나 그래프를 종합적으로 고려해야 합니다. 

따라서, 위의 평가 결과만으로는 테스트 데이터가 과적합되었다고 단정짓기 어렵습니다. 추가적인 평가 지표나 시각화를 통해 모델의 성능과 과적합 여부를 더 정확하게 평가하는 것이 좋습니다.

 


이번에는 피처들의 상관 관계를 살펴보기 위해 heatmap 방식의 시각화를 사용하겠습니다.

 

이를 통해 승-이닝, kFIP-FIP, RA9_WAR-WAR 등의 피처 쌍에서 높은 연관성을 발견할 수 있습니다.

 

import seaborn as sns

# 피처간의 상관계수 행렬을 계산합니다.
corr = picher_df[scale_columns].corr(method='pearson')
show_cols = ['win', 'lose', 'save', 'hold', 'blon', 'match', 'start', 
             'inning', 'strike3', 'ball4', 'homerun', 'BABIP', 'LOB', 
             'ERA', 'RA9-WAR', 'FIP', 'kFIP', 'WAR', '2017']

# corr 행렬 히트맵을 시각화합니다.
plt.rc('font', family='NanumGothicOTF')
sns.set(font_scale=1.5)
hm = sns.heatmap(corr.values,
            cbar=True,
            annot=True, 
            square=True,
            fmt='.2f',
            annot_kws={'size': 15},
            yticklabels=show_cols,
            xticklabels=show_cols)

plt.tight_layout()
plt.show()

 

 

# 출력 결과

피처들의 상관 관계 분석

 

하지만 회귀 분석은 피처 간의 '독립성'을 전제로 하는 분석 방법이기 때문에 올바른 회귀분석을 하려면 이러한 피처 쌍을 제거해야 합니다.

 

그래서 다중 공선성이라는 것을 살펴봐야 합니다.

 

다중 공선성이란 변수 간 상관 관계가 높아 분석에 부정적인 영향을 미치는 것을 의미합니다.

 

다중 공선성을 살펴봄으로써 여러 피처 간의 연관성을 고려했을 때 어떤 피처를 제거하는 것이 옳은 판단일지 혹은 제거하는 것 자체가 맞는 판단인지에 대한 기준을 제시해줍니다.

 

다중 공선성은 분산팽창요인(VIF)이라는 계수로 평가할 수 있습니다. 일반적으로 VIF 계수가 10~15 정도를 넘으면 그 피처는 다중 공선성의 문제가 있다고 판단합니다.

 


다중 공선성을 평가하는 코드는 다음과 같습니다.

 

# 회귀 분석 예측 성능을 높이기 위한 방법: 다중 공선성 확인

 

from statsmodels.stats.outliers_influence import variance_inflation_factor

# 피처마다의 VIF 계수를 출력합니다.
vif = pd.DataFrame()
vif["VIF Factor"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
vif["features"] = X.columns
vif.round(1)

 

#출력 결과

 

코드 실행 결과, 많은 개수의 피처가 다중 공선성 문제를 가진 것을 알 수 있습니다.

다중 공선성 확인 코드 출력 결과

 

분석 결과 시각화 하기

마지막으로 회귀 분석을 통해 얻어낸 예측연봉과 2018년 실제 연봉 데이터를 비교하는 시각화 자료를 만들어 보겠습니다.

 

다음 코드는 회귀 분석 모델의 predict() 함수를 사용하여 2018년의 연봉을 예측하고, 이를 원래의 데이터 프레임에 '예측연봉'이라는 새로운 열로 합치는 과정입니다.

 

전체 코드를 실행하면 [선수명, 실제연봉(2018), 예측연봉(2018), 작년연봉(2017)을 열로 하는 데이터 프레임을 출력할 수 있습니다.

(재계약 하지 않은 선수는 연봉에 변화가 없으므로 재계약하여 연봉이 변화한 선수만을 대상으로 합니다.)

 

# 2018년 연봉을 예측하여 데이터프레임의 column으로 생성합니다.
X = picher_df[['FIP', 'WAR', '볼넷/9', '삼진/9', '연봉(2017)']]
predict_2018_salary = lr.predict(X)
picher_df['예측연봉(2018)'] = pd.Series(predict_2018_salary)

# 원래의 데이터 프레임을 다시 로드합니다.
picher = pd.read_csv(picher_file_path)
picher = picher[['선수명', '연봉(2017)']]

# 원래의 데이터 프레임에 2018년 연봉 정보를 합칩니다.
result_df = picher_df.sort_values(by=['y'], ascending=False)
result_df.drop(['연봉(2017)'], axis=1, inplace=True, errors='ignore')
result_df = result_df.merge(picher, on=['선수명'], how='left')
result_df = result_df[['선수명', 'y', '예측연봉(2018)', '연봉(2017)']]
result_df.columns = ['선수명', '실제연봉(2018)', '예측연봉(2018)', '작년연봉(2017)']

# 재계약하여 연봉이 변화한 선수만을 대상으로 관찰합니다.
result_df = result_df[result_df['작년연봉(2017)'] != result_df['실제연봉(2018)']]
result_df = result_df.reset_index()
result_df = result_df.iloc[:10, :]
result_df.head(10)

# 출력 결과

 

예측연봉과 실제연봉 비교 코드 출력 결과

그래프에서 볼 수 있듯이 학습한 회귀 모델은 연봉 상승의 전체적인 경향을 비교적 잘 맞춰내고 있습니다. 피처가 몇 개 되지 않는 간단한 회귀 분석이라는 것을 생각해보면 기대한 것보다는 정확한 예측 결과라고 할 수 있을 것 같아요!

 

# 선수별 연봉 정보(작년 연봉, 예측 연봉, 실제 연봉)를 bar 그래프로 출력합니다.
mpl.rc('font', family='NanumGothicOTF')
result_df.plot(x='선수명', y=['작년연봉(2017)', '예측연봉(2018)', '실제연봉(2018)'], kind="bar")

 

#출력 결과

그래프 코드 출력 결과

 

자 고생하셨습니다!

 

지금까지 데이터 분석에서 가장 기초적인 예측 분석 방법인 회귀 분석을 알아보았습니다. 

 

다음에는 '비트코인 시세 예측하기'로 돌아오겠습니다.

 

 


회귀 분석 관련 이전 발행글 참조해서 더 디테일한 공부를 해보세요!

 

[파이썬/통계] Python 통계 : 분산분석/회귀모델의 분산분석 / statsmodels를 이용한 분산분석

분산분석은 정규선형모델에서 폭넓게 이용되는 검정 방법입니다. 분산분석(ANOVA)은 통계적으로 그룹 간 차이를 검정하기 위해 사용되는 방법입니다. 일반적으로 분산분석은 다음과 같은 상황에

jofresh.tistory.com

 

 

[파이썬/통계] Python 통계 : type 2 anova / 독립변수가 여럿인 모델 / t검정/ 적합한 독립변수 선택

독립변수가 여럿인 모델 매상 예측 모델에서는 습도, 기온, 날씨, 가격이라는 독립변수를 사용합니다. 날씨는 카테고리형 변수이며, 그 외에는 연속형 변수입니다. 복수의 독립변수를 가지고 있

jofresh.tistory.com

 

 

[파이썬/통계] Python 통계 : 일반선형모델 기본/ 로지스틱회귀 모델/null 모델

일반선형모델의 기본 예를 들어'있다, 없다'라는 두 개의 값만 취하는 데이터나 '1개,2개,3개'등 0 이상의 정수만 취하는 데이터가 있다면 모집단분포가 정규분포라고 가정하기에는 무리가 있습

jofresh.tistory.com

 

 

 

다양한 시각화 도구인 plot에 대한 이전 발행글 참조

 

[파이썬/통계] Python에 의한 기술 통계 : matplotlib·seaborn에 의한 데이터 가시화

안녕하세요! 조신선입니다. 오늘은 matplotlib·seaborn에 의한 데이터 가시화에 대해서 포스팅하겠습니다. 오늘도 역시나 쥬피터(vscode확장자 : ipynb) 환경으로 시작하겠습니다. 라이브러리 임포트 #

jofresh.tistory.com

 

 

모델 평가 관련 이전 발행글 참조

 

[파이썬/통계] Python 통계 : 분산분석/회귀모델의 분산분석 / statsmodels를 이용한 분산분석

분산분석은 정규선형모델에서 폭넓게 이용되는 검정 방법입니다. 분산분석(ANOVA)은 통계적으로 그룹 간 차이를 검정하기 위해 사용되는 방법입니다. 일반적으로 분산분석은 다음과 같은 상황에

jofresh.tistory.com

 

 

[파이썬/통계] Python 통계 : type 2 anova / 독립변수가 여럿인 모델 / t검정/ 적합한 독립변수 선택

독립변수가 여럿인 모델 매상 예측 모델에서는 습도, 기온, 날씨, 가격이라는 독립변수를 사용합니다. 날씨는 카테고리형 변수이며, 그 외에는 연속형 변수입니다. 복수의 독립변수를 가지고 있

jofresh.tistory.com


 

 

 

 

728x90
반응형