[변수 처리] Python에서 범주형 변수(Categorical) 다루기

2019. 9. 13. 13:53분석 Python/Data Preprocessing

728x90

http://contrib.scikit-learn.org/categorical-encoding/onehot.html

 

One Hot — Category Encoders latest documentation

options are ‘error’, ‘return_nan’, ‘value’, and ‘indicator’. The default is ‘value’. Warning: if indicator is used, an extra column will be added in if the transform matrix has unknown categories. This can cause unexpected changes in dimension in some case

contrib.scikit-learn.org

## onehot encoder

from sklearn.preprocessing import OneHotEncoder
data2[obj_col]
enc = OneHotEncoder(handle_unknown='ignore')
enc.fit(data2[obj_col])
enc.get_feature_names()

## 단점

category 이름이 사라짐

## category_encoder

## 전체 데이터셋을 넣고 특정 category 지정 후에 학습 가능
import category_encoders as ce
ce_one_hot = ce.OneHotEncoder(cols=obj_col , use_cat_names=True)
ce_one_hot.fit(X = data2,)
result = ce_one_hot.transform(data2)
result.head()
## inverse도 동일한 형태이면 category 부분만 변함.
ce_one_hot.inverse_transform(result)


## 주의사항

columns 순서가 동일해야 함

결론

category_encoder 사용하기로 함.

이름의 형태를 유지하면, 전체를 한꺼번에 iverse_transform 도 가능함.

내가 사용하려는 방법은 생성 후에 변환하고 다시 이것을 표현하기 위한 숫자 형태로 다시 변환하는 툴이 필요

col_n = [0]
columns = ce_one_hot.feature_names
mapping = ce_one_hot.ordinal_encoder.mapping
replace_col = {}
idx = 0
for col_dict in mapping :
    col , map_ = col_dict["col"] , col_dict["mapping"] 
    n = len([column for column in columns if re.search("^{}".format(col) , column )])
    idx += n
    col_n.append(idx)
    replace_col[col] = dict(zip(map_.index.tolist() , map_.values))
    
container = {}    
container["category_encoding"] = ce_one_hot    
container["label_dict"] = replace_col    
import dill
with open('./category_collection.pkl', 'wb') as f:
    dill.dump(container, f)
    
with open('./category_collection.pkl', 'rb') as f:
    container = dill.load(f)
    
container["category_encoding"].inverse_transform(result).head()
container["category_encoding"].inverse_transform(result).head().replace(container["label_dict"])

 

 

추가) Target Encoding

만약에 일반적인 방법인 category를 label encoding으로 얻은 숫자는 머신러닝 모델이 오해하기 쉽다. 

만약 사과, 배, 딸기를 임의의 수 아마 여기선 character 레벨 순서로 해서 0,1,2를 줄 것이다. 

그렇다면 사과 < 배 < 딸기라고 할 수 있는 것인가? 물론 그렇지 않을 것이다.

그렇다면 머신러닝 모델 방법을 위해서 의미있게 category를 encoding 할 수 있는 방법이 없을까?

미국 전역의 다양한 이웃을 설명하는 데이터 세트를 가지고 있으며, 우리의 목표는 평균 가계 소득을 예측하는 것이다.

여기 encoding 하고 싶은 feature 는 state이고 우리의 target은 numerical인 income이다.

target encoding 한에서 일반적인 아이디어는 target 변수의 더 큰 값에 해당하는 범주가 더 큰 숫자로 인코딩 되게 하는 것이다. 

data = pd.DataFrame({'State' : ['CA', 'NY', 'CA', 'TX', 'TX', 'NY', "NY", 'CA'],
                     'Income' : [70000, 64000, 72000, 59000, 57000, 67000, 62000 , 69000]})
encoder = ce.target_encoder.TargetEncoder(cols=['State'])
encoder.fit(data['State'], data['Income']);
data['trans_State'] = encoder.transform(data['State'])
data['Round_State'] = data['trans_State'].round() 
data

encoder = ce.cat_boost.CatBoostEncoder(cols=['State'])
encoder.fit(data['State'], data['Income']);
data['trans_State'] = encoder.transform(data['State'])
data['Round_State'] = data['trans_State'].round() 
data

left target encoder // right catboost encoder

그래서 잘 보면 CA값이 높은 INCOME을 가지므로 trans_State 도 높게 encoding 된 값을 가진다. 

이러한 방법은 catboost encoder를 비교

>> 주의사항 ( train에 encoding fitting 후 그것을 test와 train 적용 )

Target encoding can lead to data leakage. To avoid that, fit the encoder only on your train set, and then use its transform method to encode categories in both train and test sets.

하지만 나는 보통 TARGET이 MultiClass이거나 Binary Class 같은 경우를 주로 보게되는데, 동일한 알고리즘을 적용하게 되면, 문제가 생기는 것 같다. 같은 target의 비율을 가진 category는 같은 값을 가지게 된다. 이렇게 되면 2개를 비교할 수 없어지기 때문에 문제가 발생할 것 같다. 아니면 비율이 동일하니 그대로 사용해도 되는 건지도 모르겠다.

data = pd.DataFrame({'State' : ['CA', 'NY', 'CA', 'TX', 'TX', 'NY', "NY", 'ZA', 'ZA'],
                     'break' : [1, 0, 1, 1, 0, 1, 0 , 1 , 0]})
encoder = ce.target_encoder.TargetEncoder(cols=['State'])
encoder.fit(data['State'], data['break']);
data['trasn_State'] = encoder.transform(data['State'])
data['Round_State'] = data['trasn_State'].round() 
encoder = ce.cat_boost.CatBoostEncoder(cols=['State'])
encoder.fit(data['State'], data['break']);
data['trans_State'] = encoder.transform(data['State'])
data['Round_State'] = data['trans_State'].round() 
data

 

변형된 값을 보면 ZA 라는 클래스와 TX의 클래스 값의 encoding 값이 동일하다.

그러므로 저 값을 그대로 쓰게 되면, TX와 ZA의 Class를 구별할 수가 없게 된다.

 

https://medium.com/machine-learning-eli5/dealing-with-categorical-data-f4c8556cbda0

 

Dealing with Categorical Data

Categorical data can screw up you ML model if not handled properly.

medium.com

https://contrib.scikit-learn.org/categorical-encoding/helmert.html

 

Helmert Coding — Category Encoders latest documentation

options are ‘error’, ‘return_nan’, ‘value’, and ‘indicator’. The default is ‘value’. Warning: if indicator is used, an extra column will be added in if the transform matrix has unknown categories. This can cause unexpected changes in dimension in some case

contrib.scikit-learn.org

 

728x90