AI·빅데이터 융합 경영학 Study Note

[ML수업] 8주차 실습2: feature engineering- Categorical Feature Transformation (범주형) 본문

AI·ML

[ML수업] 8주차 실습2: feature engineering- Categorical Feature Transformation (범주형)

SubjectOwner 2023. 11. 21. 15:52
X_train_imp[cat].head()

###

##### pd.get_dummies() vs. OneHotEncoder()
- 학습데이터와 평가데이터를 구분하지 않고 전체 데이터를 OHE할 경우 => pd.get_dummies()
- 학습데이터와 평가데이터를 구분하여 각각 OHE할 경우 => OneHotEncoder()  

 

### One-Hot Encoding- ##### pd.get_dummies()를 사용하는 방법

# 학습 데이터와 평가 데이터를 결합한 후, get_dummies를 통한 OHE 수행
X_all_imp = pd.concat([X_train_imp, X_test_imp])
X_all_imp_ohe = pd.get_dummies(X_all_imp, columns=cat)

#X_all_imp_ohe.info()
#X_all_imp_ohe.filter(like='car').head()
# 'car'라는 문자열을 포함한 열들을 필터링하여 선택하고, 선택된 열들과 해당하는 처음 5개의 행을 출력

# 학습 데이터와 평가 데이터로 재분할
#이렇게 하면 원핫인코딩된 새로운 데이터셋이 생기는거임.
X_train_imp_ohe = X_all_imp_ohe.iloc[:X_train_imp.shape[0],:].reset_index(drop=True)
X_test_imp_ohe = X_all_imp_ohe.iloc[X_train_imp.shape[0]:,:].reset_index(drop=True)
 
 

### One-Hot Encoding- ##### OneHotEncoder를 사용하는 방법

from sklearn.preprocessing import OneHotEncoder

ohe = OneHotEncoder(handle_unknown='ignore', sparse=False)
ohe.fit(X_train_imp[cat]) #[]를 더 감싼 이유는 인자로 2차원 배열을 받기 때문.
print(ohe.categories_)  # # OneHotEncoder의 범주형 변수의 카테고리 정보를 확인합니다

'''

[array([0, 1, 2, 3, 4, 5, 6], dtype=object), array([0, 1], dtype=object), array(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], dtype=object), array([1.0, 2.0, 3.0, 4.0], dtype=object), array([0, 1], dtype=object), array([1.0, 2.0, 3.0, 4.0], dtype=object), array(['AL', 'AR', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA', 'IA', 'ID', 'IN', 'KS', 'KY', 'MD', 'ME', 'MO', 'MS', 'MT', 'ND', 'NE', 'NH', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SD', 'TN', 'UT', 'WA', 'WI', 'WV', 'WY'], dtype=object), array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], dtype=object)]

'''

각 카테고리 목록은 해당 범주형 변수에 포함된 고유한 카테고리의 값들을 나타내며, 각 값은 해당 변수의 원-핫 인코딩된 특성에 대한 인덱스로 사용됩니다. 이 정보를 통해 각 범주형 변수의 원-핫 인코딩 결과를 해석할 수 있습니다.

 

 

아래 두 코드는 같은 기능을 하는데  첫 번째 코드는 배열로 작업하고 두 번째 코드는 DataFrame으로 작업합니다.

결과물은 동일함.

 

1. 이 코드는 먼저 더미 특성의 칼럼 이름을 생성하고, 이를 원-핫 인코딩된 결과를 DataFrame으로 변환하는 데 사용합니다. 그런 다음, 인코딩된 특성을 원본 데이터에서 제거하고 나머지 특성과 결합하여 X_test_imp_ohe라는 새로운 데이터프레임을 생성합니다. 이렇게 하면 인코딩된 특성이 포함된 완전한 데이터가 준비됩니다.

# OHE 결과를 DataFrame 형식으로 만들어 사용할 경우: 

# dummy에 대한 컬럼명 생성 
columns = []
for i, c in enumerate(cat):
    columns += [f'{c}_{v}' for v in ohe.categories_[i]]
# 생성된 dummy를 DataFrame으로 변환 
ohe_train = pd.DataFrame(ohe.transform(X_train_imp[cat]), columns=columns)
ohe_test = pd.DataFrame(ohe.transform(X_test_imp[cat]), columns=columns)
# 인코딩한 feature는 제거하고 나머지 feature와 결합 
X_train_imp_ohe = pd.concat([X_train_imp.drop(columns=cat).reset_index(drop=True), ohe_train], axis=1) # 원래 피처 삭제 후 OHE된 feature 병합
X_test_imp_ohe = pd.concat([X_test_imp.drop(columns=cat).reset_index(drop=True), ohe_test], axis=1) # 원래 피처 삭제 후 OHE된 feature 병합

X_train_imp_ohe

 

 

2. 이 코드는 먼저 X_train_imp와 X_test_imp에서 범주형 변수(cat)를 제거한 후, 해당 범주형 변수를 원-핫 인코딩하여 얻은 결과를 기존의 수치형 변수와 결합합니다. 이렇게 하면 인코딩된 특성이 포함된 새로운 데이터프레임 X_train_imp_ohe와 X_test_imp_ohe가 생성됩니다.

# OHE 결과를 Array 형식으로 만들어 사용할 경우:  

X_train_imp_ohe = X_train_imp.drop(cat, axis=1) 
X_test_imp_ohe = X_test_imp.drop(cat, axis=1) 
X_train_imp_ohe = np.c_[X_train_imp.drop(cat, axis=1),
                        ohe.transform(X_train_imp[cat])]
