👩‍💼

3.4 보스턴 주택가격 예측분석 Python - 2

1. 데이터 전처리

SAS에서 했던 작업을 그대로 Python에서도 진행해봅시다. 범주화를 제외한 나머지 작업을 거의 동일하게 진행해볼텐데요. 끝까지 잘 따라오세요.

1) 필요 패키지 불러오기

import pandas as pd import numpy as np from scipy.stats import norm %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns
 

2) 불필요한 데이터 제거

아까 분석과 동일하게 사용하지 않는 데이터를 제거합니다.
train_df.drop(['Street','Alley','Utilities','Condition2','RoofMatl','BsmtFinType2', 'BsmtFinSF2','Heating','LowQualFinSF','WoodDeckSF','OpenPorchSF', 'PoolArea','PoolQC','MiscFeature','MiscVal','MoSold','YrSold'], axis = 'columns', inplace=True) test_df.drop(['Street','Alley','Utilities','Condition2','RoofMatl','BsmtFinType2', 'BsmtFinSF2','Heating','LowQualFinSF','WoodDeckSF','OpenPorchSF', 'PoolArea','PoolQC','MiscFeature','MiscVal','MoSold','YrSold'], axis = 'columns', inplace=True)
 

3) 타겟변수 확인

왼쪽 그림을 보면 타겟변수가 한쪽으로 치우쳐져 있는 것을 동일한데요. 로그함수를 취했을때 정규분포처럼 보이는 것을 확인 할 수 있습니다. 타겟변수에 로그함수 취하는것은 모델 구축전에 수행하도록 하겠습니다.
#Target 변수 확인 figure, (ax1, ax2) = plt.subplots(nrows=1, ncols=2) figure.set_size_inches(15,5) sns.distplot(train_df['SalePrice'], fit=norm, ax=ax1) sns.distplot(np.log(train_df['SalePrice']), fit=norm, ax=ax2)
notion imagenotion image

4) 파생변수 만들기

4-1 외부 컨디션

다음과 같이 외부 컨디션에 대한 변수 중 범주형으로 되어있는 변수를 가공합니다. 4가지 변수를 더하여 New_var1를 생성합니다.
Condition_var = ['OverallQual','OverallCond','ExterQual','ExterCond'] Condition_Ex = train_df[Condition_var] Condition_Ex = Condition_Ex.replace(to_replace='Ex',value = 5) Condition_Ex = Condition_Ex.replace(to_replace='Gd',value = 4) Condition_Ex = Condition_Ex.replace(to_replace='TA',value = 3) Condition_Ex = Condition_Ex.replace(to_replace='Fa',value = 2) Condition_Ex = Condition_Ex.replace(to_replace='Po',value = 1) Condition_Ex = Condition_Ex.replace(to_replace='None',value = 0) Condition_Ex = Condition_Ex.replace(to_replace='',value = 0) Condition_Ex = Condition_Ex.replace(to_replace='NA',value = 0)
train_df['New_var1'] = Condition_Ex['OverallQual'] + Condition_Ex['OverallCond'] + Condition_Ex['ExterQual'] + Condition_Ex['ExterCond']
테스트 데이터도 동일하게 작업해 줍니다.
Condition_var = ['OverallQual','OverallCond','ExterQual','ExterCond'] Condition_Ex = test_df[Condition_var] Condition_Ex = Condition_Ex.replace(to_replace='Ex',value = 5) Condition_Ex = Condition_Ex.replace(to_replace='Gd',value = 4) Condition_Ex = Condition_Ex.replace(to_replace='TA',value = 3) Condition_Ex = Condition_Ex.replace(to_replace='Fa',value = 2) Condition_Ex = Condition_Ex.replace(to_replace='Po',value = 1) Condition_Ex = Condition_Ex.replace(to_replace='None',value = 0) Condition_Ex = Condition_Ex.replace(to_replace='',value = 0) Condition_Ex = Condition_Ex.replace(to_replace='NA',value = 0)
test_df['New_var1'] = Condition_Ex['OverallQual'] + Condition_Ex['OverallCond'] + Condition_Ex['ExterQual'] + Condition_Ex['ExterCond']
 

