썸네일 이미지 출처: https://content.techgig.com/3-mini-project-ideas-for-computer-science-students/articleshow/78109217.cms
지난 번 웹크롤링 Toy 프로젝트에 이어서 두 번째 프로젝트이다. EDA로서는 첫 프로젝티이기도 하다. 머신러닝을 이용하여 분석 및 예측까지 가지 않는, 데이터 전처리이전까지만 완료해도 되는 EDA 프로젝트이다.
: 보통 csv 파일을 읽어서 pandas 형태로 받아서 'df'라는 이름의 변수에 저장한다. 그리고 (위에 기재했던 스크린샷처럼) df.shape, df.describe(), df.info() 등 함수로 전반적인 형태를 관찰한다. 행(row)과 열(column, feature)의 개수를 확인하거나 데이터의 형태(value)를 확인하기도 한다.
a_df.isnull().sum()
null_list= []
for col in a_df.columns:
if a_df[col].isnull().sum() > 1260701:
null_list.append(col)
print(len(null_list))
null_list
생각보다 결측치가 많은 컬럼이 많다. 전체 행의 수(=데이터의 수)의 절반(1260701개) 이상이 결측치인 컬럼도 있었다. 그래서 1차적으로 필자의 자력으로 결측치가 많이 있는 컬럼부터 제거하려고 했다. EDA 노트 참고하지 않고 시도하려 했다. 하지만, 수차례 시도했으나, 끝까지 Google Colab의 RAM이 허락해주지를 않았다.
어쩔 수 없이, EDA 노트를 참고하게 되었다. 그런데, 노트 저자는 초반부터 쏘쿨하게 150개의 컬럼 중 약 23개의 컬럼만을 고르고 나머지는 버렸다. 무슨 근거로, 어떤 코드로 버렸는지 궁금해서 유심히 살펴보니, 다음 스크린샷과 같은 표로 정리해두었다. (길이가 많이 길어서 일부만 스크린샷을 촬영했다.)
그래서 결론적으로는 다음 스크린샷처럼 필자도 EDA 노트 저자처럼 똑같이 컬럼을 선별하였다.
또한 RAM 보존을 위해, 다음 코드를 실행하여, 이전 변수들을 제거하였다.
del a_df, r_df
이 부분도 EDA 노트의 덕을 안 봤다고 하면 거짓말일 것이다. EDA 노트 저자가 선별한 컬럼을 'df'에 pandas DataFrame으로 할당하였다. 그리고 직후에, 'loan_status' 컬럼을 대상을 value_counts() 로 value 값들의 종류와 각 value 별로 몇 개가 있는지 살펴 보았다.
위 스크린샷처럼 나오는데, EDA 노트 저자는 value 값 중, 'Fully Paid', 'Charged Off', 'Default' value 값에 집중하였다. loan_status 중에서 위 세 항목이 가장 이 All Lending Club loan data (사용자 대출 여부 예측 대회)의 목적과 잘 부합하며, 나머지 value 값들은 의미가 없기 때문이다.
따라서 저자는, default 값 중, 'Fully Paid', 'Charged Off', 'Default'에 해당하는 데이터들을 조회하기 위해 다음과 같이 마스킹했다.
df = df[df.loan_status.isin(['Fully Paid', 'Charged Off', 'Default'])]
# 같은 코드이다.
df = df[df['loat_status'].isin(['Fully Paid', 'Charged Off', 'Default'])]
그리고 새로운 컬럼 'default'를 다음과 같이 타겟컬럼으로 만들었다. apply(), lambda 함수를 이용해서, loan_status 컬럼의 value 값들인 'Fully Paid', 'Default', 'Charged Off' 을 숫자 0과 1로 바꾸었다.
df['default'] = df.apply(lambda a: 1 if a.loan_status in ['Default', 'Charged Off'] else 0, axis = 1)
df['default']
'Fully Paid'의 경우 '1'로 바꾸고, 'Charged Off', 'Default'의 경우에는 '0'으로 바꾸었다. 머신러닝에서는 문자열연산은 힘들기 때문에 숫자로 (숫자만을 인식하고 연산할 수 있기 때문) 변환해야 한다. 이러한 작업은 '원핫인코딩'이라고도 하며, get_dummies() , Tensorflow(keras) 함수로는 to_categorical() 가 있다.
그리고 저자는 위와 같이 카테고리화 한 다음, 'loan_status' 컬럼은 쿨하게 버렸다. (RAM을 위해서였던 듯... 같은 고통 속에 있었나보다.)
df = df.drop('loan_status', axis=1)
정리를 하자면, EDA 노트 저자는 묻혀있던 '타겟 컬럼'을 찾고 끄집어내면서, 분류문제로 가시화한 것.
from tensorflow import keras
from tenrsorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, MaxPool2D, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam, SGD
from keras.layers.advanced_activations import LeakyReLU
딥러닝을 하게 되면 보게 될 것이다. ^^
: EDA 노트 저자는 그렇게 선별하고 마스킹한 df의 각 컬럼간의 상관관계를 살펴보기 위해 pandas corr()와 seaborn의 HEATMAP을 사용했다.
sns.set(style = 'whitegrid', font_scale = 1)
plt.figure(figsize = (12, 12))
plt.title("pearson corr matrix", fontsize = 24)
sns.heatmap(df.corr(), linewidths = 0.25, vmax= 0.7, square = True,
cmap = 'RdPu', linecolor='w',
annot=True, annot_kws = {'size':10},
cbar_kws={'shrink': .7})
'상관계수'라는 보편적인 개념은 'x가 증가할 때, y가 얼마나 증가(혹은 감소)하는 정도'를 말한다.
EDA 노트 저자의 다음 액션으로는 거르고 거른 컬럼들과 'default' 라는 타겟컬럼(새로 만든)간의 관계를 살펴보기 위해서 seaborn의 'boxplot'과 'barplot'을 이용하였다.
원래 EDA 노트 저자는 아래 두 코드를 elif 문을 이용하여 하나로 합쳤었는데, 한 번에 여러개 나오는 게 오래걸리기도 하고, Colab에서 보기도 힘들어서 필자 임의로 데이터 타입 type에 따라 나누었다.
: 특정되지 않은, 연속적인 숫자를 value로 가지는 컬럼의 경우는 boxplot을 사용하였다. 전반적인 분포와 평균, 분산 등을 대략적으로 살펴볼 수 있다.
for col in df.columns:
if df[col].dtype =='float64':
fig, ax = plt.subplots(figsize = (6, 6))
sns.boxplot(ax = ax, y=col, x='default', data=df, showfliers = False)
else:
pass
for문을 돌렸기 때문에, 여러 그래프가 나오는 게 맞다. 하지만, 스크롤압박의 부담을 줄여주기 위해서 다음과 같이 한 개의 그래프만을 첨부한다.
'int_rate'컬럼과 'default' 간의 관계를 boxplot으로
: 데이터 타입이 object인 경우와 컬럼에 담긴 value 값의 종류가 52가지 미만인 경우는, 범주형 컬럼일 가능성이 매우 크다. 그래서, 이러한 범주형 컬럼에서는 barplot으로 살펴보았다.
for col in df.columns:
if (df[col].dtype == 'object') and (len(df[col].unique())< 52):
fig, ax = plt.subplots(figsize = (6, 6))
data = df.groupby(col)['default'].value_counts(normalize=True).mul(100).rename('percent').reset_index()
sns.barplot(data=data, y='percent', x=col, hue='default', ax= ax)
else:
pass
'sub_grade'컬럼과 'default' 간의 관계를 boxplot으로
여기까지가 필자가 참고한 EDA 노트 저자의 EDA 단계까지 내용이다. 이후의 저자는 또 쏘쿨하게 위의 시각화를 바탕으로 상관관계가 없어보이는 컬럼 몇 개를 버리고(이것도 참고함...), 다음에 전처리를 하고 Logistic Regression 단계에 돌입한다.
필자가 필요했던 부분은 EDA 단계이기 때문에, 여기까지 참고를 했다. 나머지는 이어서 작성하겠다. 이후부터는 이를 바탕으로, 필자가 진행했던 EDA를 리뷰하겠다.