이제 데이터를 만져봐야한다.
첫 번째 : 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]
선형 회귀 분석을 통한 예측이 끝났습니다. 결과는 많이 정확하게 나오지은 않았지만 오류없이 작동하였습니다.
'개인 프로젝트' 카테고리의 다른 글
캘리포니아 주택 가격 예측 모델 만들기 - (1) (0) | 2020.09.15 |
---|