[Kaggle] Day-1. Child Mind Institute : EDA

DD[Dev_Diary]·2024년 11월 10일

첫 Velog 작성 🎉📝

이번에는 꾸준하게 일주일에 하나씩 작성하는 걸 목표로 Velog를 시작해보려고 합니다!
첫 게시물의 주제는 Kaggle의 Child Mind Institute 데이터를 이용한 예측 모델 개발입니다. 😊


프로젝트 개요 🎯

본 캐글 Competition의 주 목적은 아동 및 청소년의 신체 활동 데이터를 활용하여 문제적 인터넷 사용 수준을 예측하는 모델을 개발하는 것입니다.
Competition에서 사용할 데이터는 테이블 형태로 제공되며, 이를 바탕으로 분석을 진행할 예정입니다. 🔍

주요 진행 내용 ✨

간략한 데이터 설명 후 EDA(탐색적 데이터 분석) 과정으로 넘어가 보겠습니다!
먼저 본 Competition의 평가 방식이 다소 독특하기 때문에, 평가 방식을 먼저 설명하겠습니다. 📈


평가 방식: Quadratic Weighted Kappa (QWK)란? 📊

QWK는 예측값과 실제값 간의 일치도를 평가하는 지표로, 0에서 1 사이의 값을 가집니다.
이 지표는 다음과 같이 해석됩니다:

  • 1: 예측과 실제가 완전히 일치 👍
  • 0: 예측이 무작위 수준 😐
  • 0 이하: 무작위보다 낮은 일치도 😢

QWK 계산 절차 🧮

QWK를 계산하려면 세 가지 행렬이 필요합니다: 관찰 행렬 (O), 가중치 행렬 (W), 기대 행렬 (E)입니다. 각 행렬에 대해 자세히 설명해보겠습니다.


1. 관찰 행렬 ( O ) 🧐

  • 설명: 관찰 행렬 ( O )는 ( N \times N ) 크기의 행렬로, ( N )은 예측 및 실제 레이블의 개수를 나타냅니다.
  • 구성 방법: ( O_{i,j} )는 실제 라벨이 ( i )이고, 예측 라벨이 ( j )인 인스턴스의 개수를 나타냅니다.

예를 들어, 실제 레이블이 ([1, 2, 2, 3])이고 예측 레이블이 ([1, 2, 3, 3])이라면, 관찰 행렬 ( O )는 다음과 같습니다:

O=[100011001]O = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix}

각 요소는 실제와 예측 레이블 간의 일치도를 표시하는 빈도수입니다.


2. 가중치 행렬 ( W ) ⚖️

  • 설명: ( W )는 레이블 간 거리 차이를 제곱하여 패널티를 부여하는 행렬로, 예측과 실제가 다를수록 더 큰 패널티를 줍니다.
  • 계산 공식:
Wi,j=(ij)2(N1)2W_{i,j} = \frac{(i - j)^2}{(N - 1)^2}

여기서 ( i )와 ( j )는 레이블 값이고, ( N )은 레이블의 개수입니다.

예를 들어, ( N = 4 )일 경우 ( W )는 다음과 같습니다:

W=[00.1110.44410.11100.1110.4440.4440.11100.11110.4440.1110]W = \begin{bmatrix} 0 & 0.111 & 0.444 & 1 \\ 0.111 & 0 & 0.111 & 0.444 \\ 0.444 & 0.111 & 0 & 0.111 \\ 1 & 0.444 & 0.111 & 0 \end{bmatrix}

이 행렬에서는 예측과 실제의 차이가 클수록 가중치가 더 크게 적용됩니다. 📉


3. 기대 행렬 ( E ) 🔮

  • 설명: 기대 행렬 ( E )는 예측값과 실제값이 독립적이라고 가정할 때 나올 수 있는 기대치입니다.
  • 계산 방법:
    • 실제 레이블과 예측 레이블의 히스토그램(분포)을 계산합니다.
    • 두 히스토그램의 외적(outer product)을 계산하여 기대 행렬의 각 요소를 얻습니다.
    • 최종적으로 ( E )와 ( O )의 합이 같아지도록 정규화합니다.

예를 들어, 실제 레이블의 히스토그램이 ([2, 1, 1])이고 예측 레이블의 히스토그램이 ([1, 2, 1])이라면, 기대 행렬 ( E )는 다음과 같습니다:

E=[0.51.00.50.250.50.250.250.50.25]E = \begin{bmatrix} 0.5 & 1.0 & 0.5 \\ 0.25 & 0.5 & 0.25 \\ 0.25 & 0.5 & 0.25 \end{bmatrix}

QWK 계산 공식 📐

이제 세 개의 행렬 ( O ), ( W ), ( E )가 준비되었으므로, 아래 공식을 통해 QWK를 계산할 수 있습니다:

