Data Analysis & ML/Machine Learning

[Machine Learning][머신러닝] 데이터 전처리(범주형/연속형)

YSY^ 2020. 8. 27. 12:11

이번 포스팅에서는 머신러닝 모델링을 위해 범주형데이터와 연속형데이터를 처리하는 방법을 알아보겠습니다.

범주형 데이터 전처리

  • 사이킷런은 문자열 값을 입력 값으로 처리 하지 않기 때문에 숫자 형으로 변환해야 한다.

범주형 변수 (Categorical Variable)

  • 몇 개의 범주 중 하나에 속하는 값들로 구성된 변수. 어떤 분류에 대한 속성을 가지는 변수를 말한다.
    • 예) 성별 - 남/녀, 혈액형 - A, B, AB, O, 성적 - A,B,C,D,F
  • 비서열(Unordered) 변수
    • 범주에 속한 값간에 서열(순위)가 없는 변수
    • 성별, 혈액형
  • 서열 (Ordered) 변수
    • 범주에 속한 값 간에 서열(순위)가 있는 변수
    • 성적, 직급
  • 사이킷런은 문자열 값을 입력 값으로 처리 하지 않기 때문에 숫자 형으로 변환해야 한다.
    • 범주형 변수의 경우 전처리를 통해 정수값으로 변환한다.
    • 범주형이 아닌 단순 문자열인 경우 일반적으로 제거한다.
  • 범주형 Feature의 처리방법은 두가지 방법이 있다. 해당 포스팅에서는 두 범주형 데이터 처리방법에 대해 알아본다.
    1. Label Encoding
    2. One hot Encoding

Label encoding (레이블 인코딩)

  • 문자열(범주형) 값을 내림차순 정렬 후 0 부터 1씩 증가하는 값으로 변환
  • 숫자의 차이가 모델에 영향을 주지 않는 트리 계열 모델(의사결정나무, 랜덤포레스트)에 적용한다.
  • 숫자의 차이가 모델에 영향을 미치는 선형 계열 모델(로지스틱회귀, SVM, 신경망)에는 사용하면 안된다.
  • sklearn.preprocessing.LabelEncoder 사용
    • fit(): 어떻게 변환할 지 학습
    • transform(): 문자열를 숫자로 변환
    • fit_transform(): 학습과 변환을 한번에 처리
    • inverse_transform():숫자를 문자열로 변환
    • classes_ : 인코딩한 클래스 조회
import numpy as np
from sklearn.preprocessing import LabelEncoder
items = ['TV','TV','냉장고','컴퓨터','냉장고','컴퓨터', '에어콘']
le = LabelEncoder()
le.fit(items) 
label = le.transform(items)

print(label, type(label))
# ==> [0 0 1 3 1 3 2] <class 'numpy.ndarray'>
print(le.classes_)
# ==> ['TV' '냉장고' '에어콘' '컴퓨터']

Label encoding 적용 예시 코드

Adult data Setting

  • 데이터셋은 1994년 인구조사 데이터 베이스에서 추출한 미국 성인의 소득 데이터셋이다.
  • target 은 income 이며 수입이 $50,000 이하인지 초과인지로 분류되 있다.
  • https://archive.ics.uci.edu/ml/datasets/adult
cols = ['age', 'workclass','fnlwgt','education', 'education-num', 'marital-status', 'occupation','relationship', 'race', 'gender','capital-gain','capital-loss', 'hours-per-week','native-country', 'income']
import pandas as pd
# df = pd.read_csv('data/adult.data', header=None, names=cols, na_values=' ?')
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data'
df = pd.read_csv(url, header=None, names=cols, na_values=' ?')
df.shape   # (32561, 15)
df = df.dropna()

 

범주형 데이터에 Label Encoding 적용

  • 범주형: 'workclass','education', 'education-num', 'marital-status', 'occupation','relationship', 'race', 'gender', 'hours-per-week','native-country', 'income'
  • 연속형: 'age', fnlwgt', 'capital-gain', 'capital-loss'
encoding_columns = ['workclass','education','marital-status', 'occupation','relationship','race','gender','native-country', 'income']
not_encoding_columns = ['age','fnlwgt', 'education-num','capital-gain','capital-loss','hours-per-week']
# 각 컬럼별로 라벨 인코딩을 처리할 함수 
# LabelEncoder 는 1차원 배열(리스트,ndarray, Series) 단위로 처리한다.

