본문 바로가기
머신러닝/Linear Regression

[통계] 회귀

by 미생22 2024. 5. 3.
728x90

머신러닝에서 여기는 회귀가 아닌, 통계적인 기초를 바탕으로 하는 회귀에 대해서 공부해보겠습니다.

우선 회귀모델이 잘 만들어졌는지 그래프로 확인할때 필요한 모듈을 불러옵니다.

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

 

데이터를 로드하겠습니다.

data_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/ecommerce.csv'
data = pd.read_csv(data_url)
data

 

위 데이터의 구조에 대해 알아보자면, E커머스 회사의 고객정보입니다.
- Avg. Session length : 한 번 접속했을 때 평균 어느 정도의 시간을 사용하는지에 대한 시간(분)
- Time on App : 폰 앱으로 접속했을 때 유지 시간(분)
- Time on Website : 웹사이트로 접속했을 때 유지시간(분)
- Length of Membership : 회원 자격 유지 기간 (연)
- Yearly Amount Spent : 1년동안 쓰는 금액

 

필요없는 컬럼을 삭제하겠습니다.

data.drop(['Email', 'Address', 'Avatar'], axis=1, inplace=True)
data.info()

우리가 가진 컬럼 5개가 모두 숫자형 데이터므로 boxplot으로 한번 그려보겠습니다.

 

plt.figure(figsize=(12, 6))
sns.boxplot(data=data);

마지막 컬럼때문에 나머지가 안보이므로 쟤를 빼고 그려보겠습니다.

plt.figure(figsize=(12, 6))
sns.boxplot(data=data.iloc[:,:-1])

특별히 이상한 점은 안보이네요.

plt.figure(figsize=(12, 6))
sns.boxplot(data=data['Yearly Amount Spent'])

얘도 정규분포표를 따를 것 같이 생겼습니다.
이제 pairplot을 그려보겠습니다.

plt.figure(figsize=(12, 6))
sns.pairplot(data=data);

1차원적으로 상관있어 보이는 것은 Yearly Amount Spent와 Length of Membership 뿐입니다.
lmplot으로 확인해보겠습니다.

 

plt.figure(figsize=(12, 6))
sns.lmplot(x='Length of Membership', y='Yearly Amount Spent', data=data);

 

다른 컬럼에 비해 상관관계가 있어보입니다.
컬럼이름이 길어서 formula로 사용하지 않고 ols로 바로 접근해서 사용해보겠습니다.

import statsmodels.api as sm

X = data['Length of Membership']
y = data['Yearly Amount Spent']

lm = sm.OLS(y, X).fit()

 

여기서 주의할 점, OLS()는 x,y 순서가 (y, X)로 두고 fit 시킵니다.

저번시간의 lm_model은 smf.ols(formula='y ~ x', data=df).fit()로 해서 lm_model.params로 a,b를 구했지만 이건 좀 다릅니다. lm의 summary()를 찾아 회귀리포트를 봅니다.

lm.summary()

회귀리포트에 의하면 R squared값도 있고, codf에 상관계수, std err도 있네요. P value도 있습니다.

 

좀 더 수치의 의미를 해석하려 해보겠습니다.

- R-squared : 모형 적합도, y의 분산을 각각의 변수들이 약 99.8%로 설명할 수 있음
- Adj. R-squared : 독립변수가 여러개인 다중회귀분석에서 사용 (독립변수가 여러개면 R-sq를 안보고 얘를 봅니다.)
- Prob. F-Statistic : 회귀모형에 대한 통계적 유의미성 검정. 이 값이 0.05 이하라면 모집단에서도 의미가 있다고 볼 수 있음

내가 가지고 있는 분포가 정규분포인가, 나의 데이터는 모집단을 잘 추정하느냐에 관심을 갖는게 통계적인 접근입니다.

scatterplot을 이용해 그려보겠습니다.

 

pred = lm.predict(X)

sns.scatterplot(x=X, y=y)
plt.plot(X, pred, 'r', ls='dashed', lw='3') #lw:line width

이렇게 lm만으로 formula없이 그리면 완전히 다른 그림이 됩니다. 이는 pred한 lm모델에 y절편인 상수항이 없기 때문입니다.

 

참값과 예측값이 얼마나 비슷한지 살펴보겠습니다. 따라서 x에 y(참값)을 두고 y값에 pred(예측값)을 둡니다.

기준선을 2개 그려주겠습니다.

sns.scatterplot(x=y, y=pred)
# 현재 상수항이 없다는걸 알려주기위해 기준선을 그리겠습니다. 실제값인 y의 min, max를 좌표로 잡습니다. 
plt.plot([min(y), max(y)], [min(y), max(y)], 'r', ls='dashed', lw=3)
# 또 하나의 기준선을 잡습니다. (0,0)부터 그리는 그래프입니다.
plt.plot([0, max(y)], [0, max(y)], 'b', ls='dashed', lw=3)
#plt.plot(X, pred, 'r', ls='dashed', lw='3');

거의 맞지않는걸 볼 수 있습니다.

0,0부터 그린 그래프와 동일합니다. 상수항이 실제 0인걸 보여주기 위해 그린 그림이었습니다 ㅎㅎ
이제 상수항을 넣어주겠습니다.

A의 행렬에서 [x1~ ,1~] 아래로 1,1,1,1,이 쭉 있어야하는데 그 열이 없는겁니다. 따라서 1로 된 열을 추가해줍니다.
열을 추가하는 방법은 pinkwink 블로그에 소개가 되어있습니다. 검색창에 numpy 열 추가라고 검색하면 뜹니다.
np.c_[]나 np.r_[]를 쓰면 열이나 행을 추가할 수 있습니다.

X = np.c_[X, [1]*len(X)] #1을 X길이만큼 곱해서 열로 추가하라는 뜻이다.
X

다시 모델을 fit시키겠습니다.
y는 그대로고 X에 추가했으니 X를 그대로 써줍니다.

 

lm = sm.OLS(y, X).fit()
lm.summary()

이제는 const와 x1의 값이 잡혀있습니다.
여기서 Rsq가 매우 작아졌습니다. 1에 가까울수록 좋은줄 알았는데 작아졌네요
대신 AIC 수치가 5264로 낮아졌으므로 괜찮습니다.
AIC : 원래의 정보를 얼마나 손실시켰는지의 정도입니다.
Rsq : 얼마나 편차를 적게 가지는지이기 때문에 너무 믿으면 안됩니다.

 

다시 선형회귀를 그려보겠습니다.

pred = lm.predict(X)

sns.scatterplot(x=X[:, 0], y=y) #X[:, 0]인 이유는 상수항을 빼야 그릴 수 있기 때문입니다.
plt.plot(X[:, 0], pred, 'r', ls='dashed', lw='3')

회귀식이 데이터에 맞게 그려지는 것을 볼 수 있습니다.

이제 데이터 분리해서 평가해보겠습니다.

 

from sklearn.model_selection import train_test_split

X = data.drop('Yearly Amount Spent', axis=1)
y = data['Yearly Amount Spent']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)

 

나머지 4개의 컬럼을 다 써서 OLS에 적용을 하는 중입니다.

lm = sm.OLS(y_train, X_train).fit()
lm.summary()

AIC가 더 낮아졌고 Rsq가 1에 가까운 값이 나왔네요. 굉장히 좋습니다.
이번에도 참값과 예측값을 비교해보겠습니다.
test 데이터로 성능을 확인해보겠습니다.

 

pred = lm.predict(X_test)

sns.scatterplot(x=y_test, y=pred) #이게 1:1 대각선에 모여있으면 잘된겁니다.
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'r', ls='dashed', lw=3)

728x90