κ=1i,jWi,jOi,ji,jWi,jEi,j\kappa = 1 - \frac{\sum_{i,j} W_{i,j} O_{i,j}}{\sum_{i,j} W_{i,j} E_{i,j}}
  • 분자: 가중치가 적용된 실제 관찰 불일치도로, ( W )와 ( O )의 원소별 곱을 더한 값입니다.
  • 분모: 가중치가 적용된 기대 불일치도로, ( W )와 ( E )의 원소별 곱을 더한 값입니다.

이제 평가지표를 정리했으니, 본격적으로 EDA 과정에 들어가겠습니다! 🔍


EDA 과정 설명 🔎

1. 문제 상황 및 목표 🚀

이 EDA 과정에서는 sii에 따라 PCIAT-PCIAT_Total 값의 최소값과 최대값을 구하고, sii 값이 있는 데이터를 필터링하여 누락된 값을 시각적으로 확인했습니다. 또한 PCIAT-PCIAT_Total이 개별 PCIAT 항목들의 합으로 올바르게 계산되었는지를 검토하였습니다.

2. 코드 설명 및 새로운 함수 활용법 🛠️

agg() 함수를 사용한 그룹별 최소 및 최대값 구하기

pciat_min_max = train.groupby('sii')['PCIAT-PCIAT_Total'].agg(['min','max'])
pciat_min_max = pciat_min_max.rename(
                    columns = {'min' : 'Minimun PCIAT total Score', 'max' : 'Maximum total PCIAT Score'})
pciat_min_max
  • 이 코드는 train 데이터에서 sii 값에 따라 PCIAT-PCIAT_Total 값의 최소값과 최대값을 그룹별로 구하는 과정입니다. agg(['min', 'max'])를 사용하여 minmax 값을 동시에 구할 수 있습니다.
  • 이후 .rename()을 통해 열 이름을 직관적으로 변경하여, Minimum PCIAT total ScoreMaximum total PCIAT Score로 구분하여 분석을 진행했습니다.

notna()를 사용하여 sii 값이 있는 데이터 필터링하기 📋

train_with_sii = train[train['sii'].notna()][columns_not_in_test] # ~isna() == notna()
  • 평소 ~data['컬럼명'].isna()를 통해 NaN이 아닌 값을 필터링했지만, 여기서는 notna()를 사용하여 한 번에 해당 작업을 처리할 수 있음을 알게 되었습니다.
  • notna()isna()와 반대로 동작하여, NaN이 아닌 값들만 선택할 수 있게 합니다.

style.applymap()을 사용하여 누락된 값 시각적으로 강조하기 🌸

train_with_sii[train_with_sii.isna().any(axis=1)].head().style.applymap(
    lambda x: 'background-color: #FFC0CB' if pd.isna(x) else ''
)
  • style.applymap()을 사용해 NaN 값이 있는 셀에 배경색을 적용하여 시각적으로 강조할 수 있습니다. NaN 값인 경우 #FFC0CB 색상(핑크색)을 배경으로 지정하였고, 그렇지 않으면 아무 색상도 적용되지 않도록 하였습니다.
  • 이 방법은 누락된 데이터를 직관적으로 파악하는 데 유용하며, 시각적 EDA에서 유용하게 활용할 수 있습니다.

sum()을 사용하여 PCIAT 항목의 합이 총점과 일치하는지 확인하기 ✔️

PCIAT

_cols = [f'PCIAT-PCIAT_{i+1:02d}' for i in range(20)]
recalc_total_score = train_with_sii[PCIAT_cols].sum(axis=1, skipna=True)
(recalc_total_score == train_with_sii['PCIAT-PCIAT_Total']).all() # PCIAT Values Sum == Total
  • sum(axis=1, skipna=True)NaN 값을 무시하고 각 행을 기준으로 합계를 계산합니다. 이 코드는 PCIAT-PCIAT_Total이 실제로 개별 항목들의 합과 동일하게 계산되었는지 확인하는 과정입니다.
  • 마지막으로, (recalc_total_score == train_with_sii['PCIAT-PCIAT_Total']).all()을 사용해 계산된 총합과 PCIAT-PCIAT_Total의 값이 모두 일치하는지 확인합니다.

위의 코드를 통해 pandas에서 제공하는 agg, notna, applymap과 같은 다양한 기능을 활용하여 데이터의 특정 부분을 필터링하거나 시각적으로 강조할 수 있음을 알게 되었어요. 특히 notna() 함수는 기존에 ~data['컬럼명'].isna()와 같은 방식 대신 간결하게 사용할 수 있어 유용했고, 평소 Pandas를 자주 사용해서 쉽게 접근했지만 하나하나 진행하며 코드를 익히는 시간이 될 거 같다는 생각이 들었어요 오늘 EDA는 여기까지~😊

profile
AI로 유용한 서비스 개발을 꿈꾸는 A린이

0개의 댓글