#인코딩전 원래 값들의 class들을 저장
# key:컬럼명, value:classes_
enc_classes = {} 
def encoding_label(x):  #x: 범주형 타입의 컬럼(Series)
    le = LabelEncoder()
    le.fit(x)
    label = le.transform(x)

    enc_classes[x.name] = le.classes_   #x.name: 컬럼명

    return label
d1 = df[encoding_columns].apply(encoding_label)
d2 = df[not_encoding_columns]
data = d1.join(d2)
data.head()

데이터셋 분리 및 모델링

y = data['income']#.values
X = data.drop(columns='income')#.values
type(y), type(X)

from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

#분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, 
                                                    stratify=y, random_state=1)

CF) stratify란?

  • classification을 다룰 때 매우 중요한 옵션값
  • stratify 값을 target으로 지정해주면 각각의 class 비율(ratio)을 train / validation에 유지 (한 쪽에 쏠리는 것 방지)
  • 만약 이 옵션을 지정하지 않고 classification 모델링을 한다면, 성능의 차이가 많이 날 수 있음
#모델 생성
tree = DecisionTreeClassifier(max_depth=9)
#학습
tree.fit(X_train, y_train)
#예측-검증
pred_train = tree.predict(X_train)
pred_test = tree.predict(X_test)
acc_train = accuracy_score(y_train, pred_train)
acc_test = accuracy_score(y_test, pred_test)

print(f'학습: {acc_train}, 테스트: {acc_test}')
# ==> 학습: 0.8621702268744376, 테스트: 0.8507017349983423

 

One-Hot encoding (원핫 인코딩)

  • N개의 클래스를 N 차원의 One-Hot 벡터로 표현되도록 변환
    • 고유값들을 피처로 만들고 정답에 해당하는 열은 1로 나머진 0으로 표시한다..
  • 숫자의 차이가 모델에 영향을 미치는 선형 계열 모델(로지스틱회귀, SVM, 신경망)에서 범주형 데이터 변환시 라벨 인코딩 보다 원핫 인코딩을 사용한다.

Sklearn의 OneHotEncoder

  • sklearn.preprocessing.OneHotEncoder
    • fit(): 어떻게 변환할 지 학습
    • transform(): 문자열를 숫자로 변환
    • fit_transform(): 학습과 변환을 한번에 처리
    • get_feature_names() : 원핫인코딩으로 변환된 컬럼의 이름을 반환
  • DataFrame을 넣을 경우 모든 변수들을 변환한다.
    • 범주형 컬럼만 처리하도록 해야 한다.
  • sparse를 False로 주지 않으면 scipy의 csr_matrix(희소행렬 객체)로 반환.
  • 희소행렬은 대부분 0으로 구성된 행렬과 계산이나 메모리 효율을 이용해 0이 아닌 값의 index만 관리한다.
  • csr_matrix.toarray()로 ndarray로 바꿀수 있다.

Sklearn의 OneHotEncoder 코드 예시

d = {
    'item':items,
    'cnt':np.arange(8)
}
df = pd.DataFrame(d)
df.info()

items[..., np.newaxis]
# ==> array([['TV'],
#       ['냉장고'],
#       ['컴퓨터'],
#       ['컴퓨터'],
#       ['냉장고'],
#       ['에어콘'],
#       ['에어콘'],
#       ['선풍기']], dtype='<U3')

from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder()
ohe.fit(items[..., np.newaxis])
ohv = ohe.transform(items[..., np.newaxis])
ohv, type(ohv)
ohv.toarray()
# ==> array([[1., 0., 0., 0., 0.],
#       [0., 1., 0., 0., 0.],
#       [0., 0., 0., 0., 1.],
#       [0., 0., 0., 0., 1.],
#       [0., 1., 0., 0., 0.],
#       [0., 0., 0., 1., 0.],
#       [0., 0., 0., 1., 0.],
#       [0., 0., 1., 0., 0.]])


from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(sparse=False)
ohe.fit(items[..., np.newaxis])
ohv = ohe.transform(items[..., np.newaxis])
ohv, type(ohv)
pd.DataFrame(ohv, columns=ohe.get_feature_names())

Pandas의 get_dummies

  • pandas.get_dummies(DataFrame [, columns=[변환할 컬럼명]]) 함수 이용
  • DataFrame에서 범주형(문자열) 변수만 변환한다.
  • 범주형 변수의 값을 숫자 값을 가지는 경우가 있다. (ex: 별점)
    • 이런 경우 get_dummies() columns=['컬럼명','컬럼명'] 매개변수로 컬럼들을 명시한다.