4-2 내부면적

내부면적때는 SAS와 같이 Outliar를 제거를 먼저 합니다. 다음과 같이 drop문을 사용하면 쉽게 제거할 수 있습니다.
train_df=train_df.drop(train_df[(train_df['TotalBsmtSF'] > 4000) | (train_df['_1stFlrSF'] > 300000) | (train_df['_2ndFlrSF'] > 1700) | (train_df['GrLivArea'] > 4000)].index)
내부 면적에 대한 4가지 변수를 더하여 New_var2를 생성합니다. 테스트 데이터도 동일하게 진행합니다.
train_df['New_var2'] = train_df['TotalBsmtSF'] + train_df['_1stFlrSF'] + train_df['_2ndFlrSF'] + train_df['GrLivArea'] test_df['New_var2'] = test_df['TotalBsmtSF'] + test_df['_1stFlrSF'] + test_df['_2ndFlrSF'] + test_df['GrLivArea']
 

4-3 욕실개수

욕실개수는 반욕실에 대해서 1/2처리를 하여 4가지 변수를 더하기만 하면 됩니다. 테스트 데이터도 동일하게 처리합니다.
test_df['New_var2'] = test_df['TotalBsmtSF'] + test_df['_1stFlrSF'] + test_df['_2ndFlrSF'] + test_df['GrLivArea'] test_df['New_var3'] = test_df['BsmtFullBath'] + test_df['BsmtHalfBath']/2 + test_df['FullBath'] + test_df['HalfBath']/2
 

4-4 지하실 상태

지하실 상태는 모든 변수가 범주형변수이기 때문에 다음과 같이 처리합니다. 지하실 상태에 대한 4가지 변수를 더하여 New_var4를 생성합니다.
Under_Cond_var1 = ['BsmtQual','BsmtCond'] Under_Cond_Ex1 = train_df[Under_Cond_var1] Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Ex',value = 5) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Gd',value = 4) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='TA',value = 3) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Fa',value = 2) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Po',value = 1) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='None',value = 0) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='',value = 0) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='NA',value = 0) Under_Cond_var2 = ['BsmtExposure','BsmtFinType1'] Under_Cond_Ex2 = train_df[Under_Cond_var2] Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Gd',value = 4) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Av',value = 3) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Mn',value = 2) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='No',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='None',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='NA',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='GLQ',value = 4) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='ALQ',value = 3) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='BLQ',value = 2) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Rec',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='LwQ',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Unf',value = 1) train_df['New_var4'] = Under_Cond_Ex1['BsmtQual'] + Under_Cond_Ex1['BsmtCond'] +Under_Cond_Ex2['BsmtExposure'] + Under_Cond_Ex2['BsmtFinType1']
Under_Cond_var1 = ['BsmtQual','BsmtCond'] Under_Cond_Ex1 = test_df[Under_Cond_var1] Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Ex',value = 5) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Gd',value = 4) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='TA',value = 3) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Fa',value = 2) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='Po',value = 1) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='None',value = 0) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='',value = 0) Under_Cond_Ex1 = Under_Cond_Ex1.replace(to_replace='NA',value = 0) Under_Cond_var2 = ['BsmtExposure','BsmtFinType1'] Under_Cond_Ex2 = test_df[Under_Cond_var2] Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Gd',value = 4) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Av',value = 3) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Mn',value = 2) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='No',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='None',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='NA',value = 0) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='GLQ',value = 4) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='ALQ',value = 3) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='BLQ',value = 2) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Rec',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='LwQ',value = 1) Under_Cond_Ex2 = Under_Cond_Ex2.replace(to_replace='Unf',value = 1) test_df['New_var4'] = Under_Cond_Ex1['BsmtQual'] + Under_Cond_Ex1['BsmtCond'] +Under_Cond_Ex2['BsmtExposure'] + Under_Cond_Ex2['BsmtFinType1']

