Machine Learning/[Kaggle Course] ML (+ 딥러닝, 컴퓨터비전)

[Kaggle Course] Categorical Variables

WakaraNai 2020. 10. 12. 19:43
728x90
반응형

Categorical Variables란?

    설문조사의 항목처럼 제한된 가짓수로 선택지를 숫자로 대체 표현가능한 변수.

    변수에 대해 범주를 만들어 그 안에 포함시켜 분류하기 위해.

     예로, survey에서 "never"==0, "rarely"==1, "most days"==2, "every day"==3, 이렇게 표현 가능

 

Categorical Variables를 다루는 3가지 방법

1. Drop Categorical Variables (the easiest way)

    이 방법은 해당 column들이 유용하지 않은 정보를 포함할 때만 쓸모있음.

 

2. Label Encoding

    각각 다른 정수를 부여하여 구별시키기

 

 

위의 예의 경우 never부터 every day 항목까지 순차적으로 정렬할 수 있음. 순위를 부여할 수 있다는 의미.

모든 categorical variables가 뚜렷한 순서를 가지진 않지만

위와 같이 순위를 가진다면 "ordinal variable"이라고도 이야기할 수 있음.

ordinal variable이란 값이 완벽하지 않아도 그 범주 내에 있으면 그 값으로 처리한 후 순위를 매길 수 있는 변수.

 

decision trees나 random forest처럼 트리 기반의 모델에서는 ordinal variable인지 확인 후 label encoding 하는 게 좋음.

 

3. One-Hot Encoding

각 변수에 대해 새로운 column을 만들어서 그 변수의 존재 유무를 나타냄

 

 

순위를 매길 수 없는 categorical variable에서 사용하는 방법. 

요런 변수를 nominal variables라고 일컬음.

변수 가짓수가 너무 많으면 그닥 좋은 방법이 아님. 일반적으로 15개 미만일 때 사용.

 

 

 

 

 

Example

1st. train_data에서 모든 categorical variables의 리스트를 가져오기

각 column의 data type(dtype)으로 확인해봄.

"object" dtype은 해당 column이 text를 가지고 있다는 의미.

아래의 코드는 text를 보유한 column의 이름을 출력해줌.

 

# Get list of categorical variables
s = (X_train.dtypes == 'object')
object_cols = list(s[s].index)

print("Categorical variables:")
print(object_cols)

 

2nd. MAE 계산 함수 정의해두기

이후에 적용할 방법들에 대해서 MAE 계산 함수를 써보며 어느 방법이 더 나은지 확인할 수 있도록

 

3rd-1. Drop Columns included Categorical Variables

"object" dtype의 column을 drop하고 싶을 땐

"select_dtypes(exclude=['object'])" 메소드를 사용

drop_X_train = X_train.select_dtypes(exclude=['object'])
drop_X_valid = X_valid.select_dtypes(exclude=['object'])

print("MAE from Approach 1 (Drop categorical variables):")
print(score_dataset(drop_X_train, drop_X_valid, y_train, y_valid))

 

3rd-2. Label Encoding

Scikit-learn의 LabelEncoder 모듈을 가져와서 사용

categorical variable을 가진 각 column에 개별적으로 label encoder를 적용함

 

!!핵심코드!!

from sklearn.preprocessing import LabelEncoder

label_encoder = LabelEncoder()

     X[col] = label_encoder.transform(X[col])

 

from sklearn.preprocessing import LabelEncoder

# Make copy to avoid changing original data 
label_X_train = X_train.copy()
label_X_valid = X_valid.copy()

# Apply label encoder to each column with categorical data
label_encoder = LabelEncoder()
for col in object_cols: #object_cols -> good_label_cols
    label_X_train[col] = label_encoder.fit_transform(X_train[col])
    label_X_valid[col] = label_encoder.transform(X_valid[col])

print("MAE from Approach 2 (Label Encoding):") 
print(score_dataset(label_X_train, label_X_valid, y_train, y_valid))

 

위의 코드 예제는 서로다른 정수를 각각의 값들을 무작위 지정함. 하나하나 하는 customizing 하는 것보단 나음.

그러나 더 성능을 높이고 싶다면, ordinal variable일 때 정보가 가미된 label(informed label)을 적용하면 좋음.

컴퓨터 종류에 대한 text 정보는 출시연도에 따라 정수로 순번을 매길 수 있음. 무작위로 지정하는 것보다 나음.

