#!/usr/bin/env python3


Use of expert knowledge


여행사를 위해 항공료를 예측해야 한다고 가정하면 휴가 성수기나 공휴일 근처에서는 항공료 가격이 올라감.

크리스마스같은 날짜가 고정되어 있는 기념일은 학습이 가능하지만 음력 날짜 같은 경우에는 그레고리안 달력으로 표현하기 힘듬. 그러나 이런 공휴일의 비행스케쥴이 기록된 특성을 추가하는 일은 어렵지 않음


자전거 대여소의 예제 데이터를 이용하여 집 앞에 있는 자전거를 사람들이 얼마나 대여할 것인지를 에측하기


뉴욕에서 시티바이크는 회원 가입 시스템과 함께 자전거 대여소를 운영,

자전거 대여 데이터는 익명으로 공개되어 있고 여러방식으로 분석되어 옴


우리가 풀려는 문제는 특정 날짜와 시간에 집 앞에 있는 자전거를 사람들이 얼마나 대여할 것인지를 예측하여

빌릴 자전거가 남아 있는지 알려고 함




1. citibike 데이터의 형태와 시각화

### library import

from mglearn.datasets import load_citibike

import matplotlib

import matplotlib.pyplot as plt

import pandas as pd

import numpy as np


### matplotlib 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


### datasets

citibike = load_citibike()

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

citibike.head()

>> indexdatetime형태로 이루어져 있으며 형식은 %Y-%m-%d 00:00:00이며

Freq: 3H ==> 3시간 주기로 데이터가 기록되었으며

data의 열이름은 'starttime', 'one' 이며 one에는 대여된 자전거의 횟수가 기록됨을 알 수 있음



2. 시각화를 위한 전처리

print('citibike.index.min() \n{}'.format(citibike.index.min()))

print('citibike.index.max() \n{}'.format(citibike.index.max()))

xticks = pd.date_range(start=citibike.index.min(), end=citibike.index.max(), freq='D')

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

# 그래프의 축을 만들기 위해 freq='D'로 설정하여 일별데이터로 수정


citibike의 index전처리



# strftime('%w'): datatime에서 일요일 ~ 토요일을 인덱스 0~6까지 부여

# strftime(' %m-%d'): datatime에서 '월', '일' 뽑아내기

print('''xticks.strftime(%w) \n{}'''.format(xticks.strftime('%w'))) 

print('''xticks.strftime( '%m-%d') \n{}'''.format(xticks.strftime(' %m-%d'))) 

x축 데이터를 위한 전처리


week = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']

# xticks.strftime('%w')의 결과는 str형태이기 때문에 int()를 이용하여 숫자로 변환

xticks_name = [week[int(w)] + d for w, d in zip(xticks.strftime('%w'), xticks.strftime(' %m-%d'))] # list 축약

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

x축 데이터를 위한 xticks_name




3. citibike의 시각화

