#!/usr/bin/env python3


One-Hot-Encoding으로 wave 데이터셋 분석


데이터를 가장 잘 표현하는 방법은 데이터가 가진 의미뿐 아니라 어떤 모델을 사용하는지에 따라 다름

아주 폭넓게 사용하는 두 알고리즘인

linear 모델과 tree 기반 모델(decision tree, gradient boosting tree, random forest 등)

특성의 표현 방식으로 인해 미치는 영향이 매우 다름



1. wave 데이터셋(인위적 데이터)으로 확인하기

이 데이터에는 입력 특성이 하나뿐

이 데이터셋을 이용해 linear regression 과 decision tree regression tree를 비교


# library import

from sklearn.linear_model import LinearRegression

from sklearn.tree import DecisionTreeRegressor

from mglearn.datasets import make_wave

import numpy as np

import matplotlib.pyplot as plt

import matplotlib


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic'# 한글

plt.rcParams['axes.unicode_minus'] = False # 축 -


# dataset

x, y = make_wave(n_samples=100)

line = np.linspace(-3, 3, num=1000, endpoint=False).reshape(-1, 1)


# model fit

# decision tree

decision_tree_reg = DecisionTreeRegressor(min_samples_split=3).fit(x, y) # min_samples_split[각주:1]

plt.plot(line, decision_tree_reg.predict(line), label='decision tree')


# linear regression

linear_reg = LinearRegression().fit(x, y)

