데이터 전처리 Data Preprocessing
- 문자열 값을 입력값으로 허용하지 않음, 모든 문자열 값은 인코딩돼서 숫자형으로 변환해야 함
데이터 인코딩
레이블 인코딩 Label Encoding
- 카테고리 feature를 코드형 숫자값으로 변환하는 것
- LabelEncoder 클래스로 구현
In [2]:
from sklearn.preprocessing import LabelEncoder
items = ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
#LabelEncoder를 객체로 생성 후 fit과 transform으로 레이블 인코딩 수행
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
print('인코딩 변환값:', labels)
인코딩 변환값: [0 1 4 5 3 3 2 2]
In [3]:
#데이터가 많은 경우, 문자열 값이 어떤 숫자값으로 인코딩된지 모를 때 classes_속성값으로 확인
print('인코딩 클래스:', encoder.classes_)
인코딩 클래스: ['TV' '냉장고' '믹서' '선풍기' '전자레인지' '컴퓨터']
In [4]:
print('디코딩 원본값:', encoder.inverse_transform([4, 5, 2, 0, 1, 1, 3, 3]))
디코딩 원본값: ['전자레인지' '컴퓨터' '믹서' 'TV' '냉장고' '냉장고' '선풍기' '선풍기']
- but, 숫자값의 경우 크고 작음에 대한 특성이 작용하기 때문에, 레이블 인코딩이 일괄적인 숫자값으로 변환되면서 몇몇 ML알고리즘에는 예측 성능이 떨어지는 경우 발생
- 1보다 2가 더 크기 때문에 가중치가 더 부여된다거나 중요하게 인식할 가능성 발생 >> 선형 회귀에는 적용 X, tree계열은 OK
원-핫 인코딩 One-Hot Encoding
- feature값의 유형에 따라 새로운 feature를 추가해 고유 값에 해당하는 칼럼에만 1을 표시하고 나머진 0을 표시하는 방법
- 행 형태로 되어있는 피처의 고유값을 열 형태로 차원 변환한 후 해당 칼럼에만 1표시
- OneHotEncoder 클래스로 구현, 입력값으로 2차원 데이터 필요
- 변환한 값이 희소 행렬(sparse Matrix)형태이므로 toarray()를 이용해 밀집 행렬(Dense Matrix)로 변환해야 함
In [6]:
from sklearn.preprocessing import OneHotEncoder
import numpy as np
items = ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
#2차원으로 변환
items = np.array(items).reshape(-1, 1)
oh_encoder = OneHotEncoder()
oh_encoder.fit(items)
oh_labels = oh_encoder.transform(items)
print('원-핫 인코딩 데이터')
print(oh_labels.toarray())
print('원-핫 인코딩 데이터 차원')
print(oh_labels.shape)
원-핫 인코딩 데이터
[[1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 1.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0. 0.]]
원-핫 인코딩 데이터 차원
(8, 6)
- 판다스에서는 get_dummies()를 이용해서 숫자형으로 변환 없이도 쉽게 원-핫 인코딩을 할 수 있음
In [8]:
import pandas as pd
df = pd.DataFrame({'items': ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']})
pd.get_dummies(df)
Out[8]:
items_TV | items_냉장고 | items_믹서 | items_선풍기 | items_전자레인지 | items_컴퓨터 | |
---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 1 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 1 |
4 | 0 | 0 | 0 | 1 | 0 | 0 |
5 | 0 | 0 | 0 | 1 | 0 | 0 |
6 | 0 | 0 | 1 | 0 | 0 | 0 |
7 | 0 | 0 | 1 | 0 | 0 | 0 |
피처 스케일링과 정규화
- 피처 스케일링 feature scaling: 서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업, 대표적으로 표준화/정규화
- 표준화: 데이터의 피처 각각이 평균이 0이고 분산이 1인 가우시안 정규 분포를 가진 값으로 변환하는 것
- 정규화: 서로 다른 피처의 크기를 통일하기 위해 크기를 변환해주는 것, 모두 동일한 크기 단위로 비교하기 위해 값을 모두 최소 0 ~ 최대 1의 값으로
- 사이킷런에서 제공하는 Normalizer 모듈은 선형대수의 정규화 개념이 적용됌, 개별 벡터의 크기를 맞추기 위해 변환
StandardScaler
- 표준화를 지원
- 가우시안 정규 분포를 가질 수 있도록 사전에 데이터를 변환하는 것은 예측 성능 향상에 매우 중요 >> 특히 SVM(서포트 벡터 머신)이나 LinearRegression(션형 회귀), LogisticRegression(로지스틱 회귀)에서
In [9]:
from sklearn.datasets import load_iris
import pandas as pd
iris = load_iris()
iris_data = iris.data
iris_df = pd.DataFrame(iris_data, columns=iris.feature_names)
print('feature들의 평균값')
print(iris_df.mean())
print('\nfeature들의 분산값')
print(iris_df.var())
feature들의 평균값
sepal length (cm) 5.843333
sepal width (cm) 3.057333
petal length (cm) 3.758000
petal width (cm) 1.199333
dtype: float64
feature들의 분산값
sepal length (cm) 0.685694
sepal width (cm) 0.189979
petal length (cm) 3.116278
petal width (cm) 0.581006
dtype: float64
In [12]:
from sklearn.preprocessing import StandardScaler
#객체 생성
scaler = StandardScaler()
#fit이랑 transform으로 데이터 세트 변환
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature들의 평균값')
print(iris_df_scaled.mean())
print('\nfeature들의 분산값')
print(iris_df_scaled.var())
feature들의 평균값
sepal length (cm) -1.690315e-15
sepal width (cm) -1.842970e-15
petal length (cm) -1.698641e-15
petal width (cm) -1.409243e-15
dtype: float64
feature들의 분산값
sepal length (cm) 1.006711
sepal width (cm) 1.006711
petal length (cm) 1.006711
petal width (cm) 1.006711
dtype: float64
- 모든 칼럼의 평균이 0에 아주 가까운 값으로, 분산은 1에 아주 가까운 값으로 변환됐음을 알 수 있음
MinMaxScaler
- 데이터값을 0과 1 사이의 범위값으로 변환(음수값이 있으면 -1에서 1로 변환)
In [13]:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature들의 최소값')
print(iris_df_scaled.min())
print('\nfeature들의 최대값')
print(iris_df_scaled.max())
feature들의 최소값
sepal length (cm) 0.0
sepal width (cm) 0.0
petal length (cm) 0.0
petal width (cm) 0.0
dtype: float64
feature들의 최대값
sepal length (cm) 1.0
sepal width (cm) 1.0
petal length (cm) 1.0
petal width (cm) 1.0
dtype: float64
학습 데이터와 테스트 데이터의 스케일링 변환 시 유의점
- Scaler 객체를 이용해 학습 데이터 세트로 fit과 transform을 적용하면 테스트 데이터셋으로는 다시 fit을 수행하지 않고 학습데이터셋으로 fit을 수행한 결과를 이용해 transform 변환을 적용해야 한다는 것
- 즉, 학습 데이터로 fit이 적용된 스케일링 기준 정보를 그대로 테스트데이터에 적용해야 함
In [17]:
#문제가 되는 코드
from sklearn.preprocessing import MinMaxScaler
import numpy as np
train_array = np.arange(0, 11).reshape(-1, 1) #학습데이터를 0~10까지
test_array = np.arange(0, 6).reshape(-1, 1) #테스트데이터를 0~5까지
#MinMaxScaler 객체에 별도의 feature_range 파라미터값을 지정하지 않으면 0~1로 변환
scaler = MinMaxScaler()
scaler.fit(train_array)
train_scaled = scaler.transform(train_array)
print('원본 train_array 데이터:', np.round(train_array.reshape(-1), 2))
print('Scale된 train_array 데이터:', np.round(train_scaled.reshape(-1), 2))
원본 train_array 데이터: [ 0 1 2 3 4 5 6 7 8 9 10]
Scale된 train_array 데이터: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
In [18]:
#MinMaxScaler에 test_array를 fit하게되면 원본 데이터의 최소값이 0, 최대값이 5로 설정됨
scaler.fit(test_array)
#1/5 scale로 test_array 데이터 변환함. 원본 5 >> 1로 변환
test_scaled = scaler.transform(test_array)
#test_array의 scale 변환 출력
print('원본 test_array 데이터:', np.round(test_array.reshape(-1), 2))
print('scaled된 test_array 데이터:', np.round(test_scaled.reshape(-1), 2))
원본 test_array 데이터: [0 1 2 3 4 5]
scaled된 test_array 데이터: [0. 0.2 0.4 0.6 0.8 1. ]
- 반드시 테스트 데이터는 학습데이터의 스케일링 기준에 따라야 함
- 테스트 데이터에 다시 fit을 적용하면 안되고 학습데이터로 이미 fit이 적용된 scaler를 이용해 transform으로 변환해야 함
In [23]:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(train_array)
train_scaled = scaler.transform(train_array)
print('원본 train_array 데이터:', np.round(train_array.reshape(-1), 2))
print('Scale된 train_array 데이터:', np.round(train_scaled.reshape(-1), 2))
#test_array에 scale 변환 할때는 fit 호출하지 않고, transform만
test_scaled = scaler.transform(test_array)
print('원본 test_array 데이터:', np.round(test_array.reshape(-1), 2))
print('scaled된 test_array 데이터:', np.round(test_scaled.reshape(-1), 2))
원본 train_array 데이터: [ 0 1 2 3 4 5 6 7 8 9 10]
Scale된 train_array 데이터: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
원본 test_array 데이터: [0 1 2 3 4 5]
scaled된 test_array 데이터: [0. 0.1 0.2 0.3 0.4 0.5]
- 데이터셋 분리 전에 전체 데이터셋에 스케일링 적용한 뒤에 분리하는 것이 바람직
- PCA와 같은 차원 축소 변환이나 텍스트의 피처 벡터화 변환 작업 시에도 동일하게 적용됨
'Data Science > 파이썬 머신러닝 완벽 가이드' 카테고리의 다른 글
[sklearn] (9) - 데이터 스케일링 Data Scaling (0) | 2023.05.05 |
---|---|
[kaggle] 타이타닉 생존률 예측하기 (2) - 모델링 (0) | 2023.05.04 |
[sklearn] (7) - GridSearchCV (0) | 2023.05.02 |
[sklearn] (6) - train_test_split, KFold, StratifiedKFold, cross_val_score (0) | 2023.04.30 |
[sklearn] (5) scikit-learn 기반 프레임워크 익히기 (0) | 2023.04.30 |