4-5 부지 평탄도

부지평탄도에 대한 변수도 모두 범주형이기 때문에 다음과 같이 처리합니다. 2가지 변수를 더하여 New_var5를 생성합니다.
Land_var = ['LotShape','LandContour'] Land_Ex = train_df[Land_var] Land_Ex = Land_Ex.replace(to_replace='IR1',value = 0) Land_Ex = Land_Ex.replace(to_replace='IR2',value = 0) Land_Ex = Land_Ex.replace(to_replace='IR3',value = 0) Land_Ex = Land_Ex.replace(to_replace='Reg',value = 1) Land_Ex = Land_Ex.replace(to_replace='None',value = 0) Land_Ex = Land_Ex.replace(to_replace='',value = 0) Land_Ex = Land_Ex.replace(to_replace='NA',value = 0) Land_Ex = Land_Ex.replace(to_replace='Bnk',value = 0) Land_Ex = Land_Ex.replace(to_replace='HLS',value = 0) Land_Ex = Land_Ex.replace(to_replace='Low',value = 0) Land_Ex = Land_Ex.replace(to_replace='Lvl',value = 1) train_df['New_var5'] = Land_Ex['LotShape'] + Land_Ex['LandContour']
Land_var = ['LotShape','LandContour'] Land_Ex = test_df[Land_var] Land_Ex = Land_Ex.replace(to_replace='IR1',value = 0) Land_Ex = Land_Ex.replace(to_replace='IR2',value = 0) Land_Ex = Land_Ex.replace(to_replace='IR3',value = 0) Land_Ex = Land_Ex.replace(to_replace='Reg',value = 1) Land_Ex = Land_Ex.replace(to_replace='None',value = 0) Land_Ex = Land_Ex.replace(to_replace='',value = 0) Land_Ex = Land_Ex.replace(to_replace='NA',value = 0) Land_Ex = Land_Ex.replace(to_replace='Bnk',value = 0) Land_Ex = Land_Ex.replace(to_replace='HLS',value = 0) Land_Ex = Land_Ex.replace(to_replace='Low',value = 0) Land_Ex = Land_Ex.replace(to_replace='Lvl',value = 1) test_df['New_var5'] = Land_Ex['LotShape'] + Land_Ex['LandContour']
 

4-6 차고상태

차고상태에서도 범주형 변수 처리가 필요한 변수가 있기 때문에 다음과 같이 처리하고, 3가지 변수를 더하여 New_var6을 생성합니다.
Garage_var = ['GarageFinish','GarageQual','GarageCond'] Garage_Ex = train_df[Garage_var] Garage_Ex = Garage_Ex.replace(to_replace='Ex',value = 5) Garage_Ex = Garage_Ex.replace(to_replace='Gd',value = 4) Garage_Ex = Garage_Ex.replace(to_replace='TA',value = 3) Garage_Ex = Garage_Ex.replace(to_replace='Fa',value = 2) Garage_Ex = Garage_Ex.replace(to_replace='Po',value = 1) Garage_Ex = Garage_Ex.replace(to_replace='None',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='NA',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='Fin',value = 3) Garage_Ex = Garage_Ex.replace(to_replace='RFn',value = 2) Garage_Ex = Garage_Ex.replace(to_replace='Unf',value = 1) train_df['New_var6'] = Garage_Ex['GarageFinish'] + Garage_Ex['GarageQual'] + Garage_Ex['GarageCond']
Garage_var = ['GarageFinish','GarageQual','GarageCond'] Garage_Ex = test_df[Garage_var] Garage_Ex = Garage_Ex.replace(to_replace='Ex',value = 5) Garage_Ex = Garage_Ex.replace(to_replace='Gd',value = 4) Garage_Ex = Garage_Ex.replace(to_replace='TA',value = 3) Garage_Ex = Garage_Ex.replace(to_replace='Fa',value = 2) Garage_Ex = Garage_Ex.replace(to_replace='Po',value = 1) Garage_Ex = Garage_Ex.replace(to_replace='None',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='NA',value = 0) Garage_Ex = Garage_Ex.replace(to_replace='Fin',value = 3) Garage_Ex = Garage_Ex.replace(to_replace='RFn',value = 2) Garage_Ex = Garage_Ex.replace(to_replace='Unf',value = 1)
test_df['New_var6'] = Garage_Ex['GarageFinish'] + Garage_Ex['GarageQual'] + Garage_Ex['GarageCond']
 

