코딩하는 해맑은 거북이

[머신러닝] 범주형 변수의 인코딩(Encoding) 방법 4가지 본문

AI

[머신러닝] 범주형 변수의 인코딩(Encoding) 방법 4가지

#CJE 2023. 12. 11.
해당 글은 아래의 3가지를 다룬다.
📌 범주형 변수란?
📌 인코딩과 사용하는 이유
📌 인코딩 방법
  🔷 One-Hot Encoding
  🔷 Label Encoding
  🔷 Target Encoding
  🔷 Ordinal Encoding

📌 범주형 변수란?

(연속적이거나 순서가 있는 값을 가지는 변수인 수치형 변수(Numerical Variable)와 달리)
범주형 변수(Categorical Variable)란 유한한 범주 혹은 그룹 중 하나에 속하는 값을 가지는 변수를 의미한다. 
범주형 변수는 다시 명목형 변수와 순서형 변수로 나눌 수 있다.
 

  • 명목형 변수(Nominal Variable)
    • 범주 간에 순서나 등간격이 없는 변수
    • 예시: 성별, 혈액형
  • 순서형 변수(Ordinal Variable)
    • 범주 간에 상대적인 순서 또는 등간격이 있는 변수
    • 예시: 학점, 만족도조사

 

📌 인코딩과 사용하는 이유

인코딩(Encoding)이란 데이터를 특정 형식으로 표현하거나 변환하는 과정을 의미한다. 주로 머신러닝이나 데이터 분석에서 범주형 변수를 수치형으로 변환하는데 사용된다.
 
Q. 그럼 범주형 변수는 왜 인코딩 해야할까?
머신러닝 모델은 수치형 데이터만 다룰 수 있기 때문에 String 형태로 된 범주형 변수는 모델이 인식할 수 있도록 적절한 수치형으로 변환하는 인코딩 과정을 거쳐야 한다.
 

📌 인코딩 방법

해당 글에서 주요 인코딩 방법 4가지에 대해서 설명한다. 이 외에도 다양한 인코딩 방법도 존재한다.
 

🔷 One-Hot Encoding

각 범주를 새로운 피쳐인 이진 열로 변환하는 방법으로, 각 열에 해당 범주가 속하면 1, 아니라면 0을 가지게 된다.

  • 장점: 범주 간의 순서를 부여하지 않고, 각 범주가 모델에 독립적으로 고려되기 때문에 해석에 용이하다. (명목형 변수에 적합)
  • 단점: 범주의 수 만큼 새로운 열을 생성하기 때문에 너무 많은 범주를 가진 변수에 적용할 때 차원이 크게 증가하고, 메모리 사용이 증가할 수 있다. 또한, 새로운 열 간에 상관관계가 발생할 수 있어 다중공선성 문제가 발생할 수 있다.

*예시 데이터

import pandas as pd

# 예시 데이터 - 각 도시에 대한 주택 가격 평균 (인코딩 결과 비교를 위해 중복 포함)
data = {'City': ['Seoul', 'Busan', 'Seoul', 'Busan', 'Seoul', 'Incheon', 'Incheon', 'Daegu'],
        'Price': [500, 300, 450, 250, 550, 400, 420, 350]}
df = pd.DataFrame(data)
df

 
 
1) sklearn의 OneHotEncoder 사용

from sklearn.preprocessing import OneHotEncoder
# One-Hot Encoder 생성
# sparse=True : 희소 행렬 반환 / sparse=False : 밀집 행렬 반환
ohe = OneHotEncoder(sparse=False)

# 'City' 변수에 대해 One-Hot Encoding 적용
encoded_data = ohe.fit_transform(df[['City']])

# 생성된 열의 이름을 가져오기
feature_names = ohe.get_feature_names_out(['City'])

# 인코딩된 새로운 데이터프레임 생성
encoded_df = pd.DataFrame(encoded_data, columns=feature_names)

# 기존 'City' 열 제거
new_df = df.drop(['City'], axis=1).copy()

# 인코딩된 열 추가
onehot_encoded_df2 = pd.concat([new_df, encoded_df], axis=1)
display(ohe.categories_)
display(onehot_encoded_df2)

 
2) pandas의 get_dummies 사용