해당 column에 어떤 text들이 들어있는지 알고 싶으면 X["column이름"].unique()를 쓰면 됨.

그럼 리스트 형식으로 안에 들어있는 값들을 나열해서 보여줌

 

 

그런데 다음 사진과 같이 train_data와 valid_data의 내용이 다를 수 있음. train_data에서만 나타날 떼!!

이러면 encoder는 error를 냄. valid_data에서 처음 만난 값에 대한 integer 정보가 없으니까.

위의 사진에서는 train_data에 valid_data의 'RRAn'과 'RRNn' 값이 없음.

이럴 땐 customizing으로 하나하나 정수값을 지정하거나 drop column을 해야하겠지요...

 

그래서 good_label_cols를 따로 모아보고, bad_label_cols도 따로 모아봄.

bad_label_cols는 버려야할 column들이니 이후에 쓰지 않도록 주의하자!

encoder에는 good_label_cols만 쓰시고!

# All categorical columns
object_cols = [col for col in X_train.columns if X_train[col].dtype == "object"]

# Columns that can be safely label encoded
good_label_cols = [col for col in object_cols if 
                   set(X_train[col]) == set(X_valid[col])]
        
# Problematic columns that will be dropped from the dataset
bad_label_cols = list(set(object_cols)-set(good_label_cols))
        
print('Categorical columns that will be label encoded:', good_label_cols)
print('\nCategorical columns that will be dropped from the dataset:', bad_label_cols)

# Drop categorical columns that will not be encoded
label_X_train = X_train.drop(bad_label_cols, axis=1)
label_X_valid = X_valid.drop(bad_label_cols, axis=1)

 

 

3rd-3. One-Hot Encoding

Scikit-learn의 OneHotEncoder 모듈을 가져와서 사용

여기에는 customizing해야 할 매개변수가 많이 있음.

  • handle_unknown='ignore' : train_data에 표현되지 않은 class를 가진 valid_data로 인해 직면할 error를 방지
  • sparse=False : encoded column들이 numpy array(sparse matrix)로 표현되는 걸 보장해줌.
  • X_train[object_cols], 꼭 categorical_variable이 포함된 column들에만 위의 모듈을 써야함!
  • one-hot encoding은 index를 지우기에 꼭 다시 되붙여주어야 함. (X_train.index)
  • one-hot 처리 완료된 column을 본 데이터에 삽입하기 전에, 본 데이터에 남아있는 text로된 column을 먼저 삭제한 뒤 합병해야 함!
from sklearn.preprocessing import OneHotEncoder

# Apply one-hot encoder to each column with categorical data
OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))
OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))

# One-hot encoding removed index; put it back
OH_cols_train.index = X_train.index
OH_cols_valid.index = X_valid.index

# Remove categorical columns (will replace with one-hot encoding)
num_X_train = X_train.drop(object_cols, axis=1)
num_X_valid = X_valid.drop(object_cols, axis=1)

# Add one-hot encoded columns to numerical features
OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)
OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)

print("MAE from Approach 3 (One-Hot Encoding):") 
print(score_dataset(OH_X_train, OH_X_valid, y_train, y_valid))

 

+) One-Hot Coding을 하기 전 거쳐야할 단계

1. Cardinality(값의 종류, 가짓수)가 너무 큰 지 보기

    해당 column을 one-hot으로 바꾸기엔 Cardinality가 너무 많은지 확인하기

    -> 그런 column은 제외하자

2. 내가 필요한 특정 column을 one-hot으로 바꾸는데 몇 개의 column이 필요한지 알아보자

# Get number of unique entries in each column with categorical data
object_nunique = list(map(lambda col: X_train[col].nunique(), object_cols))
d = dict(zip(object_cols, object_nunique))

# Print number of unique entries by column, in ascending order
sorted(d.items(), key=lambda x: x[1])
#하나의 리스트 안에 (column이름, cardinality)가 cardinality를 기준으로 오름차순으로 정렬된 리스트를 반환



high_cardinality_numcols=0
num_cols_neighborhood = 0
for col,cardi in sorted(d.items(), key=lambda x: x[1]):
    if cardi>9: #check num of columns that cardinality is greater than 10
        high_cardinality_numcols += 1
    if col == 'Neighborhood': #check cardinaility of column I need
        num_cols_neighborhood = cardi
728x90
반응형