Pandas의 get_dummies 코드 예시

import numpy as np
import pandas as pd
items = np.array(['TV','냉장고', '컴퓨터','컴퓨터','냉장고','에어콘','에어콘','선풍기'])
pd.get_dummies(items)

One-Hot encoding 적용 예시 코드

Adult data Setting

## adult dataset에 one-hot encoding 적용
cols = ['age', 'workclass','fnlwgt','education', 'education-num', 'marital-status', 'occupation','relationship', 'race', 'gender','capital-gain','capital-loss', 'hours-per-week','native-country', 'income']
import pandas as pd
url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data'
df = pd.read_csv(url, header=None, names=cols, na_values=' ?')
df = df.dropna()
  • 범주형: 'workclass','education', 'education-num', 'marital-status', 'occupation','relationship', 'race', 'gender', 'hours-per-week','native-country', 'income'
  • 연속형: 'age', fnlwgt', 'capital-gain', 'capital-loss'
  • 위의 컬럼들중 'age','workclass','education', 'occupation', 'gender', 'hours-per-week', 'income' 만 사용한다.
  • income 은 순서형 데이터이기 때문에 Label Encoding으로 처리한다.
data_cols = ['age','workclass','education', 'occupation', 'gender', 'hours-per-week', 'income']
adult_df = data[data_cols]
adult_df.head()

범주형 데이터에 Label Encoding 및 One-Hot encoding적용

# income 컬럼 - LabelEncoding
import numpy as np
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit(adult_df['income'])
y = le.transform(adult_df['income'])
# y = le.fit_transform(adult_df['income'])
le.classes_, y[:5], np.unique(y)


# income을 제외한 나머지 컬럼들 one hot encoding
import pandas as pd
X = pd.get_dummies(adult_df[adult_df.columns[:-1]])
X.iloc[:2]

df = X.copy()
df['income'] = y
df.head()

데이터셋 분리 및 모델링

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

y = df['income']
X = df.drop(columns='income')

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=1)
print(X.shape, X_train.shape, X_test.shape)
# ==> (30162, 41) (22621, 41) (7541, 41)

from sklearn.metrics import accuracy_score
# 모델생성
lr = LogisticRegression(max_iter=1500)
# 학습
lr.fit(X_train, y_train)
# 검증
pred_train = lr.predict(X_train)
pred_test = lr.predict(X_test)

print(accuracy_score(y_train, pred_train))
# ==> 0.8086733566155342
print(accuracy_score(y_test, pred_test))
#==> 0.8042699907174115

 

연속형(수치형) 데이터 전처리

Feature Scaling(정규화)

  • 각 피처가 가지는 값들의 숫자 범위(Scale)가 다를 경우 이 값의 범위를 일정한 범위로 맞추는 작업
  • 트리계열을 제외한 대부분의 머신러닝 알고리즘들이 피처의 스케일에 영향을 받는다.
    • 선형모델, SVM 모델, 신경망 모델 등
  • Scaling(정규화)은 train set으로 fitting 한다. test set이나 예측할 새로운 데이터는 train set으로 fitting한 것으로 변환한다.
  • Scaling 종류
    • 표준화(Standardization)
    • Min Max Scaling

Feature Scaling 함수

  • fit(): 어떻게 변환할 지 학습
  • transform(): 변환
  • fit_transform(): 학습과 변환을 한번에 처리

표준화(StandardScaler)

  • 피쳐의 값들이 평균이 0이고 표준편차가 1인 범위(표준정규분포)에 있도록 변환한다.
    • 0을 기준으로 모든 데이터들이 모여있게 된다
  • 특히 SVM이나 선형회귀, 로지스틱 회귀 알고리즘(선형모델)은 데이터셋이 표준정규분포를 따를때 성능이 좋은 모델이기 때문에 표준화를 하면 대부분의 경우 성능이 향상된다.
  • sklearn.preprocessing.StandardScaler 를 이용

Skleaern StandardScaler 예시

from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler, MinMaxScaler
import pandas as pd

iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
# iris_df.head()
iris_df.describe()

표준화 전

scaler = StandardScaler()
scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
# iris_scaled.shape
iris_scaled_df = pd.DataFrame(iris_scaled, columns = iris.feature_names)
# iris_scaled_df.head()
iris_scaled_df.describe()

