범주형 변수(Categorical Variable)을 처리하는 주요 방법에 대해서 정리해보도록 하겠습니다.
참고로 Categorical variable을 핸들링하기 전에 가장 중요한 것 중하나는 먼저 Missing values를 어떻게 처리할 것(이미 처리했거나, 하나의 범주 지표로 놔두거나) 인지가 선행되어야 합니다. 특정 라이브러리들은 NaN값을 처리하지 못하기 때문입니다.
나누는 방법에는 굉장히 여러가지가 있겠지만 dtypes
를 이용하여 간단하게 나누는 코드 예는 다음과 같이 작성해볼 수 있습니다.
num_col = [col for col in df.columns if df[col].dtypes == 'int64' or df[col].dtypes == 'float64' ]
cat_col = [col for col in df.columns if df[col].dtypes == 'object' ]
데이터의 순서(Order)가 존재하는 Ordinal variables를 대상으로 가장 손쉽게 핸들링할 수 있는 방법입니다.
다음과 같이 mapping정보가 담긴 딕셔너리를 생성해서 df.map()
메소드로 매핑해주면 끝입니다.
mapping = {
"Freezing":0,
"Warm": 1,
"Cold": 2,
"Boiling Hot": 3,
"Hot": 4,
"Lava Hot": 5
}
train["ord"] = train["ord"].map(mapping)
데이터의 특정한 순서가 없는 범주형 데이터(Nominal) 같은 경우는 범주의 수로 columns를 확장하여 해당 범주 값이 존재하면 1, 존재하지 않으면 0으로 표현하는 One-Hot encoder가 가장 유명한 방법 중 하나입니다.
sklearn.preprocessing의 라이브러리를 사용하여 구현할 수 있는데, OH encoder의 경우의 특성상 희소행렬(sparse matrix)가 형성됩니다.
이는 데이터용량을 많이 잡아먹기 때문에 CSR(Compressed Sparsed Row)라는 형태로 저장하게 됩니다. 다음 코드를 보면 그냥 0과 1로 저장하는 dense행렬과, csr형태로 저장하는 경우의 용량의 차이가 존재함을 확인할 수 있습니다.
from sklearn import preprocessing
import scipy as sp
train = pd.read_csv(path+"train.csv")
df = train.copy()
oh_enc_dense = preprocessing.OneHotEncoder(sparse_output=False)
encoded_df_dense = oh_enc.fit_transform(df.ord_0.values.reshape(-1, 1))
oh_enc_csr = preprocessing.OneHotEncoder(sparse_output=True)
encoded_df_csr = oh_enc_csr.fit_transform(df.ord_0.values.reshape(-1, 1))
print(f'dense mat size = {encoded_df_dense.nbytes}')
print('sparse mat size =')
print('indptr:', encoded_df_csr.indptr.nbytes)
print('indices:', encoded_df_csr.indices.nbytes)
print('data:', encoded_df_csr.data.nbytes)
print(f'= {encoded_df_csr.indptr.nbytes + encoded_df_csr.indices.nbytes + encoded_df_csr.data.nbytes}')
# print
dense mat size = 7200000
sparse mat size =
indptr: 1200004
indices: 1200000
data: 2400000
= 4800004
사이킷런을 이용하지 않고, 판다스로 수행할 수 있는 방법도 있습니다.
drop_first
인자를 활용하면 자명한 컬럼 하나를 줄일 수 있습니다.
pd.get_dummies(data, columns = ['Class'], drop_first=True)
범주의 숫자를 하나의 새로운 피처로 활용하는 방법도 사용할 수 있다.
# Replacing to Count
df = train.copy()
# groupby to pk
df.groupby(["nom_0"])["id"].count()
# aggregate
df.groupby(["nom_0"])["id"]\
.agg(["count"])\
.sort_values(by=["nom_0"], ascending=False)