plt.xticks(xticks, xticks_name,rotation=90, ha='left'# ha: horizon align; 수평정렬 

plt.plot(citibike, lw=1, c='darkblue'# lw = linewidth, c = colors

plt.xlabel('date')

plt.ylabel('count')

plt.show()

한 시티바이크 대여소에서 한달간 자전거가 대여된 횟수


>> 데이터를 보면 24시간 간격으로 낮과 밤을 확실히 구분 가능, 주중과 주말의 패턴도 구분이 가능

시계열 데이터를 이용한 예측 작업은 과거 데이터에서 학습하여 미래를 예측하는 방식을 사용

즉 데이터를 train set와 test set로 나눌 때 어떤 날짜까지의 모든 데이터를 train data로 하고, 날짜 이후의 모든 데이터를 test set로 사용

23일과 나머지 8일을 train set 와 data set로 사용



4. citibike 데이터의 기계학습과 시각화


pandas의 DatetimeIndex는 나노초이기 때문에 10**9으로 나눠줘야함

이 작업에서 우리가 사용할 특성은 일정 횟수의 대여가 일어난 날짜와 시간뿐

즉 입력 특성은 '2015-08-01 00:00:00'같은 날짜이고 출력은 대여횟수


y = citibike.values # target

x = citibike.index.astype('int64').values.reshape(-1, 1) // 10**9 # datetime ==> int64 ==> 값만 가지고오기 ==> 1열의 데이터로 ==> scale조정


### library import

from sklearn.ensemble import RandomForestRegressor


### train 갯수

n_train = 8 * 23


### data 분할

x_train, x_test, y_train, y_test = \

  x[:n_train], x[n_train:], y[:n_train], y[n_train:]


### 모델 적용  

rfr = RandomForestRegressor(n_estimators=100, random_state=0)

rfr.fit(x_train, y_train)

y_pred = rfr.predict(x_test)

y_pred_train = rfr.predict(x_train)


### 유효성 평가

print('R-Square \n{:.3f}'.format(rfr.score(x_test, y_test))) # -0.035


### visualization

plt.xticks(range(0, len(x), 8), xticks_name, rotation=90, ha='left'# ha = holizon align 

plt.plot(range(n_train), y_train, label='train')

plt.plot(range(n_train, len(y_test) + n_train), y_test, ls=':', label='test'# plt.plot(x, y, linestyles, label)

plt.plot(range(n_train), y_pred_train, ls='--', label='train test') # ls = linestyles

plt.plot(range(n_train, len(y_test) + n_train), y_pred, ls='--', label='test pred'

plt.legend(loc=2)

plt.xlabel('date')

plt.ylabel('count')

plt.show()


POSIX 시간만 사용하여 만든 랜덤 포레스트의 예측

>> 테스트 세트에 대해선 한 가지 값으로만 예측

R-Square 값은 -0.035로 아무것도 학습되지 않음

테스트 세트에 있는 POSIX시간 특성의 값은 훈련 세트에 있는 모든 데이터보다 뒤의 시간

트리 모델인 랜덤포레스트는 훈련 세트에 있는 특성의 범위 밖으로 extrapolation외삽할 수 있는 능력이 없음


데이터 그래프를 보면 시간요일이라는 두 요소가 중요한 것으로 판단 <== 데이터 분석가의 판단

POSIX 시간으로 아무것도 학습되지 않으므로 이 특성은 제외


이 로직을 계속 사용할 것이기 때문에 함수로 만듬

RandomForest는 이전 포스팅 참조: [1]Random Forest -- intro, [2]Random Forest


# n_train = 8 * 23

# model = RandomForestRegressor(n_estimators=100, random_state=0)

def eval_feature(features, target, model):

    # data 분할

    x_train, x_test, y_train, y_test = \

      features[:n_train], features[n_train:], target[:n_train], target[n_train:]


    # model 적용

    model.fit(x_train, y_train)

    y_pred = model.predict(x_test)

    y_pred_train = model.predict(x_train)

    

    # 유효성 평가

    print('R-Square \n{:.3f}'.format(model.score(x_test, y_test)))


    # visualization

    plt.xticks(range(0, len(x), 8), xticks_name, rotation=90, ha='left')

    plt.plot(range(n_train), y_train, label='train')

    plt.plot(range(n_train, len(y_test) + n_train), y_test, ls=':', label='test')

    plt.plot(range(n_train), y_pred_train, ls='--', label='train test')

    plt.plot(range(n_train, len(y_test) + n_train), y_pred, ls='--', label='test pred')

    plt.legend(loc=2)

    plt.xlabel('date')

    plt.ylabel('count')

    plt.show()


5. 데이터의 x축 중 시간 요소만 출력하여 분석

# x = citibike.index.astype('int64').values.reshape(-1, 1) // 10**9

x_hours = citibike.index.hour.values.reshape(-1, 1) # 시간만 출력

eval_feature(x_hours, y, model) # 사용자 정의 함수


시간만 사용하여 만든 랜덤 포레스트의 예측// R-square = 0.600

>> R-square가 나아졋지만 일별 패턴은 예측하지 못함



6. 시간요소에 요일정보를 더해서 분석

# print(citibike.index.dayofweek) : 일요일부터 토요일까지를 0~6까지로 인덱싱 

### 시간데이터에 요일 정보를 추가

x_hours_week = np.hstack([citibike.index.dayofweek.values.reshape(-1, 1),

                          citibike.index.hour.values.reshape(-1 ,1)])

eval_feature(x_hours_week, y, model)


시간과 요일 특성을 사용해 만든 랜덤 포레스트의 예측//R-square = 0.842


>> 이제 모델은 하루의 시간과 요일에 따른 주기적인 패턴을 따르고 있음. R-square는 0.842로 좋은 예측을 나타냄

이 모델이 학습한 것은 8월 23일까지 요일별, 시간별 평균 대여 횟수

랜덤 포레스트 회귀로 만든 예측은 여러 트리가 예측한 값들의 평균이므로

그래서 8월 24일 월요일의 예측값과 8월 31일의 예측값은 동일

이런 작업에서 랜덤 포레스트 같이 복잡한 모델이 필요한 것은 아니므로 더 간단한 모델인 LinearRegression을 적용해보면



7. 시간요소에 요일정보를 더해서 LinearRegression을 적용

LinearRegression은 이전 포스팅 참조: [1]Linear Regression -- intro, [2]LinearRegression ,[3]Ridge


# library import

from sklearn.linear_model import LinearRegression, Ridge

eval_feature(x_hours_week, y, LinearRegression())

시간과 요일 특성을 사용하여 만든 선형 회귀의 예측, R-square=0.132


>> LinearRegression성능은 훨씬 나쁘고 주기적인 패턴도 다르게 나타남

그 이유는 요일시간정수로 인코딩 되어있어서 연속형 변수로 해석되기 때문임. 선형모델은 시간을 선형 함수로 함수로만 학습할 수 있어서, 시간이 흐를수록 대여 수가 늘어나게 학습됨


이 패턴을 잡아내기 위해 연속형 변수를 범주화하기 위해 OneHotEncoder를 사용


8. OneHotEncoder를 적용한 Ridge분석


# library import

from sklearn.preprocessing import OneHotEncoder


# encoder load

encoder = OneHotEncoder()

encoder.fit(x_hours_week)

x_hours_week_encoder = encoder.transform(x_hours_week).toarray() # ndarray로 전환


eval_feature(x_hours_week_encoder, y, Ridge())


시간과 요일의 원-핫 인코딩을 사용해 만든 선형 회귀의 예측, R-Square = 0.619


>> 연속형 특성일 때보다 훨씬 좋아짐

이 선형 모델은 요일에 대해 하나의 계수를 학습하고 시간에 대해서도 하나의 계수를 학습

이 말은 시간 패턴이 모든 모든 날에 걸쳐 공유된다는 뜻임


9. 상호작용 특성을 사용하여 Ridge분석

상호작용 특성을 사용하면 시간과 요일의 조합별 계수를 학습할 수 있음


상호작용 특성은 이전 포스트 참조: [1]polynomial analysis with boston house price


# library import

from sklearn.preprocessing import PolynomialFeatures


poly_transformer = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False# include_bias=왼쪽에 절편항 추가

poly_transformer.fit(x_hours_week_encoder) # 인코더 학습

x_hours_week_encoder_poly = poly_transformer.transform(x_hours_week_encoder) #  인코더를 데이터에 적용


ridge = Ridge()

eval_feature(x_hours_week_encoder_poly, y, ridge)


시간과 요일의 곱을 특성으로 사용해서 만든 선형 회귀의 예측R-Square = 0.845


>> 이런 특성변환을 통해 모델의 성능이 랜덤 포레스트와 거의 비슷해짐

이 모델의 가장  큰 장점은 무엇이 학습되었는지가 명확하다는 것

각 날짜와 시간에 대해 하나의 계수를 학습

랜덤 포레스트와는 달리 선형 모델은 모델이 학습한 계수를 그래프로 나타낼 수 있음


hour = ['{:02d}:00'.format(i) for i in range(0, 24, 3)]

day = ['mon', 'tue', 'wed', 'thr', 'fri','sat', 'sun']

features = day + hour


### get_featrues_names 메소드를 통해 PolynomialFeatures로 추출한 상호작용 특성에 이름을 달아주고 계수가 0이 아닌 특성만 선형


features_poly = poly_transformer.get_feature_names(features)

features_nonzero = np.array(features_poly)[ridge.coef_ != 0]

coef_nonzero = ridge.coef_[ridge.coef_ != 0]


x_axis_data = np.arange(len(coef_nonzero))

plt.scatter(x_axis_data, coef_nonzero)

plt.xticks(x_axis_data, features_nonzero, rotation=90)

plt.xlabel('feature name')

plt.ylabel('coefficient size')

plt.show()


시간과 요일의 곱을 사용한 선형 모델의 계수

선형 모델은 구간 분할이나 다항식과 상호작용 특성을 새로 추가해 큰 이득을 볼 수 있음

반면에 랜덤 포레스트나 SVM같은 비선형 모델은 특성을 늘리지 않고서도 복잡한 문제를 학습가능

#!/usr/bin/env python3


feature selection based model(모델 기반 특성 선택)


0. 살펴보기

feature selection based model모델 기반 특성 선택지도 학습 machine learning 모델을 사용하여

1. 특성의 중요도를 평가

2. 가장 중요한 특성들만 선택함


특성 선택에 사용하는 지도 학습 모델은 최종적으로 사용할 지도 학습 모델과 같을 필요는 없음

특성 선택을 위한 모델은 각 특성의 중요도를 측정하여 순서를 매길 수 있어야


decision tree는 각 특성의 중요도가 담겨있는 feature_importances_를 제공

decision tree : [1]Decision Tree


선형 모델 계수의 절댓값도 특성의 중요도를 평가하는 데 사용할 수 있음

L1규제를 사용한 선형 모델은 일부 특성의 계수만 학습(Lasso)

이는 그 모델 자체를 위해 특성이 선택된다고 생각할 수 있지만,

다른 모델의 특성 선택을 위해 전처리 단계로 사용할 수도 있음


Lasso : Lasso



단변량 분석과는 반대로 feature selection based model

한 번에 모든 특성을 고려하므로(사용된 모델이 상호작용을 잡아낼 수 있다면)

상호작용 부분을 반영할 수 있음


SelectFromModel은 (지도 학습 모델로 계산된) 중요도가 지정한 임계치보다 큰 모든 특성을 선택

이는 매우 복잡한 모델이고 단변량분석보다 훨씬 강력한 방법



1. breast cancer 데이터를 통한 단변량 분석과 모델 기반 특성 선택의 비교

### library import

from sklearn.datasets import load_breast_cancer

from sklearn.feature_selection import SelectFromModel, SelectPercentile

from sklearn.ensemble import RandomForestClassifier

from sklearn.linear_model import LogisticRegression

from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt

import matplotlib

import numpy as np


### matplotlib 한글, 축 - 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


### datasets

cancer = load_breast_cancer()

# print(cancer.data.shape)


### noise

rnd = np.random.RandomState(seed=0)

noise = rnd.normal(size=(cancer.data.shape[0], 70))


### np.hstack

cancer_data_noise = np.hstack([cancer.data, noise])


### data 분할

x_train, x_test, y_train, y_test =\

  train_test_split(cancer_data_noise, cancer.target,

                   random_state=0, test_size=0.3)


### 특성을 고를 model 적용

randforest = RandomForestClassifier(n_estimators=100, random_state=0)

select_model = SelectFromModel(randforest, threshold='median').fit(x_train, y_train)


select_uni = SelectPercentile(percentile=50).fit(x_train, y_train)



### visualization

models = [select_uni, select_model]

_, axes = plt.subplots(3, 1)


ones = np.ones(x_train.shape[1], dtype=np.bool)

axes[0].matshow(ones.reshape(1, -1), cmap='gray')

axes[0].set_yticks([])

axes[0].set_title('full feature')


for model, ax in zip(models, [axes[1], axes[2]]):

    x_train_model = model.transform(x_train)

    x_test_model = model.transform(x_test)


    idx = model.get_support()

    ax.matshow(idx.reshape(1, -1), cmap='gray')

    ax.set_yticks([])

    ax.set_title('{}'.format(model.__class__.__name__))

plt.show()


특성의 전체 선택, 단변량 선택, 모델기반선택으로 선택한 특성



### 일반화

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

score = logreg.score(x_test, y_test)

print('total 특성의 정확도: {:.3f}'.format(score))

models = [select_uni, select_model]

names = ['univariate', 'model_based']

for model, name in zip(models, names):

    x_train_model = model.transform(x_train)

    x_test_model = model.transform(x_test)

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

    score = logreg.score(x_test_model, y_test)

    print('{}의 정확도: {:.3f}'.format(name, score))

각 모델별 정확도


#!/usr/bin/env python3


univariate analysis(단변량 분석)


0.살펴보기

univariate analysis단변량 통계에서는

1. 개개의 특성target 사이에 중요한 통계적 관계가 있는지를 계산

2. 깊게 관련되어 있다고 판단되는 특성을 선택


분산분석 : 데이터를 클래스별로 나누어 평균을 비교하는 방법

분산분석으로 계산한 어떤 특성의 F-통계량이 높으면 그 특성은 클래스별 평균이 서로 다르다는 뜻

분류에서는 ANOVA, analysis of variance분산분석


사용하는 분포는 F-distribution

0. 기본적인 형태는 group(1) / group(2), 큰 값을 분자로 사용하여 최솟값을 1이 되게함

1. H0 = 클래스들의 평균과 분산이 같다

2. H0상태에서의 F-distribution 그래프를 그림

3. F-통계량이 1(좌측)에 가까운 수가 나올수록 두 집단은 비슷한 평균과 분산을 갖음

4. F-통계량이 1에 가까울수록 p-value의 값은 커짐 ==> target에 미치는 영향이 적다고 판단

5. p-value는 pvalues_에 저장됨

6. scikit-learn의 SelectBest, SelectPercentile에서 특성을 선택하는 기준은 F-통계값

7. F-통계값은 scores_에 저장됨


이 방법의 핵심 요소는 univariate, 즉 각 특성이 독립적으로 평가됨

==> 따라서 다른 특성과 깊게 연관된 특성은 선택되지 않을 것으로 예상 가능


이 방법의 특징은

1. 계산이 매우 빠르고 평가를 위해 모델을 만들 필요가 없음

2. 특성을 선택한 후 적용하려는 모델에 상관없이 사용 가능


scikit-learn에서 univariate로 특성을 선택하려면 

1. 분류에는 f_classif(default)를, 회귀에서는 f_regression을  선택하여 테스트

2. 계산한 p-value에 기초하여 특성을 제외하는 방식을 선택. 


이런 방식들은 매우 높은 p-value를 가진(target값과 연관성이 적은) 특성을 제외할 수 있도록 critical value를 조정하는 parameter를 사용

critical value를 계산하는 방법중 하나인 SelectKBest는 고정된 k개의 특성을 선택하고 SelectPercentile은 지정된 비율만큼 특성을 선택



1. cancer data에서 univariate analysis(단변량 분석)하기

### library import

from sklearn.datasets import load_breast_cancer

from sklearn.feature_selection import SelectPercentile

from sklearn.model_selection import train_test_split

import numpy as np

import matplotlib

import matplotlib.pyplot as plt


### matplotlib 한글출력, 축- 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


### dataload

cancer = load_breast_cancer()


### preprocessing

rng = np.random.RandomState(seed=0) # random seed

noise = rng.normal(size=(cancer.data.shape[0], 50)) # normal분포에서 cancer.data.shape[0]x50의 데이터추출

cancer_data_noise = np.hstack([cancer.data, noise]) # data 병합

print('cancer_data_noise.shape \n{}'.format(cancer_data_noise.shape)) # (569, 80)


### data 분할

x_train, x_test, y_train, y_test = \

  train_test_split(cancer_data_noise, cancer.target,

                   test_size=0.5, random_state=0)


### preprocessing 메소드 호출 및 적용

select = SelectPercentile(percentile=50).fit(x_train, y_train) # percentile: 50%만 선택

x_train_select = select.transform(x_train) # 적용

x_test_selected = select.transform(x_test)


print('x_train.shape \n{}'.format(x_train.shape))

print('x_train_select.shape \n{}'.format(x_train_select.shape))

합친 데이터와 선택된 데이터의 형태

>>  특성의 갯수가 80에서 40개로 줄음



2. 어떤 특성이 선택되었는지 확인


idx = select.get_support()[각주:1]

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

논리값으로 만들어진 idx


plt.matshow(idx.reshape(1, -1), cmap='gray_r')

plt.yticks([]) # 축눈금없애기

plt.xlabel('feature number')

plt.show()


SelectPercentile이 선택한 특성


>> 마스킹된 그래프에서 볼 수 있듯이 선택된 특성은 대부분 원본특성이며, 노이즈 특성은 거의 제거됨을 볼 수 있음




3. 전체 특성을 이용했을 때와 선택된 특성만 사용했을 때 로지스틱 회귀 성능 비교


Logistic Regression 링크 참조

[1]Logistic Regression[1], [2]Logistic Regression[2]


### library import

from sklearn.linear_model import LogisticRegression


### model 호출

lr = LogisticRegression()


### 전체특성을 사용했을 때 정확도

lr.fit(x_train, y_train)

print('전체 특성을 사용한 점수: \n{:.3f}'.format(lr.score(x_test, y_test))) # 0.937


### 일부특성을 사용했을 때 정확도

lr.fit(x_train_select, y_train)

print('선택된 특성을 사용한 점수 \n{:.3f}'.format(lr.score(x_test_selected, y_test))) # 0.951


>> 이 경우에는 일부 원본 특성이 없더라도 노이즈 특성을 제거한 쪽의 성능이 더 높음

이 예는 인위적으로 간단하게 만들었고 실제 데이터에서는 엇갈리는 경우도 많음

너무 많은 특성 때문에 모델을 만들기가 현실적으로 어려울 때 univariate를 사용하거나,

많은 특성들이 확실히 도움이 안된다고 생각할 때 사용

  1. get_support(): 선택된 특성을 논리값으로 표시 [본문으로]

#!/usr/bin/env python3


feature auto-selection --intro


새로운 특성을 만드는 방법이 많으므로 데이터의 차원이 원복 특성의 수 이상으로 증가

따라서 모델은 더 복잡해지고 overfitting될 가능성도 높아짐

새로운 특성을 추가하거나, 고차원 데이터셋을 사용할 때 가장 유용한 특성만 선택하여 차원을 줄이는 것이 좋음

이렇게 하면 모델이 간단해지고 일반화 성능이 올라감


어떤 특성이 좋은지 아는 방법에는 크게 3가지가 있음

1. univariate statics일변량통계

2. model-based selection모델기반선택

3. iterative selection반복적 선택


이 방법들은 모두 지도학습이므로 target값이 필요하며

train set에만 특성 선택에 사용해야함

#!/usr/bin/env python3


수학함수를 이용한 비선형 변환


제곱항이나 세제곱 항을 추가하면 linear regression model의 성능을 향상시킬 수 있음

한편 log, sin, exp 같은 함수를 적용하는 것도 특성 변환에 유용


tree 모델은 max_feature(트리의 분기에서 사용될 후보 특성의 갯수)가 주요 parameter


각 모델의 특징은 이전 포스팅 참조


random forest의 기본값은 'auto'로 특성 갯수의 제곱근을 나타내기때문에 나열 순서가 결과의 영향을 줄 수 있지만

decision tree와 gradient boosting tree의 기본값은 'None'으로 전체 특성을 모두 사용하기 때문에 순서에 상관없음

그러나 max_feature='auto'로 설정하면 random forest 처럼 특성의 나열 순서에 영향을 받을 수 있음


linear modelneural network는 각 특성의 스케일과 분포에 밀접하게 연관

특성과 target값 사이에 비선형이 있다면 특히 linear regression에서는 모델을 만들기가 어려움

log와 exp함수는 데이터 스케일을 변경하여 linear model과 neural network의 성능을 올릴 수가 있음


sin, cos함수주기적인 패턴이 들어 있는 데이터를 다룰 때 사용


대부분의 모델은 gaussian distribution을 기본으로 하여 만들어졌기 때문에 각 특성이 gaussian distribution과 비슷할 때 최고의 성능을 나타냄


logexp같은 함수는 gaussian dist.모양을 만드는데 쉽고 효과적임

이런 변환은 정수형 데이터를 다룰 때 유효

카운트 데이터(음이 아닌 정수)는 특별한 통계 pattern을 따르는 경우가 많음



1. 인위적 카운트 데이터를 생성하여 비선형 변환 적용


# library import

import numpy as np

import matplotlib.pyplot as plt

import matplotlib


# matplotlib 한글/ 축- 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus']


# datasets

rnd = np.random.RandomState(seed=0) # random seed

x_org = rnd.normal(size=(1000,3)) # 정규분포

w = rnd.normal(size=3) # 정규분포


x = rnd.poisson(lam=np.exp(x_org)) # poisson distribution, lam = lambda

y = np.dot(x_org, w) # x_org(1000,3) , w(3,)이므로 (1000,)형태의 데이터가 만들어짐

print('x[:10, 0] \n{}'.format(x[:10, 0]))

print('특성 종류 \n{}'.format(np.unique(x[:, 0])))

print('특성 빈도 \n{}'.format(np.bincount(x[:,0])))


x의 개략적인 구조 확인


# visualization

_, axes = plt.subplots(1, 3)

for i, ax in enumerate(axes.ravel()):

    bins = np.bincount(x[:, i])

    ax.bar(range(len(bins)), bins, color='grey')

    ax.hlines(y=263, xmin=-1, xmax=len(bins)+1, alpha=0.4, linestyles=':', colors='red')

    ax.hlines(y=129, xmin=-1, xmax=len(bins)+1, alpha=0.4, linestyles=':', colors='red')

    ax.set_xlabel('x의 {}번째 특성'.format(i+1))

    ax.set_ylabel('frequency')

plt.show()


x의 특성별 histogram


>> bincount는 0부터 시작하고 큰 값의 수는 빠르게 줄어들음



2. poisson distribution에 여러가지 모델 적용

이런 종류의 분포(작은 수치는 많고, 큰값은 적은)는 많이 나타나지만 선형 모델은 이런 데이터를 잘 처리하지 못함


# library import: Ridge는 위의 링크 참조

from sklearn.linear_model import Ridge

from sklearn.model_selection import train_test_split


# data 분할

x_train, x_test, y_train, y_test =\

  train_test_split(x, y,

                   random_state=0, test_size=0.3)


# model 유효성 평가

for alpha in [0.001, 1, 1000]:

    ridge = Ridge(alpha=alpha).fit(x_train, y_train)

    print('\nalpha={}, {}의 정확도 {:.3f}'.format(alpha, ridge.__class__.__name__,ridge.score(x_test, y_test)))


alpha값에 따란 Ridge의 정확도

### 정확도가 51.3%로 좋지 못하지만, 규제를 풀수록(alpha가 커질수록) 정확도가 낮아지는 것을 확인


### log scaling

x_train_log = np.log(x_train + 1) # 0이 있으면 Inf가 나오기 때문에 1을 더함

x_test_log = np.log(x_test + 1)


###  visualization

_, bins = np.histogram(x_train_log, bins=10)

_, axes = plt.subplots(1, 3)

for i, ax in enumerate(axes.ravel()):

    ax.hist(x_train_log[:, i], bins=bins, color='darkblue')

    ax.hlines(y=200, xmin=-1, xmax=len(bins)+1, alpha=0.4, linestyles=':', colors='red')

    ax.hlines(y=100, xmin=-1, xmax=len(bins)+1, alpha=0.4, linestyles=':', colors= 'red')

    ax.set_xlabel('logx의 {}번째 특성'.format(i+1))

    ax.set_ylabel('frequency')

plt.show()


로그 변환 후 x 특성 값의 histogram


>> 데이터셋과 모델의 조합에 최적인 변환 방법을 찾는것은 예술에 가까운 일

이 예에서는 모든 특성이 같은 속성을 가지고 있으나 실제로 이런 경우는 드물며,

실제로는 일부 특성만 변환하거나 특성마다 모두 다르게 변환

이렇게 특성 변환은 선형 모델에서는 필수


구간 분할, 다항식, 상호작용(특성곱)은 데이터가 주어진 상황에서 모델의 성능에 큰 영향을 줄 수 있음

tree모델 스스로 중요한 상호작용을 찾아낼 수 있고, 대부분의 경우 데이터를 변환하지 않아도 됨

SVM, K-NN, neural network모델은 선형 모델보다는 영향이 뚜렷하지 않음

#!/usr/bin/env python3


polynomial analysis with boston house price


1. 실제 데이터로 polynomial 분석



Ridge 참조


Random Forest 참조


### load library

from sklearn.datasets import load_boston

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import MinMaxScaler, OneHotEncoder, PolynomialFeatures

from sklearn.linear_model import Ridge

from sklearn.ensemble import RandomForestRegressor


### data load

boston = load_boston() 

# print(boston.keys())

x_train, x_test, y_train, y_test = \

  train_test_split(boston.data, boston.target,

                   test_size=0.3, random_state=0)


### scale 조정

scaler_mms = MinMaxScaler().fit(x_train) # scaler 호출

x_train_scaled = scaler_mms.transform(x_train) # scaler를 x_train에 적용

x_test_scaled = scaler_mms.transform(x_test) # scaler를 x_test를 적용


### model

randfor = RandomForestRegressor(n_estimators=100, random_state=0, n_jobs=-1) # n_jobs = 코어의 수(-1 : all)

ridge = Ridge()

models = [randfor, ridge]


### model 적용 및 성능 테스트

for degree in range(1, 6):

    poly = PolynomialFeatures(degree=degree, include_bias=False).fit(x_train_scaled)# include_bias = True: x_train_scaled 맨 왼쪽에 상수항 추가. 

    x_train_poly = poly.transform(x_train_scaled)

    x_test_poly = poly.transform(x_test_scaled)

    print('degree={}'.format(degree))

    print('x_train.shape={}'.format(x_train.shape))

    print('x_train_poly.shape={}\n'.format(x_train_poly.shape))

    print('원래 특성과 polynomial 한 특성의 수 \n==> origin:{}, poly:{}\n'.format(

        boston.data.shape[1], len(poly.get_feature_names())))


    for model in models:

        conf = model.fit(x_train_scaled, y_train)

        print('교차항이 없을 때 {} 점수: {:.3f}'.format(

            model.__class__.__name__, model.score(x_test_scaled, y_test))) # __class__.__name__ : class이름 호출

        conf = model.fit(x_train_poly, y_train)

        print('degree={}, 교차항이 있을 때 {} 점수{:.3f}\n'.format(

            degree, model.__class__.__name__, model.score(x_test_poly, y_test)))

    ptrint('----------------------------------------------')


모델 성능 테스트 평가의 일부분

교차항이 있을 때 Ridge모델은 성능이 상승하지만

복잡한 모델인 RandomForestRegressor경우에는 정확도가 큰차이가 나지 않음

degree=3의 경우

중복조합을 이용

원래 특성 13개 + x0 ~ x12 중 중복을 허락하여 2개를 조합(91개) + x0 ~ x12 중 중복을 허락하여 3개를 조합(455개)를 다합친 559개가 특성으로 사용됨


'데이터 표현과 특성 > interaction과 polynomial' 카테고리의 다른 글

interaction과 polynomial  (0) 2018.03.28

#!/usr/bin/env python3


interaction과 polynomial


특별히 특성을 다양하게 나타내는 방법은 원본 데이터interactionpolynomial을 추가하는 것

이런 방법은 머신러닝에도 많이 적용


구간별 선형 모델은 인위적인 wave 데이터셋의 각 구간에 대해 상수값을 학습하지만 linear 모델은 절편뿐만 아니라 기울기도 학습 가능


선형 모델에 기울기를 추가하는 방법은 구간으로 분할된 데이터에 원래특성을 다시 추가

wave 데이터셋의 경우 10개의 구간을 나누면 원래 특성 + 10 = 11차원 데이터셋이 만들어짐



1. wave data(인위적 데이터셋)에 특성 추가하기

### import library

import matplotlib.pyplot as plt

import matplotlib

from sklearn.preprocessing import OneHotEncoder

from mglearn.datasets import make_wave

import numpy as np


### matplotlib 설정

matplotlib.rc('font', family='AppleGothic')

plt.rcParams['axes.unicode_minus'] = False


### datasets

x , y = make_wave(n_samples=100)

# print(x)

# print(y)


### binding

bins = np.linspace(-3, 3, num=11)

# print(bins)


which_bins = np.digitize[각주:1](x, bins=bins) # bins: 구간

# print(x[:5])

# print(which_bins)


encoder = OneHotEncoder(sparse=False).fit(which_bins) # OneHotEncoder 메소드 호출 후 적용

x_binned = encoder.transform(which_bins)

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

wave데이터의 x에서 one-hot-encoding을 적용한 데이터의 일부


### import library

from sklearn.linear_model import LinearRegression


### model 호출 후 OHE(One Hot Encoding)된 데이터 적용

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


### 축 생성

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


### np.digitize(line, bins=bins) : line의 각 데이터 포인트가 bins의 어디 구간인지 표현 ==> 1~10 까지 숫자

### encoder.transform(np.digitize(line, bins=bins)) : 1 ~ 10까지를 OHE로 표현 ex) 1 => [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]  // 3 => [0, 0, 1, 0, 0, 0, 0, 0 ,0 ,0]

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


### predict

lreg_pred = lreg.predict(line_binned) 

### 각 구간에서는 같은 특성(ex)[0, 1, 0, 0, 0 ,0 ,0 ,0 ,0 ,0])을 가지기 때문에 각 구간에서는 일정한 값으로 예측됨


### 구간에서 기울기 값 추가

x_combined = np.hstack([x, x_binned]) # 원래 특성x와 구간별로 OHE된 리스트를 결합

print('x_combined.shape \n{}'.format(x_combined.shape))


line_combined = np.hstack([line, line_binned])


### model 호출 후 결합한 데이터를 적용

lreg_comb = LinearRegression().fit(x_combined, y)


### predict

pred_lreg_comb = lreg_comb.predict(line_combined)


### visualization

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

plt.plot(line, lreg_pred, label='binding linear Reg', ls='--', lw=3) # ls = linestyles,  lw= linewidths

plt.plot(line, pred_lreg_comb, label='plus original feature')

plt.vlines(bins, ymin=-3, ymax=3, lw=1, alpha=0.2, linestyles=':'# lw = linewidths

plt.ylabel('output regression')

plt.xlabel('input feature')

plt.legend(loc=2) # loc = 1, 2, 3, 4 : 오른쪽 위, 왼쪽 위, 왼쪽 아래, 오른쪽 아래

plt.show()

구간으로 분할된 특성과 하나의 기울기를 사용한 linear regression


이 모델은 각 구간의 절편과 기울기를 학습

학습된 기울기는 음수이고 모든 구간에 걸쳐 동일

x축 특성이 하나이므로 기울기도 하나이며 모든 구간에서 동일




2. wave data(인위적 데이터셋)에 기울기 특성 추가하기(1)


각 구간에서 다른 기울기를 가지게 하기 위해 데이터 포인트가 있는 구간과

x축 사이의 상호작용 특성을 추가 가능(구간 특성과 원본 특성의 곱)하여 기울기를 표현


### data 축 추가

x_product = np.hstack([x_binned, x * x_binned])

line_proudct = np.hstack([line_binned, line*line_binned])

print(x_product.shape) # (100, 20)

### x_binned는 데이터 포인트가 속한 구간의 one-hot-encoding이므로 x를 곱하면 해당 구간이외에는 모두 0


### 모델 호출 후 결합한 데이터 적용

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

pred_linear_reg = linear_reg.predict(line_proudct)


### visualization

plt.plot(line, pred_linear_reg, label='original feature를 곱한 linear regression')


for bin in bins:

    plt.plot([bin, bin], [-3, 3], ls=':', c='k', lw=1, alpha=0.4)


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

plt.ylabel('output regression')

plt.xlabel('input feature')

plt.legend(loc=2)

plt.show()

구간별 기울기가 다른 linear regression




3. wave data(인위적 데이터셋)에 기울기 특성 추가하기(2)

### 구간 나누기는 연속형 특성을 확장하는 방법 중 하나

### 특성 x가 주어지면 x**2, x**3, x**4 ... 등을 시도 가능하여 여러차원으로 까지 늘릴 수 있음

### preprocessing의 PolynomialFeatures에 구현되어 있음


from sklearn.preprocessing import PolynomialFeatures


### degree: x**10까지, include_bias=True: 절편을 그리기 위해 값이 1인 특성을 왼쪽에 추가

poly = PolynomialFeatures(degree=10, include_bias=False) 

poly.fit(x)


x_poly = poly.transform(x)


# 10차원을 사용했으므로 10개의 특성이 만들어짐

print('x_poly.shape \n{}'.format(x_poly.shape))

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

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

print('poly.get_feature_names() \n{}'.format(poly.get_feature_names()))

만들어진 특성의 모양


### 다항식 특성을 선형 모델과 함께 사용하면 전형적인 polynomial regression다항 회귀모델이 됨


SVR 은 아래 링크 참조

Kernelized Support Vector Machines


### viusalization

### 다항식을 결합한 특성을 적용

poly_reg = LinearRegression().fit(x_poly, y)

line_poly = poly.transform(line)

pred_poly_reg = poly_reg.predict(line_poly)


plt.plot(line, pred_poly_reg, label='polynomial linear regression')

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

plt.xlabel('input feature')

plt.ylabel('output regression')

plt.vlines(bins, ymin=-3, ymax=3, linestyles=':', colors='grey', alpha=0.4) # linestyles = ':'  ==> dash

from sklearn.svm import SVR

for gamma in [1, 10]:

    svr = SVR(gamma=gamma).fit(x, y)

    plt.plot(line, svr.predict(line), label='SVR gamma={}'.format(gamma))

plt.legend(loc=(-0.001, 1.01))

plt.show()

RBF 커널 SVM의 gamma parameter 변화와 10차 다항식을 이용한 LinearRegression



  1. np.digitize: 각 데이터 포인트가 어느 구간에 속하는지 계산 후 인덱스로 리턴 [본문으로]

#!/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