4-7 건축연도

건축연도는 간단한 사칙연산을 통해 두 값을 더하여 2로 나누어 평균을 구합니다. 이 값을 New_var7로 저장합니다.
train_df['New_var7'] = ((2020 - train_df['YearBuilt']) + (2020 - train_df['YearRemodAdd'])) / 2 test_df['New_var7'] = ((2020 - test_df['YearBuilt']) + (2020 - test_df['YearRemodAdd'])) / 2
 

4-8 Missing 및 NA값 처리

먼저 Train 데이터와 Test 데이터의 파생변수에 사용되었던 변수를 제거하고, 변수 속성이 다르기 때문에 각 속성에 맞춰 None, 0, 최빈값으로 대체하여 Missing 값 및 NA값을 처리합니다.
# Train 파생변수 관련 변수 제외 train_df.drop(['OverallQual','OverallCond','ExterQual','ExterCond', 'TotalBsmtSF','_1stFlrSF','_2ndFlrSF','GrLivArea', 'BsmtFullBath','BsmtHalfBath','FullBath','HalfBath', 'BsmtQual','BsmtCond','BsmtExposure','BsmtFinType1', 'LotShape','LandContour', 'GarageFinish','GarageQual','GarageCond', 'YearBuilt','YearRemodAdd','Electrical'], axis = 'columns', inplace=True)
# Test 파생변수 관련 변수 제외 test_df.drop(['OverallQual','OverallCond','ExterQual','ExterCond', 'TotalBsmtSF','_1stFlrSF','_2ndFlrSF','GrLivArea', 'BsmtFullBath','BsmtHalfBath','FullBath','HalfBath', 'BsmtQual','BsmtCond','BsmtExposure','BsmtFinType1', 'LotShape','LandContour', 'GarageFinish','GarageQual','GarageCond', 'YearBuilt','YearRemodAdd','Electrical'], axis = 'columns', inplace=True)
for col in ('MasVnrType','FireplaceQu','GarageType','Fence'): train_df[col] = train_df[col].fillna('None') , for col in ('New_var1','New_var2','New_var3','New_var4','New_var5','New_var6','New_var7', 'LotFrontage','MasVnrArea','GarageYrBlt', 'BsmtFinSF1' ,'GarageCars', 'GarageArea', 'BsmtUnfSF'): train_df[col] = train_df[col].fillna(0) for col in ('MSZoning', 'KitchenQual', 'Exterior1st', 'Exterior2nd', 'SaleType', 'Functional'): train_df[col] = train_df[col].fillna(test_df[col].mode()[0])
for col in ('MasVnrType','FireplaceQu','GarageType','Fence'): test_df[col] = test_df[col].fillna('None') for col in ('New_var1','New_var2','New_var3','New_var4','New_var5','New_var6','New_var7' 'LotFrontage','MasVnrArea','GarageYrBlt', 'BsmtFinSF1' ,'GarageCars', 'GarageArea', 'BsmtUnfSF'): test_df[col] = test_df[col].fillna(0) for col in ('MSZoning', 'KitchenQual', 'Exterior1st', 'Exterior2nd', 'SaleType', 'Functional'): test_df[col] = test_df[col].fillna(test_df[col].mode()[0])

4-9 범주형 변수 더미화

