본문 바로가기
ML.DL

[ML경진대회] 범주형 데이터 이진분류-탐색적 데이터 분석(1)

by 권미정 2022. 9. 30.

책 <머신러닝, 딥러닝 문제해결 전략> 7장을 실습한 내용입니다.


지난 글에서는 <머신러닝 문제해결 프로세스>를 정리해보았다. 이번 글에서는 그 프로세스를 따라 실제 경진대회에 참가해볼 것이다!

들어가기 전에, 주피터 노트북에서 자주 사용하는 단축키들을 정리해보았다.

  • 셀 실행: Ctrl+Enter / Shift+Enter
  • 현재 셀 아래에 셀 추가: Esc, b
  • 현재 셀 위에 셀 추가: Esc, a
  • 현재 셀 삭제: Esc, d, d
  • 현재 셀 잘라내기: Esc, x
  • 잘라낸 셀 붙여넣기: Esc, v
  • 마크다운 셀로 변환: Esc, m
  • 코드 셀로 변환: Esc, y

 

1. 경진대회 이해

이번에 참가하는 '범주형 데이터 이진분류 경진대회'는 2019년 8월부터 12월까지 진행됐고 총 1,338팀이 참가했다. 이 대회의 목표는 범주형 피처 23개를 활용해 해당 데이터가 타깃값 1에 속할 확률을 예측하는 것이다.

이 대회는 3가지 특이점이 있다.
첫 번째, 인위적으로 만든 데이터를 제공한다. 대부분의 경진대회에서 제공하는 실제 데이터는 정리되어 있지 않은 경우가 많아서, 연습용으로는 인공 데이터가 오히려 좋다고 한다.
두 번째, 각 피처와 타깃값의 의미를 알 수 없다. 인위로 만들 때 데이터에 아무런 의미를 부여하지 않았기 때문인데, 이런 경우 배경 지식과 직관을 활용할 수 없다. 그래서 이번 경진대회는 온전히 데이터만 보고 접근해야 한다.
세 번째, 제공되는 데이터가 모두 범주형이다. 범주형 데이터는 범주를 나눌 수 있는 데이터로, 사칙 연산이 불가능하다. 값이 두 개로만 구성된 데이터, 순서형 데이터, 명목형 데이터, 날짜 데이터가 있다.
bin_로 시작하는 피처는 이진 피처, nom_로 시작하는 피처는 명목형 피처, ord_로 시작하는 피처는 순서형 피처이고, day와 month는 날짜 피처이다. 또, ord_3, ord_4, ord_5는 알파벳순으로 고윳값 순서를 매겼다고 한다.
타깃값도 범주형 데이터로, 0과 1 두 개로 구성돼 있다. 그래서 이진분류 문제에 속한다.

이런 데이터를 분석해보는 이유는 머신러닝 프로젝트에서 범주형 데이터를 다룰 일이 많기 때문이다. 이번 실습을 통해 범주형 데이터 인코딩 방법을 숙달해보자!

 

2. 탐색적 데이터 분석

먼저, 캐글 검색창에 "Categorical Feature Encoding Challenge"를 검색해서 경진대회에 참가한다.

 

2-1. 데이터 둘러보기

데이터가 어떻게 구성돼 있는지 살펴보기 위해 훈련, 테스트, 제출 샘플 데이터를 불러오자.

import pandas as pd
#데이터 경로
data_path='/kaggle/input/cat-in-the-dat/'

train=pd.read_csv(data_path+'train.csv',index_col='id')
test=pd.read_csv(data_path+'test.csv',index_col='id')
submission=pd.read_csv(data_path+'sample_submission.csv',index_col='id')

데이터를 읽어올 때 index_col 파라미터에 'id'를 전달해, 해당 열(id)을 인덱스로 지정하였다. 이번 대회에서는 index_col을 명시하지 않고 데이터를 불러오면 id라는 열이 보이는데, 각 행을 구분하는 역할만 한다.

 

훈련 데이터와 테스트 데이터가 인공 데이터라 행 개수가 30만 개, 20만 개로 딱 떨어지는 걸 볼 수 있다.

이제 훈련 데이터가 어떻게 이루어졌는지 살펴보자. 피처 개수가 많아 train.head()로 출력하면 중간에 피처가 생략된 상태로 출력되는데, 한눈에 보기에 불편하다. 그런데 T 메서드를 호출해서 행과 열 위치를 바꿔준다면? 모든 피처의 처음 다섯 개 값까지가 한눈에 들어온다!

