Day 11 - 핸즈온 머신러닝 2장 - part 5

채원·2025년 7월 22일

핸즈온 머신러닝

목록 보기
6/6

데이터 정제

먼저, total_bedrooms 특성에 값이 없는 경우가 있는데, 이를 수정해보자. 방법에는 다음과 같이 세 가지가 있다.

  1. 해당 구역을 제거하기
  2. 전체 특성을 삭제하기
  3. 대체 : 누락된 값을 어떤 값으로 채우기

판다스의 dropana(), drop(), fillna() 메서드로 이런 작업을 간단하게 처리할 수 있다.

# housing.dropna(subset=["total_bedrooms"], inplace=True)  
  
# housing.drop("total_bedrooms", axis=1, inplace=True)  
  
median = housing["total_bedrooms"].median()  
housing["total_bedrooms"].fillna(median, inplace=True)

여기까지는 저번 파트에서 다루었던 내용이다.


세 번째 옵션이 누락된 값을 중간값으로 채우는 코드이다.
위와 같은 동작을 사이킷런의 SimpleImputer 클래스를 통해 할 수 있다. 이 클래스는 각 특성의 중간값을 저장하고 있어 유용하다. 또한, 훈련 세트뿐만 아니라 검증 세트와 테스트 세트 그리고 모델에 주입될 새로운 데이터에 있는 누락된 값을 대체할 수 있다.

다음과 같이 누락된 값을 특성의 중간값으로 대체하도록 지정하여 SimpleImputer의 객체를 생성한다.

from sklearn.impute import SimpleImputer  
  
imputer = SimpleImputer(strategy="median")

중간값은 수치형 특성에서만 계산될 수 있으므로 수치 특성만 가진 데이터 복사본을 생성한다.

housing_num = housing.select_dtypes(include=[np.number])

이후, imputer 객체의 fit() 메서드를 사용해 훈련 데이터에 적용할 수 있다.

imputer.fit(housing_num)

imputer은 각 특성의 중간값을 계산하여 그 결과를 객체의 statistics_ 속성에 저장한다.
이후, 학습된 impuer 객체를 사용해 훈련 세트에서 누락된 값을 학습된 중간값으로 바꿀 수 있다.

X = imputer.transform(housing_num)

누락된 값을 평균이나 가장 자주 등장하는 값, 상수로 바꾸는 것도 가능하다. 뒤 두 가지 방법은 수치가 아닌 데이터도 지원한다.

사이킷런 변환기는 판다스 데이터프레임이 입력되더라도 넘파이 배열이나 사이파이 희소 행렬을 출력한다. 따라서 imputer.transform(housing_num) 의 출력 또한 넘파이 배열이다.

type(X)

>> numpy.ndarray

현재 상태에서는 열 이름도 인덱스도 없기 때문에, 이를 데이터프레임으로 감싸서 열 이름과 인덱스를 복원할 수 있다.

housing_tr = pd.DataFrame(X, columns=housing_num.columns,  
                          index=housing_num.index)

텍스트와 범주형 특성 다루기

위에서는 수치형 특성만을 다뤘고, 이제는 텍스트 특성을 살펴보자.

housing_cat = housing[["ocean_proximity"]]  
housing_cat.head(8)

      ocean_proximity
13096        NEAR BAY
14973       <1H OCEAN
3785           INLAND
14689          INLAND
20507      NEAR OCEAN
1286           INLAND
18078       <1H OCEAN
4396         NEAR BAY
housing_cat.value_counts()

ocean_proximity
<1H OCEAN          7274
INLAND             5301
NEAR OCEAN         2089
NEAR BAY           1846
ISLAND                2
Name: count, dtype: int64

이를 통해, 이 특성은 범주형 특성임을 알 수 있다.

이 카테고리를 텍스트에서 숫자로 변환하기 위해 사이킷런의 OrdinalEncoder을 사용한다.

from sklearn.preprocessing import OrdinalEncoder  
  
ordinal_encoder = OrdinalEncoder()  
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)

인코딩된 몇 개의 값을 확인하면 다음과 같다.

housing_cat_encoded[:8]

array([[3.],
       [0.],
       [1.],
       [1.],
       [4.],
       [1.],
       [0.],
       [3.]])

categories_ 인스턴스 변수를 사용해 카테고리 리스트를 얻을 수 있다. 범주형 특성마다 1D 카테고리 배열을 담은 리스트가 반환된다.

ordinal_encoder.categories_

[array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'],
       dtype=object)]

이런 표현 방식의 문제는 머신러닝 알고리즘이 가까이 있는 두 값을 떨어져 있는 두 값보다 더 비슷하다고 생각한다는 점이다. (숫자 상으로 비슷하면, 실제로는 전혀 다른 특징을 가진 값이어도 비슷하게 인식하게 된다.)

이러한 문제는 카테고리별 이진 특성을 만들어 해결한다. 카테고리가 <1H OCEAN일 때 한 특성이 1이고, 카테고리가 INLAND일 때 다른 한 특성이 1이 되는 식이다.
한 특성만 1이고 나머지는 0이므로 이를 원-핫 인코딩이라고 부른다. 새로운 특성을 더미 특성이라고도 부른다. 사이킷런은 범주 값을 원-핫 벡터로 바꾸기 위한 OneHotEncoder 클래스를 제공한다.

from sklearn.preprocessing import OneHotEncoder  
  
cat_encoder = OneHotEncoder()  
housing_cat_1hot = cat_encoder.fit_transform(housing_cat)

OneHotEncoder의 출력은 넘파이 배열이 아니라 사이파이 희소 행렬이다.

housing_cat_1hot

<16512x5 sparse matrix of type '<class 'numpy.float64'>'
	with 16512 stored elements in Compressed Sparse Row format>

희소 행렬은 0이 대부분인 행렬을 매우 효율적으로 표현한다. 내부적으로 0이 아닌 값과 그 위치만 저장한다. 이는 많은 메모리를 절약하고 계산 속도를 높여주는 효과를 가진다. 공식 documentation

대부분 희소 행렬을 보통의 2D 배열처럼 사용할 수 있지만, 넘파이 배열로 바꾸려면 toarray() 메서드를 호출해야 한다.

housing_cat_1hot.toarray()

array([[0., 0., 0., 1., 0.],
       [1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       ...,
       [0., 0., 0., 0., 1.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1.]])
profile
학부생

0개의 댓글