표준화 후

 

MinMaxScaler

  • 데이터셋의 모든 값을 0과 1 사이의 값으로 변환한다.

Skleaern MinMaxScaler 예시

mm_scaler = MinMaxScaler()
iris_scaled2 = mm_scaler.fit_transform(iris_df)
iris_scaled_df2 = pd.DataFrame(iris_scaled2, columns=iris.feature_names)
iris_scaled_df2.head()

Scaling(정규화) 적용 예시

위스콘신 유방암 데이터셋

  • 위스콘신 대학교에서 제공한 유방암 진단결과 데이터
  • ID, 암측정값들, 진단결과 컬럼들로 구성
  • 사이킷런에서 제공. load_breast_cancer() 함수 이용
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler

cancer = load_breast_cancer()
print(cancer.feature_names)
print(cancer.target_names)

데이터셋 분리 및 모델링

X_train, X_test, y_train, y_test = train_test_split(cancer.data, 
                                                    cancer.target, 
                                                    stratify=cancer.target,
                                                    random_state=0)

# stratify: default=None 입니다. classification을 다룰 때 매우 중요한 옵션값입니다. 
# stratify 값을 target으로 지정해주면 각각의 class 비율(ratio)을 train / validation에 유지해 줍니다.
# (한 쪽에 쏠려서 분배되는 것을 방지합니다) 
# 만약 이 옵션을 지정해 주지 않고 classification 문제를 다룬다면, 성능의 차이가 많이 날 수 있습니다.

from sklearn.svm import SVC

svm = SVC()
svm.fit(X_train, y_train)
pred_train = svm.predict(X_train)
pred_test = svm.predict(X_test)
print('train 정확도:', accuracy_score(y_train, pred_train))
print('test 정확도:', accuracy_score(y_test, pred_test))
# ==> train 정확도: 0.9225352112676056
# ==> test 정확도: 0.916083916083916

MinMaxScaler

  • 학습과 테스트 데이터로 나누기 전에 먼저 Scaling등의 데이터 전처리 진행
# MinMaxScaler
mm_scaler = MinMaxScaler()
# fit() -> Training Set
X_train_scaled = mm_scaler.fit_transform(X_train)
# 테스트 데이터에서는 다시 fit(), transform()이나 fit_transform()을 수행하지 않고 transform만 수행.
# 이를 수행하면 scaler 객체가 기존에 학습 데이터에 fit 했던 기준을 모두 무시하고 다시 테스트 데이터를 기반으로 기준을 적용하기 때문
X_test_scaled = mm_scaler.transform(X_test)
# 이런 번거로움을 피하기 위해 학습과 테스트 데이터로 나누기 전에 먼저 Scaling등의 데이터 전처리를 해주는 것이 좋음


svm = SVC()
svm.fit(X_train_scaled, y_train)

pred_train = svm.predict(X_train_scaled)
pred_test = svm.predict(X_test_scaled)
pred_test2 = svm.predict(X_test)

print('train 정확도:', accuracy_score(y_train, pred_train))
print('test 정확도(scaling한):', accuracy_score(y_test, pred_test))
print('test 정확도(scaling안한)', accuracy_score(y_test, pred_test2))
# ==> train 정확도: 0.9906103286384976
# ==> test 정확도(scaling한): 0.9440559440559441
# ==> test 정확도(scaling안한) 0.3706293706293706

StandardScaler

  • 학습과 테스트 데이터로 나누기 전에 먼저 Scaling등의 데이터 전처리 진행
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler

cancer = load_breast_cancer()
print(cancer.feature_names)
print(cancer.target_names)

#StandardScaler 
mm_scaler = StandardScaler()
cancer_data = cancer.data
cancer_target = cancer.target

scaled_cancer = mm_scaler.fit_transform(cancer_data)

X_train, X_test, y_train, y_test = train_test_split(scaled_cancer, 
                                                    cancer.target, 
                                                    test_size = 0.2,
                                                    stratify=cancer.target,
                                                    random_state=2)


svm = SVC()
svm.fit(X_train, y_train)

pred_train = svm.predict(X_train)
pred_test = svm.predict(X_test)

print('train 정확도(scaling한):', accuracy_score(y_train, pred_train))
print('test 정확도(scaling한):', accuracy_score(y_test, pred_test))
# ==> train 정확도: 0.9824175824175824
# ==> test 정확도(scaling한): 0.9824561403508771

 

728x90
반응형