전처리의 마지막 부분입니다. 앞서 분석했었을때와 동일하게 범주형 변수에 대해 더미화를 시킵니다.
dummy_list = list(train_df.select_dtypes(include =['object']).columns) dummies = pd.get_dummies(train_df[dummy_list], prefix = dummy_list) train_df.drop(dummy_list, axis = 1, inplace = True) X = pd.concat([train_df, dummies], axis = 1) dummy_list = list(test_df.select_dtypes(include =['object']).columns) dummies = pd.get_dummies(test_df[dummy_list], prefix = dummy_list) test_df.drop(dummy_list, axis = 1, inplace = True) X = pd.concat([test_df, dummies], axis = 1)

2. 모델 구축

이번 장에서는 앞서 SAS로 분석했던 Lasso와 더불어 머신러닝 모델인 Gredient Boosting, XGBoost를 활용하여 모델을 구축하고 결과를 캐글에 제출해보도록 합시다.

1) 분석 데이터 준비

앞서 Target변수의 정규분포화를 위해 로그함수를 취해줍니다. 그리고 Train_df(분석변수) 와 Train_target(타겟변수)로 데이터를 나눠줍니다.
train_target=train_df['SalePrice'] train_df=train_df[list(test_df)] train_target=np.log(train_target)

2) Lasso 모델 구축

2-1 Lasso 모델 불러오기

from sklearn.linear_model import Lasso

2-2 Lasso 모델 구축

Lasso = Lasso() Lasso.fit(train_df, train_target)

2-3 결과 데이터 생성

predict = Lasso.predict(test_df) predict = np.exp(predict) result =pd.DataFrame({ 'Id': test_df['Id'], 'SalePrice': predict }) result.to_csv('result_Lasso1.csv', index=False)

2-4 결과 확인

데이터 전처리를 통해 결과가 확실히 좋아진것을 확인할 수 있습니다.
notion imagenotion image

3) Gradient Boosting 모델 구축

Gradient Boosting 모델의 학습방법은 쉽게 말하여 약한 부분을 결합하고 틀린 값에 가중치를 부여하여 더 좋은 모델을 만들어 나가는 방법입니다. 모델을 변경해서 분석해봅시다.

3-1 Gradient Boosting 모델 불러오기

from sklearn.ensemble import GradientBoostingRegressor from sklearn import metrics

3-2 Gradient Boosting 모델 구축

GBoost = GradientBoostingRegressor() GBoost.fit(train_df, train_target)

3-3 결과 데이터 생성

predict = GBoost.predict(test_df) predict = np.exp(predict) result =pd.DataFrame({ 'Id': test_df['Id'], 'SalePrice': predict }) result.to_csv('result_GBoost.csv', index=False)

3-4 결과 확인

Lasso 보다 Gradient Boosting 모델의 결과가 확실히 좋아진것을 확인할 수 있습니다.
notion imagenotion image
 

4) XGBoost 모델 구축

XGBoost 모델은 분류와 회귀모델에서 뛰어난 예측능력을 보이는 모델로 최근에 캐글에서 각광받았던 모델입니다. Gredient Boosting의 단점을 해결하여 더욱 좋은 모델 성능을 보이므로 많이 사용됩니다.

4-1 XGBoost 모델 불러오기

from xgboost import XGBRegressor

4-2 Lasso 모델 구축

XGB=XGBRegressor() XGB.fit(train_df, train_target)

4-3 결과 데이터 생성

predict = XGB.predict(test_df) predict = np.exp(predict) result =pd.DataFrame({ 'Id': test_df['Id'], 'SalePrice': predict }) result.to_csv('result_XGB.csv', index=False)

4-4 결과 확인

이번 분석에서는 Gradient Boosting 모델보다는 약간 떨어지지만 여전히 좋은 결과를 얻을 수 있습니다.
notion imagenotion image
 

5) Mixed 모델

세가지 모델의 결과의 평균을 Kaggle에 제출해봅시다. SAS에서도 Mixed모델이 가장 좋은 성능을 냈었습니다.
결과를 보니 이번에는 Gradient Boosting을 단독으로 쓴 모델이 가장 좋네요.
notion imagenotion image