개인 프로젝트

캘리포니아 주택 가격 예측 모델 만들기 - (2) feat.특성 스케일링

Jerry Jun 2020. 9. 17. 17:58
728x90

표지사진

이제 데이터를 만져봐야한다.

 

첫 번째 : total_bedrooms 에만 특성이 없는 경우들이 있었다. (total_bedrooms 만 20433 이다)

housing.info()
-----------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 10 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   longitude           20640 non-null  float64
 1   latitude            20640 non-null  float64
 2   housing_median_age  20640 non-null  float64
 3   total_rooms         20640 non-null  float64
 4   total_bedrooms      20433 non-null  float64
 5   population          20640 non-null  float64
 6   households          20640 non-null  float64
 7   median_income       20640 non-null  float64
 8   median_house_value  20640 non-null  float64
 9   ocean_proximity     20640 non-null  object 

이 경우를 해결할 수 있는 방법은 3가지이다.

  • 제거하기 - dropna()
  • Feature 삭제하기 - drop()
  • 다른 값으로 변경하기(0 or 평균 or 중간값) - fillna()

 

이번 경우는 데이터가 많은 경우가 아니기 때문에 삭제하기보다는 다른 값으로 변경해야겠다. 다른 값으로 변경하는 방법은 단순한 방법과 scikit-learn 을 이용한 방법이 있다.

 

1. 단순하게 변경하기

median = housing["total_bedrooms"].median()
housing["total_bedrooms"].fillna(median, inplace = True)

housing의 중간값을 median에 넣고 fillna() 를 사용하여 median을 삽입하는 방식이다. 바로 바꾸기 위해 inplace는 True로 선언했다.

 

 

2. Scikit-learn 이용하기

from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy = 'median')
housing_num = housing.drop('ocean_proximity', axis=1)
imputer.fit(housing_num)

SimpleImputer의 설정값을 'median'으로 하고 imputer 라는 객체를 생성한다. 그리고 housing에 단 한가지 문자형으로 되어있는 ocean_proximity를 제외하고 fit()을 이용해 중앙값으로 변환한다.

 

 

housing_num.info()
-------------------------------------
 0   longitude                 16512 non-null  float64
 1   latitude                  16512 non-null  float64
 2   housing_median_age        16512 non-null  float64
 3   total_rooms               16512 non-null  float64
 4   total_bedrooms            16354 non-null  float64
 5   population                16512 non-null  float64
 6   households                16512 non-null  float64
 7   median_income             16512 non-null  float64
 8   median_house_value        16512 non-null  float64
 9   rooms_per_household       16512 non-null  float64
 10  bedrooms_per_room         16354 non-null  float64
 11  population_per_household  16512 non-null  float64

하지만 아직 total_bedrooms의 non-null은 달라지지 않았다.

 

X = imputer.transform(housing_num)
housing_df = pd.DataFrame(X, index = list(housing.index.values), columns = housing_num.columns)
housing_df.info()
-------------------------------------------------------------
 0   longitude                 16512 non-null  float64
 1   latitude                  16512 non-null  float64
 2   housing_median_age        16512 non-null  float64
 3   total_rooms               16512 non-null  float64
 4   total_bedrooms            16512 non-null  float64
 5   population                16512 non-null  float64
 6   households                16512 non-null  float64
 7   median_income             16512 non-null  float64
 8   median_house_value        16512 non-null  float64
 9   rooms_per_household       16512 non-null  float64
 10  bedrooms_per_room         16512 non-null  float64
 11  population_per_household  16512 non-null  float64

X에 변환된 데이터들이 있지만 numpy.array 이므로 다시 DataFrame으로 만들어줍니다. 다시 정보를 확인하니 드디어 편안한 숫자가 되었습니다.

 

 

 

두 번째 : 텍스트 Feature를 숫자로 바꾸기

housing["ocean_proximity"].value_counts
----------------------------------------------
<bound method IndexOpsMixin.value_counts of 17606     <1H OCEAN
18632     <1H OCEAN
14650    NEAR OCEAN
3230         INLAND
3555      <1H OCEAN
            ...    
6563         INLAND
12053        INLAND
13908        INLAND
11159     <1H OCEAN
15775      NEAR BAY
Name: ocean_proximity, Length: 16512, dtype: object>