train.head().T


제출 샘플 데이터도 출력해보자.

submission.head()

테스트 데이터 인덱스가 300,000부터 시작하기 때문에, 제출 샘플 데이터도 300,000부터 시작한다.
target 열은 모두 0.5로 입력돼 있다. 타깃값은 0과 1 두 가지인데, 이중 타깃값이 1일 확률을 예측해 저장해주면 된다. 즉, 이 경진대회의 목표는 각 테스트 데이터의 타깃값이 1일 확률을 예측하는 것이다.

2-1-1. 피처 요약표 만들기

이번에는 피처 타입이 무엇인지, 결측값은 없는지, 고유한 값은 몇 개인지, 실제 어떤 값이 입력돼 있는지 알아보기 위해 피처 요약표를 만들어보자. resumetable() 함수를 이용해 요약표를 만들 수 있다.

def resumetable(df):
    print(f'데이터셋 형상: {df.shape}')
    summary=pd.DataFrame(df.dtypes, columns=['데이터 타입'])
    summary=summary.reset_index()
    summary=summary.rename(columns={'index':'피처'})
    summary['결측값 개수']=df.isnull().sum().values
    summary['고윳값 개수']=df.nunique().values
    summary['첫 번째 값']=df.loc[0].values
    summary['두 번째 값']=df.loc[1].values
    summary['세 번째 값']=df.loc[2].values
    
    return summary

resumetable(train)

2-1-2. 피처 요약표 해석하기

피처 요약표의 내용이 많으니 비슷한 피처끼리 네 부류로 묶어 해석해볼 것이다.

① 이진(binary) 피처: bin_0~bin_4

이진 피처기 때문에 고윳값이 모두 2개고, 결측값은 없다. 이중 bin_0~2는 데이터 타입이 int64고 실젯값이 0 또는 1로 구성되어 있다. bin_3~4는 object 타입이고 실젯값은 T 또는 F, Y 또는 N이다. 머신러닝 모델은 숫자만 인식하기 때문에, T(True)와 Y(Yes)는 1로, F(False)와 N(No)는 0으로 인코딩하도록 하자.

② 명목형(nominal) 피처: nom_0~nom_9

모두 object 타입이고 결측값은 없다. nom_0~4는 고윳값이 6개 이하인데, nom_5~9는 고윳값이 엄청 많다. 또 의미를 알 수 없는 값이 입력돼 있는 걸 볼 수 있다.

③ 순서형(ordinal) 피처: ord_0~ord_5

ord_0만 int64 타입이고 나머지는 object 타입이다. 결측값은 없다. 순서형 데이터는 순서에 따라 타깃값에 미치는 영향이 다르기 때문에, 순서에 유의하며 인코딩해야 한다.

unique() 함수를 이용해 순서형 피처의 고윳값을 출력해서 순서를 파악해보자. 먼저 고윳값 개수가 적은 ord_0~3부터 해보자.

for i in range(3):
    feature='ord_'+str(i)
    print(f'{feature} 고윳값: {train[feature].unique()}')

ord_0 피처의 고윳값은 모두 숫자이므로, 숫자 크기에 순서를 맞추면 된다.
ord_1 피처의 고윳값은 캐글 등급이다. 등급 단계에 따라 Novice, Contributor, Expert, Master, Grandmaster 순으로 맞추자.
ord_2 피처의 고윳값은 춥고 더운 정도를 나타낸다. Freezing, Cold, Warm, Hot, Boiling Hot, Lava Hot 순으로 맞추자.

이번에는 고윳값 개수가 많은 나머지 피처를 출력해보자.

for i in range(3,6):
    feature='ord_'+str(i)
    print(f'{feature} 고윳값: {train[feature].unique()}')

이 피처들은 알파벳순으로 정렬되어 있기 때문에, 알파벳순으로 인코딩하면 된다.

④ 그 외 피처: day, month, target

모두 int64 타입이고 결측값은 없다.

print('day 고윳값:', train['day'].unique())
print('month 고윳값:', train['month'].unique())
print('target 고윳값:', train['target'].unique())

day 피처의 고윳값이 7개이므로, 요일을 나타낸다고 짐작할 수 있다!
month 피처의 고윳값은 1~12까지 월을 나타내고, 타깃값은 0 또는 1로 구성돼 있다.

 

지금까지 각 피처의 대략적 구조를 알아보았다. 다음 글에서는 이 피처들을 시각화해서 타깃값별 피처 분포를 알아보는 실습을 해볼 것이다.

댓글