plt.plot(line, linear_reg.predict(line), ls='--', label='linear regression'# ls = linestyle, 


plt.scatter(x[:, 0], y, marker='o', c='k'# c=color

plt.ylabel('output regression')

plt.xlabel('input feature')

plt.legend(loc=2)

plt.show()

wave 데이터셋에 적용한 LinearRegression과 DecisionTreeRegressor




2. binding(구간분할)

linear 모델은 선형으로만 modeling 하므로 특성이 하나일 땐 직선으로 나타남

decision tree는 훨씬 복잡한 modeling을 함

연속형 데이터에 아주 강력한 linear 모델을 만드는 방법중 하나는

한 특성을 여러 특성으로 나누는 구간분할binding(이산화)


wave 데이터셋이 특성의 입력값 범위(-3, 3)가 나뉘어 여러 구간으로

10개로 되었다고 가정하면 각 데이터 포인트가 어떤 구간에 속하는지로 나타낼 수 있음


### step1. 구간을 먼저 정하고 ==> 같은 간격으로 10개의 구간을 정의

bins = np.linspace(-3, 3, num=11) # num: 쪼갤 갯수

print('구간: \n{}'.format(bins))

print('구간의 수 \n{}'.format(len(bins)-1))

-3, 3 사이의 구간과 구간의 수


### step2. 각 데이터 포인트가 어느 구간에 속하는지 기록

which_bin = np.digitize(x, bins=bins) # 데이터, 구간

print('데이터 포인트 처음 5개 \n{}'.format(x[:5]))

print('데이터 포인트의 소속구간 처음 5개 \n{}'.format(which_bin[:5]))

각 구간에 맵핑한 x 데이터의 일부


이렇게 함으로써 wave 데이터셋에 있는 연속형 특성을 각 데이터 포인트가 어느 구간에 속하는지 인코딩한 범주형 특성으로 변환이 가능

==> 이 데이터에 모델을 적용하기 위해 preprocessing 모듈의 OneHotEncoder로 이산적인 이 특성을 one-hot-encoding으로 변환

OneHotEncoder는 pandas.get_dummies와 같지만 현재는 숫자로된 범주형 변수에만 적용가능




3. OneHotEncoding된 데이터로 Linear Regressor와 Decision Tree Regressor의 비교

# library import

from sklearn.preprocessing import OneHotEncoder


encoder = OneHotEncoder(sparse=Falsesparse matrix: 희소행렬, True면 1이 있는 값의 위치와 값을 나타냄

encoder.fit(which_bin)

x_binned = encoder.transform(which_bin)

print('x_binned[:5] \n{}'.format(x_binned[:5]))

one-hot-encoder된 구간에 맵핑된 x 데이터의 일부


### 구간을 10개로 정의했기 때문에 변환된 데이터셋 x_binned는 10개의 특성으로 구간

print('x_binned.shape \n{}'.format(x_binned.shape)) # (100, 10)


### one-hot-encoding된 데이터로 선형 회귀 모델과 결정트리의 모델을 새로 만들면

line_binned = encoder.transform(np.digitize(line, bins=bins))

plt.scatter(x[:, 0], y, marker='o', c='k'# c = color

lr_reg = LinearRegression().fit(x_binned, y)

plt.plot(line, lr_reg.predict(line_binned), label='binding linear regression', lw=5) # lw = linewidth


tree_reg = DecisionTreeRegressor(min_samples_split=3).fit(x_binned, y)

plt.plot(line, tree_reg.predict(line_binned), ls='--', label='binding decision tree', c='red'# ls = linestyle, c = color


plt.vlines(bins, ymin=-3, ymax=3, lw=1, alpha=0.2) # lw = linewidth

plt.legend(loc=2)

plt.ylabel('output regression')

plt.xlabel('input feature')

plt.show()

구간으로 나뉜 특성에 적용한 Linear Regrssion과 Decision Tree Regressor의 비교


LinearRegression과 DecisionTreeRegressor가 같은 예측을 만들어내서 완전히 겹침

구간별로 이 모델이 예측한 것은 상수값, 각 구간안에서는 특성의 값이 상수이므로 어떤 모델이든 그 구간의 포인트에 대해서는 같은 값을 예측함


구간으로 나누기전과 비교하면 각 구간에서 다른 값을 가지고 있으므로 linear모델이 훨씬 유연해짐, 반면 decision tree는 덜 유연해짐

tree 모델은 데이터를 자유롭게 나눠 학습할 수 있으므로 특성의 값을 구간으로 나누는 것이 아무런 득이 되지 않음

즉  decision tree는 데이터셋에서 예측을 위한 가장 좋은 구간을 학습함

구간나누기는 특성마다 따로 해야 하지만 decision tree는 한 번에 여러 특성을 살필 수 있음

하지만 linear 모델은 이런 변환으로 큰 이득을 얻음


일부 특성과 출력이 비선형 관계이지만, 용량이 매우 크고 고차원 데이터셋이라 선형모델을 사용해야 한다면

binding구간분할이 모델 성능을 높이는데 아주 좋은 방법이 될 수 있음

  1. min_samples_split: int, float, optional (default=2) The minimum number of samples required to split an internal node: If int, then consider min_samples_split as the minimum number. If float, then min_samples_split is a percentage and ceil(min_samples_split * n_samples) are the minimum number of samples for each split. Changed in version 0.18: Added float values for percentages. [본문으로]

'데이터 표현과 특성 > One-Hot-Encoding(' 카테고리의 다른 글

One-Hot-Encoding(가변수)  (0) 2018.03.27

#!/usr/bin/env python3


One-Hot-Encoding(가변수)


0.살펴보기

factor범주 변수를 표현하는데 가장 널리 쓰이는 방법인 One-Hot-Encoding원-핫-인코딩

dummy variable가변수을 만들어 factor형 변수를 0 또는 1 의 값을 가진 하나 이상의 새로운 특성으로 바꿈

따라서 특성당 0, 1의 값이 들어있기 때문에 binary classifier algorithm이진분류알고리즘 적용가능


pandas의 get_dummies[각주:1] 함수는 숫자 특성은 모두 연속형이라 생각해서 가변수를 만들지 않음 

대신 어떤 열이 연속형인지 범주형인지를 저장할 수 있는 scikit-learn의 OneHotEncoder를 사용할수 있고

DataFrame에 있는 숫자로 된 열을 문자열로 바꿀 수도 있음


# library import

import pandas as pd


# DataFrame 생성

df = pd.DataFrame({'숫자 특성': [0, 1, 2, 1],

                        'factor형 특성': ['핸드폰', '밀스', '핸드폰', '상자']})


# DataFrame 출력

print('df \n{}'.format(df))


# one-hot-encoding 적용

df_dummy = pd.get_dummies(df)

print('df_dummy \n{}'.format(df_dummy))


### get_dummies를 사용하면 문자열 특성만 인코딩되며 숫자 특성은 바뀌지 않음


### series를 str형태로 변형후 get_dummies 적용

### get_dummies를 적용할 때 columns를 직접 입력

### 방법 1

df1 = df.copy() #  DataFrame 복사

df1['숫자 특성'] = df1['숫자 특성'].astype(str) # 타입변형

df1_dummies = pd.get_dummies(df1)

print('df1_dummies \n{}'.format(df1_dummies))


### 방법 2

df2_dummies = pd.get_dummies(df, columns=['숫자 특성', 'factor형 특성']) # get_dummies 적용시 columns 지정

print('df2_dummies \n{}'.format(df2_dummies))



1. adult data loading

# library import

import pandas as pd


# data load

adult.csv

adult = pd.read_csv('adult.csv', index_col=False


# 원하는 열만 추출

adult = adult[['age', 'workclass', 'education', 'gender', 'hours-per-week', 'occupation', 'income']]

print('adult.head() \n{}'.format(adult.head()))

adult데이터의 처음 5번째 행


2. 범주형 데이터 문자열 확인

pandas ==> value_counts 메소드로 유일한 값이 각각 몇 번 나타나는지 확인


print('columns names \n{}'.format(adult.columns)) # 열 출력


print('age의 갯수 \n{}'.format(adult.age.value_counts())) # dataframe.열.메소드

print('workclass의 갯수 \n{}'.format(adult.workclass.value_counts())) # ? 값 있음을 확인

print('education의 갯수 \n{}'.format(adult.education.value_counts())) 

print('gender의 갯수 \n{}'.format(adult.gender.value_counts()))

print('hours-per-week의 갯수 \n{}'.format(adult['hours-per-week'].value_counts())) # 열이름이 특수문자나 띄어쓰기 있으면 ['열이름']으로 접근

print('occupation의 갯수 \n{}'.format(adult.occupation.value_counts())) # ? 값 있음을 확인

print('income의 갯수 \n{}'.format(adult.income.value_counts()))


value_counts로 확인한 특성의 수



3. pandas의 get_dummies로 데이터 reshaping 


adult_dummies = pd.get_dummies(adult)

print('원본 특성:\n', list(adult.columns), '\n')

print('get_dummies 후의 특성 :\n', list(adult_dummies.columns))

원본 특성과 get_dummies후의 특성, 'age', 'hours-per-week'를 제외하고 전부 나뉘어진 것을 볼 수 있음


### 숫자형인 age, hours-per-week는 그대로지만 factor형 특성은 값마다 새로운 특성이 만들어짐

### pandas의 values속성을 이용해 numpy 배열로 바꿀수 있음

### target을 income으로 잡을 것이므로 income을 데이터에서 분리해야함


### numpy에서 np.arange(11)[0:10]은 인덱스가 10인 항목을 포함하지 않음

### 그러나 pandas에서 범위 끝은 그 열을 포함함

features = adult_dummies.loc[:, 'age':'occupation_ Transport-moving']


import numpy as np

x = features.values # feature를 numpy배열로 변환

y = adult_dummies['income_ >50K'].values # 타겟데이터를 numpy 배열로 변환


print('x.shape: {}. y.shape: {}'.format(x.shape, y.shape)) # x.shape: (31143, 44), y.shape:(31143,)



4. one-hot-coding한 데이터를 바탕으로 machine learning

from sklearn.linear_model import LogisticRegression

from sklearn.model_selection import train_test_split


x_train, x_test, y_train, y_test = \

  train_test_split(x, y,

                   test_size=0.3, random_state=0)


logreg = LogisticRegression().fit(x_train, y_train)

print('테스트 점수 \n{:.3f}'.format(logreg.score(x_test, y_test))) # 0.813


### adult 데이터셋에서는 factor형 변수가 문자열로 인코딩 되어있음

### 철자 오류가 날 수 있지만 다른 한 편으로는 변수가 factor형이란 것을 확실하게 알 수가 있음


### 하지만 저장 공간을 절약하거나 데이터 취합 방식에 따라 범주형 변수가 숫자로 인코딩 된 경우가 많음

### 예를들어 adult 데이터셋에 있는 인구조사 데이터가 설문지를 이용해 모은 것이라고 가정하면 workclass에 대합 대답은 0, 1 ,2 등이 됨 ==> 이 열은 0~8까지의 숫자로 채워지

### 게 되고 누군가 이 데이터셋을 보면 이 변수를 연속형으로 다뤄야할지 factor형으로 다뤄야할지 단번에 알아채기가 어려움. 그러나 숫자가 workclass를 나타낸다고 알게  되면

### 이  값은 이산적이므로 연속형 변수로 다루면 안된다는 것이 명확해짐



  1. 객체 타입(문자열 같은)이나 범주형을 가진 열을 자동으로 변환해줌 [본문으로]

+ Recent posts