빅데이터분석기사/작업형2

[작업형2] 에어비앤비 가격 예측하기(회귀)

seo0seok 2023. 6. 17. 21:27

// 퇴근후딴짓 님의 강의를 참고하였습니다. //

 

Dataset :

test.csv
1.35MB
train.csv
5.54MB
y_test.csv
0.03MB

 

문제) 에어비엔비 가격을 다음과 같은 형식의 CSV 파일로 생성하시오.

  • 평가: R-Squared, MAE, MSE, RMSE, RMSLE, MAPE
  • target : price(가격)
  • csv파일 생성 : 수험번호.csv (예시 아래 참조)

 

1. 데이터 불러오기

import pandas as pd

train = pd.read_csv("train.csv")
test =  pd.read_csv("test.csv")

→ train과 test 2개의 파일로 나누어져있다.

 

2. EDA

train.shape, test.shape

실행 결과 : 
(39116, 16) (9779, 15)
train.head(3)

→ train 데이터는 16개의 열로 이루어져있다.

 

test.head(3)

→ test 데이터는 15개의 열로 이루어져있다.

 

train.info()

실행 결과 :
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 39116 entries, 0 to 39115
Data columns (total 16 columns):
 #   Column                          Non-Null Count  Dtype  
---  ------                          --------------  -----  
 0   id                              39116 non-null  int64  
 1   name                            39102 non-null  object 
 2   host_id                         39116 non-null  int64  
 3   host_name                       39100 non-null  object 
 4   neighbourhood_group             39116 non-null  object 
 5   neighbourhood                   39116 non-null  object 
 6   latitude                        39116 non-null  float64
 7   longitude                       39116 non-null  float64
 8   room_type                       39116 non-null  object 
 9   minimum_nights                  39116 non-null  int64  
 10  number_of_reviews               39116 non-null  int64  
 11  last_review                     31063 non-null  object 
 12  reviews_per_month               31063 non-null  float64
 13  calculated_host_listings_count  39116 non-null  int64  
 14  availability_365                39116 non-null  int64  
 15  price                           39116 non-null  int64  
dtypes: float64(3), int64(7), object(6)

→ 3개의 실수형, 7개의 정수형, 6개의 object형으로 이루어져있다.

 

train.isnull().sum()

실행 결과 :
id                                   0
name                                14
host_id                              0
host_name                           16
neighbourhood_group                  0
neighbourhood                        0
latitude                             0
longitude                            0
room_type                            0
minimum_nights                       0
number_of_reviews                    0
last_review                       8053
reviews_per_month                 8053
calculated_host_listings_count       0
availability_365                     0
price                                0
dtype: int64
test.isnull().sum()

실행 결과 : 
id                                   0
name                                 2
host_id                              0
host_name                            5
neighbourhood_group                  0
neighbourhood                        0
latitude                             0
longitude                            0
room_type                            0
minimum_nights                       0
number_of_reviews                    0
last_review                       1999
reviews_per_month                 1999
calculated_host_listings_count       0
availability_365                     0
dtype: int64

→ train, test 데이터 모두 'name', 'host_name', 'last_review', 'review_per_month' 컬럼에 결측치가 있다.

 

3. 데이터 전처리 & 피처엔지니어링

train.nunique()

실행 결과 : 
id                                39116
name                              38447
host_id                           30821
host_name                          9943
neighbourhood_group                   5
neighbourhood                       221
latitude                          17378
longitude                         13607
room_type                             3
minimum_nights                       94
number_of_reviews                   377
last_review                        1720
reviews_per_month                   903
calculated_host_listings_count       47
availability_365                    366
price                               638
dtype: int64

→ 'name', 'host_name', 'last_review', 'host_id' 컬럼은 많은 행을 가지고 있어 제거할 예정이다.

→ 'review_per_month' 컬럼은 결측치에 0을 대입할 예정이다.

 

cols = ['name', 'host_name', 'last_review', 'host_id']
print(train.shape)
train = train.drop(cols, axis=1)
test = test.drop(cols, axis=1)
print(train.shape)

실행 결과 : 
(39116, 16)
(39116, 12)

→ 'name', 'host_name', 'last_review', 'host_id' 컬럼을 제거 후 컬럼이 16개에서 12개로 줄어들었다.

 

train['reviews_per_month'] = train['reviews_per_month'].fillna(0)
test['reviews_per_month'] = test['reviews_per_month'].fillna(0)
train.isnull().sum()

실행 결과 : 
id                                0
neighbourhood_group               0
neighbourhood                     0
latitude                          0
longitude                         0
room_type                         0
minimum_nights                    0
number_of_reviews                 0
reviews_per_month                 0
calculated_host_listings_count    0
availability_365                  0
price                             0
dtype: int64

 'review_per_month' 컬럼의 결측치를 0으로 대체 후 결측치 확인 시 결측치가 없는 것을 확인 할 수 있다.

 

train = train.drop('id', axis=1)
test_id = test.pop('id')

 제출파일에서 id로 사용될 'id' 컬럼을 제거 및 test_id 변수에 따로 담아둔다.

 

cols =['neighbourhood_group', 'neighbourhood', 'room_type']
from sklearn.preprocessing import LabelEncoder

for col in cols:
    le = LabelEncoder()
    train[col] = le.fit_transform(train[col])
    test[col] = le.transform(test[col])

train[cols]

→ object형 컬럼 3개를 Label 작업한다.

4. 검증 데이터 분리