지금 경우의 데이터들 중 ocean_proximity만 문자형이다. 많은 종류들이 있는데 숫자로 바꾸긴 위해서 사용하는 것이 factorize() 이다. 텍스트를 숫자로 바꾸기 위해서 Pandas에서 지원하는 factorize() 메소드를 사용합니다. 

 

housing_ocean_num, housing_ocean_name = housing["ocean_proximity"].factorize()
print(housing_ocean_name)
print(housing_ocean_num[:5])
------------------------------------------------------------------------------
Index(['<1H OCEAN', 'NEAR OCEAN', 'INLAND', 'NEAR BAY', 'ISLAND'], dtype='object')
[0 0 1 2 0]

<1H OCEAN 은 0으로 매핑되고 NEAR OCEAN 은 1, INLAND 는 2... 등으로 매핑됩니다. 여기에서 알아야 할 것은 한 특성이 1일 때 나머지는 0이 되게끔 하는 원-핫 인코딩(One-Hot Encoding)입니다.

 

 

 

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(categories = 'auto')
housing_ocean_hot = encoder.fit_transform(housing_ocean_num.reshape(-1, 1))
housing_ocean_hot.toarray()
---------------------------------------------------------------------------
array([[1., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       ...,
       [0., 0., 1., 0., 0.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0.]])

사이킷런(scikit-learn)에서 지원하는 OneHotEncoder를 이용하여 카테고리의 숫자를 넣었던 housing_ocean_num을 바꾸었습니다. reshape을 한 이유는 fit_transform()에 들어가는 것은 2차원 배열이기 때문입니다. reshape에 -1의 의미는 자동으로 행을 결정하라는 의미입니다. 

 

 

 


# 특성 스케일링

데이터를 조작할 때 중요하게 생각해야 할 것 중 하나는 특성 스케일링입니다. 대부분의 머신러닝 알고리즘은 feature들의 스케일들이 각각 많이 다르면 잘 동작하지 않습니다.

print("전체 방 개수의 최소값 : ", min(housing["total_rooms"]))
print("전체 방 개수의 최대값 : ", max(housing["total_rooms"]))
--------------------------------------------------------------
전체 방 개수의 최소값 :  6.0
전체 방 개수의 최대값 :  39320.0

이 경우에서도 total_rooms 의 범위는 6 ~ 39320 인 반면 median_income의 범위는 0 ~ 15입니다. 그래서 이에 대한 문제의 해결책은 min-max 스케일링과 표준화(standardization)가 주로 사용됩니다.

 

[1] min-max 스케일링

- 쉽게 말하면 feature의 값들을 전부 0 ~ 1 범위 내로 바꾸는 겁니다. 계산으로는 해당 데이터에서 최소값을 뺀 후 최대값과 최소값의 차이로 나누는 것입니다. 하지만 사이킷런에서 제공하는 MinMaxScaler 를 통해 쉽게 변환할 수 있습니다. 물론 0 ~ 1로 고정되어 있지 않으므로 매개변수를 통해 변경할 수 있습니다.

 

 

[2] 표준화(standardization)

- <(데이터 - 평균) / 표준편차> 로 계산합니다. 범위가 정해져 있지 않은 것이 min-max 스케일링과 다른 점일 것입니다. 이 또한 사이킷런에서 제공하는 StandardScaler 를 통해 구현할 수 있습니다.

 


# 선형 회귀 모델로 훈련하기

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(housing_prepared, housing_labels)
------------------------------------------------
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

선형 모델이므로 scikit-learn의 LinearRegression 을 사용한다. 

 

 

some_data = housing.iloc[:5]
some_labels = housing_labels.iloc[:5]
some_data_prepared = full_pipeline.transform(some_data)

print("예측값:", lin_reg.predict(some_data_prepared))
print("실제값:", list(some_labels))
-------------------------------------------------------
예측값: [210644.60459286 317768.80697211 210956.43331178  59218.98886849 189747.55849879]
실제값: [286600.0, 340600.0, 196900.0, 46300.0, 254500.0]

선형 회귀 분석을 통한 예측이 끝났습니다. 결과는 많이 정확하게 나오지은 않았지만 오류없이 작동하였습니다.

300x250