X_test_imp_ohe = np.c_[X_test_imp.drop(cat, axis=1),
                        ohe.transform(X_test_imp[cat])]
X_train_imp_ohe

 

 
 
 
### Label Encoding

두가지 방법이 있다.

주요 차이점:

LabelEncoder는 각 범주에 고유한 정수를 할당하며 범주 간 순서를 고려하지 않습니다.
OrdinalEncoder는 범주 간의 상대적인 순서를 고려하여 인코딩합니다. 또한 알 수 없는 범주를 처리하는 방법을 설정할 수 있습니다.

 

# LabelEncoder: 하나의 범주형 피처만 인코딩

from sklearn.preprocessing import LabelEncoder

# 레이블 인코딩 결과를 저장할 변수를 생성합니다.
X_train_imp_le, X_test_imp_le, = X_train_imp.copy(), X_test_imp.copy()

le = LabelEncoder()

# 각 범주형 변수에 대해 레이블 인코딩을 수행합니다.
for c in cat:
# 학습 데이터(X_train_imp)를 이용하여 레이블 인코딩을 수행하고, 결과를 학습 데이터 및 테스트 데이터에 적용합니다.
    X_train_imp_le[c] = le.fit_transform(X_train_imp[c])
    X_test_imp_le[c] = le.transform(X_test_imp[c])

 

 

 

handle_unknown 매개변수를 사용하여 알 수 없는 범주를 처리할 수 있으며, unknown_value를 설정하여 알 수 없는 범주를 대체할 값을 지정할 수 있습니다.

 

'error' (기본값): 알 수 없는 범주가 발견되면 오류를 발생시킵니다. 이 옵션을 선택하면 새로운 데이터에 등장하는 알 수 없는 범주가 처리되지 않고, 오류 메시지가 출력됩니다.
'use_encoded_value': 알 수 없는 범주를 사용자가 지정한 unknown_value 매개변수로 인코딩합니다. 따라서 새로운 범주는 사용자가 지정한 값으로 대체됩니다.

# OrdinalEncoder: 전체 범주형 피처를 인코딩

from sklearn.preprocessing import OrdinalEncoder

X_train_imp_le, X_test_imp_le, = X_train_imp.copy(), X_test_imp.copy()

le = OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1, dtype=int)
X_train_imp_le[cat] = le.fit_transform(X_train_imp[cat])
X_test_imp_le[cat] = le.transform(X_test_imp[cat])

X_train_imp_le[cat]

 

 

<타겟 인코딩> <<< 이게 제일 좋은 듯 .

타겟 인코딩(Target Encoding)은 범주형 변수를 다루는 데이터 전처리 기술 중 하나로, 주로 분류(classification) 문제에서 사용됩니다. 이 기술은 각 범주형 변수의 범주(category)를 해당 범주에 대한 타겟 변수(종속 변수)의 평균값 또는 확률값으로 대체하는 방법입니다.

타겟 인코딩은 일반적으로 범주형 변수가 많은 경우나 범주 간의 관계가 중요한 경우에 유용하게 사용됩니다.

 

최종적으로 X_train_imp_te 데이터프레임의 범주형 변수 열은 TargetEncoder를 적용한 결과로 업데이트되었습니다.

# Target Encoding

from category_encoders import TargetEncoder

X_train_imp_te, X_test_imp_te, = X_train_imp.copy(), X_test_imp.copy()

te = TargetEncoder(cols=cat, min_samples_leaf=5, smoothing=100)
X_train_imp_te[cat] = te.fit_transform(X_train_imp[cat], y_train)
X_test_imp_te[cat] = te.transform(X_test_imp[cat])

X_train_imp_te[cat]

 

# day 별 target 평균값
pd.concat([X_train_imp[cat], y_train], axis=1).groupby('day')['record_type'].mean()

 

 

<타겟 인코딩 using K-fold> <<<< 이게 그냥 타겟인코딩을 하는것보다 성능이 좋음.

# Target encoding using k-fold

from sklearn.model_selection import KFold

X_train_imp_te, X_test_imp_te, = X_train_imp.copy(), X_test_imp.copy()

# for문을 이용한 변수를 반복하여 타깃 인코딩 수행
for c in cat:
    # 학습 데이터 전체에서 각 범주별 타깃 평균을 계산
    data_tmp = pd.DataFrame({c: X_train_imp[c], 'target': y_train})
    target_mean = data_tmp.groupby(c)['target'].mean()

    # 테스트 데이터의 카테고리 변경
    X_test_imp_te[c] = X_test_imp[c].map(target_mean)

    # 학습 데이터 변환 후 값을 저장하는 배열을 준비
    tmp = np.repeat(np.nan, X_train_imp.shape[0])

    # 학습 데이터 분할
    kf = KFold(n_splits=4, shuffle=True, random_state=72)  # n_splits은 4~10 정도가 적당
    for idx_1, idx_2 in kf.split(X_train_imp):
        # 아웃 오브 폴드로 각 범주형 목적변수 평균 계산
        target_mean = data_tmp.iloc[idx_1].groupby(c)['target'].mean()
        # 변환 후의 값을 날짜 배열에 저장
        tmp[idx_2] = X_train_imp[c].iloc[idx_2].map(target_mean)

    # 변환 후의 데이터로 원래의 변수를 변경
    X_train_imp_te[c] = tmp
    
X_train_imp_te[cat]