# 'City' 변수에 대해 One-Hot Encoding 적용
# prefix - 생성된 열의 이름의 접두사
onehot_encoded_df = pd.get_dummies(df, columns=['City'], prefix='City')
display(onehot_encoded_df)

 
 

🔷 Label Encoding

각 범주를 고유한 정수로 변환하는 방법으로, 0~(범주의 수-1)까지 연속적인 정수를 가지게 된다.

  • 장점: 각 범주를 단순히 정수로 바꿔주어 구현이 간단하고, 새로운 열을 생성하지 않기 때문에 메모리 사용이 효율적이다.
  • 단점: 연속적인 정수로 범주 간의 순서를 부여하므로 모델이 해당 순서를 인식할 수 있어 예측에 영향을 미칠 수 있다.  또한, 해당 순서 및 수치적인 차이가 범주 간의 순서 정보가 아닐뿐더러 범주 간의 순서 정보를 고려하지 않고 부여된 정수이기에 순서 정보가 중요한 경우에 유용하지 않다.
    • 이러한 특성 때문에 선형 회귀와 같은 머신러닝 알고리즘에서는 적용하지 않는다. 단, 트리계열은 이러한 특성을 반영하지 않기 때문에 문제없다.
from sklearn.preprocessing import LabelEncoder
label_encoded_df = df.copy()

# Label Encoder 생성
le = LabelEncoder()

# 'City' 변수에 대해 Label Encoding 적용
label_encoded_df['City'] = le.fit_transform(df['City'])
display(f'{dict(zip(le.classes_, le.transform(le.classes_)))}')
display(label_encoded_df)

 
 

🔷 Target Encoding

범주의 타켓 변수(종속 변수)의 특정 통계량(주로 평균값)으로 변환하는 방법이다.

  • 장점: 범주가 타켓 변수와의 어떤 관계를 반영하여 예측을 수행할 수 있고, 새로운 열을 생성하지 않기 때문에 메모리 사용이 효율적이다. (명목형 변수에 적합)
  • 단점: 범주의 타켓 변수의 통계량을 사용하므로 인코딩 결과가 동일하게 되면 과적합의 원인이 될 수 있다.
!pip install category_encoders
from category_encoders import TargetEncoder
target_encoded_df = df.copy()

# Target Encoder 생성
te = TargetEncoder()

# 'City' 변수에 대해 Target Encoding 적용
target_encoded_df['City'] = te.fit_transform(df['City'], df['Price'])

display(te.mapping['City'][:-2])
display(target_encoded_df)

 
 

🔷 Ordinal Encoding

각 범주에 순서 또는 순위를 부여하여 고유한 정수로 변환하는 방법으로, 사용자가 범주 간의 순서를 정의해 주어야 한다.

  • 장점: 각 범주에 순서를 부여하여 모델의 순서 정보를 전달할 수 있고, 새로운 열을 생성하지 않기 때문에 메모리 사용이 효율적이다. (순서형 변수에 적합)
  • 단점: 범주 간의 순서 및 중요도가 동일하지 않은 경우 예측에 영향이 미칠 수 있다.

*예시 데이터

data = {'Grade': ['B', 'C', 'A', 'A+', 'B+']}
df = pd.DataFrame(data)
df

 
 
1) sklearn의 OrdinalEncoder 사용

from sklearn.preprocessing import OrdinalEncoder
ordinal_encoded_df = df.copy()

# Ordinal Encoding에 사용할 순서 정의
oe = OrdinalEncoder(categories=[['C', 'C+', 'B', 'B+', 'A', 'A+']])

# Ordinal Encoding 적용
ordinal_encoded_df['Grade'] = oe.fit_transform(df[['Grade']])

display(oe.categories_)
display(ordinal_encoded_df)

 
2) dataframe의 map 함수 사용

ordinal_encoded_df2 = df.copy()

# Ordinal Encoding에 사용할 순서 정의
ordinal_mapping = {'C': 0, 'C+': 1, 'B': 2, 'B+': 3, 'A': 4, 'A+': 5}

# Ordinal Encoding 적용
ordinal_encoded_df2['Grade'] = df['Grade'].map(ordinal_mapping)

display(ordinal_encoded_df2)

 

Comments