from sklearn.model_selection import train_test_split
X_tr, X_val, y_tr, y_val = train_test_split(train.drop('price', axis=1), train['price'],
                                            test_size=0.15, random_state=2022)

→ target 컬럼인 'price' 를 train에서는 제거하고 test에서는 'price' 컬럼만 포함시켜 분리한다.

 

5. 모델 & 평가

# 평가
import numpy as np
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
def rmse(y_test, y_pred): #RMSE
    return np.sqrt(mean_squared_error(y_test, y_pred)) 

def rmsle(y_test, y_pred): #RMSLE
    return np.sqrt(np.mean(np.power(np.log1p(y_test) - np.log1p(y_pred), 2)))
    
def mape(y_test, y_pred): #MAPE
    return np.mean(np.abs((y_test - y_pred) / y_test)) * 100

→ rmse, rmsle, mape 평가 방법은 sklearn에서 제공되지 않아 함수로 직접 만들어야한다.

 

# 선형회귀
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_tr,y_tr)
pred=model.predict(X_val)
print("r2: ",r2_score(y_val, pred))
print("mae: ",mean_absolute_error(y_val, pred))
print("mse: ",mean_squared_error(y_val, pred))
print("rmse: ",rmse(y_val, pred))
print("rmsle: ",rmsle(y_val, pred))
print("mape: ",mape(y_val, pred))

실행 결과 : 
r2:  0.08452239774949566
mae:  73.98120907242394
mse:  44813.52112975366
rmse:  211.6920431422817
rmsle:  0.628466721627045
mape:  58.89870347351792
# 릿지
from sklearn.linear_model import Ridge

model = Ridge()
model.fit(X_tr,y_tr)
pred=model.predict(X_val)
print(r2_score(y_val, pred))
print("r2: ",r2_score(y_val, pred))
print("mae: ",mean_absolute_error(y_val, pred))
print("mse: ",mean_squared_error(y_val, pred))
print("rmse: ",rmse(y_val, pred))
print("rmsle: ",rmsle(y_val, pred))
print("mape: ",mape(y_val, pred))

실행 결과 : 
r2:  0.08458002418498156
mae:  73.94214514943864
mse:  44810.70025955658
rmse:  211.68538036330375
rmsle:  0.6215134552794792
mape:  58.858160278049596
# 라쏘
from sklearn.linear_model import Lasso

model = Lasso()
model.fit(X_tr,y_tr)
pred=model.predict(X_val)
print("r2: ",r2_score(y_val, pred))
print("mae: ",mean_absolute_error(y_val, pred))
print("mse: ",mean_squared_error(y_val, pred))
print("rmse: ",rmse(y_val, pred))
print("rmsle: ",rmsle(y_val, pred))
print("mape: ",mape(y_val, pred))

실행 결과 : 
r2:  0.07703411533588211
mae:  74.74494739225966
mse:  45180.07985423047
rmse:  212.55606284985257
rmsle:  0.6125581554187468
mape:  60.8858081365096
# 랜덤포레스트
from sklearn.ensemble import RandomForestRegressor

regressor = RandomForestRegressor()
model.fit(X_tr,y_tr)
pred=model.predict(X_val)
print("r2: ",r2_score(y_val, pred))
print("mae: ",mean_absolute_error(y_val, pred))
print("mse: ",mean_squared_error(y_val, pred))
print("rmse: ",rmse(y_val, pred))
print("rmsle: ",rmsle(y_val, pred))
print("mape: ",mape(y_val, pred))

실행 결과 : 
r2:  0.07703411533588211
mae:  74.74494739225966
mse:  45180.07985423047
rmse:  212.55606284985257
rmsle:  0.6125581554187468
mape:  60.8858081365096
# xgb
from xgboost import XGBRegressor

model = XGBRegressor()
model.fit(X_tr,y_tr)
pred=model.predict(X_val)
print("r2: ",r2_score(y_val, pred))
print("mae: ",mean_absolute_error(y_val, pred))
print("mse: ",mean_squared_error(y_val, pred))
print("rmse: ",rmse(y_val, pred))
print("rmsle: ",rmsle(y_val, pred))
print("mape: ",mape(y_val, pred))

실행 결과 : 
r2:  0.1870945928960972
mae:  66.26209479359112
mse:  39792.51217964102
rmse:  199.48060602384638
rmsle:  0.5132144368515108
mape:  48.07212870050863

→ 선형 회귀, 릿지, 라쏘, 랜덤포레스트, xgb 5개의 모델로 평가한 결과이다.

→ 오차에 기반한 평가방법으로 값이 작을수록 모델 성능이 뛰어난 것이다. xgb 모델이 가장 뛰어난 것으로 보인다.

 

6. 예측 및 csv 제출

pred = model.predict(test)
pred

실행 결과 : 
array([261.76437, 137.69249, 173.10406, ..., 176.9876 , 170.21184,
       211.4193 ], dtype=float32)

검증용 데이터가 아닌 실제 제출용 test 데이터로 예측한 결과이다.

array 안의 숫자는 에어비엔비 가격을 예측한 것이다.

 

pd.DataFrame({'id':test_id, 'output':pred}).to_csv("00000.csv", index=False)

문제 예시와 같이 'id' 컬럼에 test_id를 'output' 컬럼에 예측 가격을 넣어 데이터프레임을 만든다.

 

pd.read_csv("00000.csv")

제출용 csv 파일을 만든 후 read_csv로 불러와 제출이 제대로 되었는지 재확인한다.