[캐글 필사] 타이타닉 튜토리얼 1 - Exploratory data analysis, visualization, machine learning

2021. 5. 10. 14:00레퍼런스/Kaggle

출처 : https://kaggle-kr.tistory.com/17?category=868316

 

import numpy as np
import pandas as pd 
import matplotlib.pyplot as plt 
import seaborn as sns 

sns.set(font_scale = 2)
plt.style.use('dark_background')

import missingno as msno 

# ignore warnings
import warnings
warnings.filterwarnings('ignore')

%matplotlib inline

Process

  1. 데이터셋 확인 - null data 확인, 수정
  2. EDA (탐색적 데이터 분석) - 개별 feature 분석, 상관관계 확인, 시각화 insight
  3. feature engineering - one-hot encoding, class로 나누기, 구간으로 나누기, 텍스트 데이터 처리
  4. modeling - sklearn 사용 (ML) / tensorflow, pytorch 사용 (DL)
  5. training , prediction - trainset으로 모델 학습, testset으로 prediction
  6. evaluation - 예측 성능이 원하는 수준인지 판단.

1. Dataset 확인

df_train = pd.read_csv('./train.csv')
df_test = pd.read_csv('./test.csv')

df_train.head()

df_train.describe() # 각 feature가 가진 통계치 반환

df_test.describe()

1.1 Null data check

for col in df_train.columns:
    msg = 'column: {:>10}\t Percent of NaN value : {:.2f}%'.format(col, 
            100 *(df_train[col].isnull().sum() / df_train[col].shape[0]))
    print(msg)

for col in df_test.columns:
    msg = 'column: {:>10}\t Percent of NaN value : {:.2f}%'.format(col,
            100* (df_test[col].isnull().sum() / df_test[col].shape[0]))
    print(msg)

msno.matrix(df=df_train.iloc[:,:], figsize=(8,8), color = (0.8,0.5,0.2))

msno.bar(df = df_train.iloc[:,:], figsize = (8,8), color = (0.8,0.5,0.2))

msno.bar(df = df_test.iloc[:, :], figsize = (8,8) , color = (0.8,0.5,0.2))

1.2 Target label 확인

f, ax = plt.subplots(1,2, figsize = (18,8))
df_train['Survived'].value_counts().plot.pie(explode=[0,0.1], 
                                             autopct = '%1.1f%%', 
                                             textprops = {'fontsize':20 , 'color':'Black'},
                                             ax= ax[0], 
                                             shadow = True)
ax[0].set_title('Pie plot - Survived', fontsize = 20)
ax[0].set_ylabel('')

sns.countplot('Survived', data= df_train, ax = ax[1])
# ax[1].set_yticklabels(ax[1].get_yticks(), size = 15)
# ax[1].set_xticklabels(ax[1].get_xticks(), size = 15)

ax[1].set_title('Count plot - Survived', fontsize = 20)
plt.show()

  • 생존 : 38.4%
  • target label 분포 균일 balanced
  • cf) embalanced (불균일) 할 경우,
    (100개 중, 1이 99개, 0이 1개 일때, 모델이 모든 것을 1이라고 해도 정확도가 99%가 나옴,
    0을 찾는 문제일 경우, 정확도가 99% 임에도 원하는 결과를 얻지 못함)

2. 탐색적 데이터 분석 (Exploratory data analysis)

시각화 라이브러리 이용 : matplotlib, seaborn, plotly

2.1 Pclass

  • ordinal (서수형) 데이터, 카테고리이면서, 순서가 있는 데이터 타입
"""
Pclass: 티켓의 클래스 (categorical feature)
1 : 1st
2 : 2nd 
3 : 3rd
"""
df_train[['Pclass', 'Survived']]

df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index = True).count()

df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index = True).sum()

pd.crosstab(df_train['Pclass'],
            df_train['Survived'], 
            margins = True).style.background_gradient(cmap='summer_r')

# 각 클래스별 생존률
df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index = True).mean()

df_train[['Pclass', 'Survived']].groupby(['Pclass'], 
                                         as_index = True).mean().sort_values(by = 'Survived', 
                                                                             ascending = False).plot.bar()
# pclass가 좋을 수록(1st) 생존률이 높음

y_position = 1.02 
f, ax = plt.subplots(1,2, figsize = (18,8))

df_train['Pclass'].value_counts().plot.bar(
    color = ['#CD7F32', '#FFDF00', '#D3D3D3'], ax = ax[0])
ax[0].set_title('Number of Passengers By Pclass', y = y_position)
ax[0].set_ylabel('Count')

sns.countplot('Pclass', hue = 'Survived', data= df_train, ax = ax[1])
plt.setp(ax[1].get_legend().get_title(), fontsize = '21')
ax[1].set_title('Pclass: Survived vs Dead', y = y_position)
plt.show()

  • 결론: 클래스가 높을 수록, 생존 확률이 높음,
  • Pclass 1,2,3 순으로 63% , 48% , 25%임
  • Pclass가 큰 영향을 미친다고 판단

2.2 Sex

f, ax = plt.subplots(1,2 ,figsize = (18,8))
df_train[['Sex', 'Survived']].groupby(['Sex'], as_index = True).mean().plot.bar(ax = ax[0])
ax[0].set_xticklabels(ax[0].get_xticklabels(), rotation = 0)
ax[0].set_title('Survived vs Sex')

sns.countplot('Sex', hue= 'Survived', data = df_train, ax = ax[1])
ax[1].set_title('Sex: Survived vs Dead')
plt.show()

  • 여자가 생존할 확률이 더 높음
df_train[['Sex', 'Survived']].groupby(['Sex'], 
                                      as_index = False).mean().sort_values(by = 'Survived', ascending = False)

pd.crosstab(df_train['Sex'], 
            df_train['Survived'], 
            margins = True).style.background_gradient(cmap = 'summer_r')

2.3 Both Sex and Pclass

  • seaborn factorplot 이용 : 3차원 그래프
sns.factorplot('Pclass', 'Survived', hue = 'Sex', data = df_train, size = 5, aspect = 1.5)

  • 모든 클래스에서 female이 살 확률이 male 보다 높음
  • 남자, 여자 상관없이 클래스가 높을 수록 살 확률이 높음
sns.factorplot(x = 'Sex', y = 'Survived', col = 'Pclass', 
              data = df_train, satureation =.5, 
              size = 4, aspect = 1)

2.4 Age

print('제일 나이 많은 탑승객 : {:.1f} 살'.format(df_train['Age'].max()))
print('제일 어린 탑승객 : {:.1f} 살'.format(df_train['Age'].min()))
print('탑승객 평균 나이 : {:.1f} 살'.format(df_train['Age'].mean(), common_grid=False))
>>> 
제일 나이 많은 탑승객 : 80.0 살
제일 어린 탑승객 : 0.4 살
탑승객 평균 나이 : 29.7 살
fig, ax = plt.subplots(1,1 ,figsize = (9,5))
sns.kdeplot(df_train[df_train['Survived'] == 1]['Age'], ax = ax)
sns.kdeplot(df_train[df_train['Survived'] == 0]['Age'], ax = ax)
plt.legend(['Survived == 1', 'Survived ==0'])
plt.show()

  • 생존자 중 나이가 어린 경우가 많음
# Age Distribution withing classes 
plt.figure(figsize = (8,6))
df_train['Age'][df_train['Pclass'] == 1].plot(kind = 'kde')
df_train['Age'][df_train['Pclass'] == 2].plot(kind = 'kde')
df_train['Age'][df_train['Pclass'] == 3].plot(kind = 'kde')

plt.xlabel('Age')
plt.title('Age Distribution within classes')
plt.legend(['1st Class', '2nd Class', '3rd Class'])

 

  • class가 커질 수록 나이 많은 사람의 비중이 커짐
cummulate_survival_ratio = []
for i in range(1, 80):
    cummulate_survival_ratio.append(df_train[df_train['Age'] < i]['Survived'].sum() / 
                                   len(df_train[df_train['Age'] < i]['Survived']))
plt.figure(figsize = (7,7))
plt.plot(cummulate_survival_ratio)
plt.title('Survival rate change depending on range of Age', y =1.02)
plt.ylabel('Survival rate')
plt.xlabel('Range of Age(0~x)')
plt.show()

  • 나이가 어릴 수록, 생존률이 높음.

2.5 Pclass, Sex, Age

  • violinplot 이용
  • x축 : (Pclass, Sex)
  • y축 : (Age)
f, ax = plt.subplots(1,2 , figsize = (18, 8))
sns.violinplot('Pclass', 'Age', hue = "Survived", 
               data = df_train, scale ='count', 
               split = True, ax = ax[0])
ax[0].set_title('Pclass and Age vs Survived')
ax[0].set_yticks(range(0,110,10))

sns.violinplot('Sex', 'Age', hue = 'Survived',
              data= df_train, scale = 'count', 
              split = True, ax = ax[1])
ax[1].set_title('Sex and Age vs Survived')
ax[1].set_yticks(range(0,110,10))
plt.show()

  • 모든 클래스에서 나이가 어릴 수록, 생존을 많이 함
  • 오른쪽 그림만 보면, 명확히 여자가 생존을 많이 함
  • 여성과 아이를 먼저 챙김

2.6 Embarked

  • 탑승한 항구
f,ax = plt.subplots(1,1, figsize = (7,7))
df_train[['Embarked', 'Survived']].groupby(['Embarked'], 
                                           as_index = True).mean().sort_values(by = 'Survived' 
                                                                               , ascending = False).plot.bar(ax=ax)
ax.set_xticklabels(ax.get_xticklabels(), rotation = 0)

  • C 항구가 제일 높음
f, ax = plt.subplots(2,2 , figsize = (20,15))
sns.countplot('Embarked', data = df_train, ax= ax[0,0])
ax[0,0].set_title('(1) No. Of Passengers Boarded')

sns.countplot('Embarked', hue = 'Sex', data = df_train, ax = ax[0,1])
ax[0,1].set_title('(2) Male - Female Split for Embarked')

sns.countplot('Embarked', hue = 'Survived', data = df_train, ax = ax[1,0])
ax[1,0].set_title('(3) Embarked vs Survived')

sns.countplot('Embarked', hue = 'Pclass', data = df_train, ax = ax[1,1])
ax[1,1].set_title('(4) Embarked vs Pclass')
plt.subplots_adjust(wspace= 0.2, hspace = 0.5)
plt.show()

  • Figure(1) - S에서 가장 많은 사람이 탑승함
  • Figure(2) - C와 Q는 남녀 비율이 비슷, S는 남자가 더 많음
  • Figure(3) - S의 경우 생존 확률이 많이 낮음
  • Figure(4) - C가 생존률이 높은 것은, 클래스가 높은 사람이 많이 타서임. S는 3rd class가 많아서 생존 확률이 낮게 나옴

2.7 Family - SibSp(형제 자매) + Parch(부모, 자녀)

df_train['FamilySize'] = df_train['SibSp'] + df_train['Parch'] + 1 # 자신을 포함
df_test['FamilySize'] = df_test['SibSp'] + df_test['Parch'] + 1 # 자신을 포함 
print('Maximum size of Family: ' , df_train['FamilySize'].max())
print('Minimum size of Family: ' , df_train['FamilySize'].min())
>>> 
Maximum size of Family:  11
Minimum size of Family:  1
f, ax = plt.subplots(1,3 , figsize = (25,6))
sns.countplot('FamilySize', data = df_train, ax = ax[0])
ax[0].set_title('(1) No. Of Passengers Boarded', y = 1.02)

sns.countplot('FamilySize', hue = 'Survived', data = df_train, ax = ax[1])
ax[1].set_title('(2) Survived countplot depending on FamilySize', y =1.02)

df_train[['FamilySize', 'Survived']].groupby(['FamilySize'], 
                                             as_index = True).mean().sort_values(by = 'Survived', ascending = False).plot.bar(ax=ax[2])
ax[2].set_xticklabels(ax[2].get_xticklabels(), rotation = 0)
ax[2].set_title('(3) Survived rate depending on FamilySize', y = 1.02)

plt.subplots_adjust(wspace = 0.2, hspace = 0.5)
plt.show()

  • Figure (1) - 가족 크기가 1~11까지 있음, 대부분 1 명 그다음으로 2,3,4 명
  • Figure (2), (3) - 가족 크기에 따른 생존 비교, 가족이 4명인 경우 가장 생존확률이 높음
  • 가족 수가 많아질 수록 , (5,6,7,8,11) 생존 확률이 낮아짐
  • 가족 수가 너무 작아도(1), 너무 커도 (5,6,8,11) 생존확률이 작음
  • 3~4명 선에서 생존확률이 높음

2.8 Fare

  • 탑승 요금 : contious feature
fig, ax = plt.subplots(1, 1, figsize = (8,8))
g = sns.distplot(df_train['Fare'], color = 'w', 
                 label = 'Skewness : {:.2f}'.format(df_train['Fare'].skew()) , ax = ax)
g = g.legend(loc = 'best')

  • distriobution 이 매우 비대칭임 (high skewness)
  • 몇개 없는 outlier에 대해 너무 민감하게 반응하면, 실제 예측 시에 좋지 못한 결과를 얻을 수 있음
  • outlier의 영향을 줄이기 위해 Fare에 log를 취함
# 결측치를 평균 값으로 치환 
df_test.loc[df_test.Fare.isnull(), 'Fare'] = df_test['Fare'].mean() 

df_train['Fare'] = df_train['Fare'].map(lambda i : np.log(i) if i > 0 else 0)
df_test['Fare'] = df_test['Fare'].map(lambda i : np.log(i) if i > 0 else 0)
fig, ax = plt.subplots(1, 1, figsize = (8,8))
g = sns.distplot(df_train['Fare'], color = 'w', 
                label = 'Skewness : {:.2f}'.format(df_train['Fare'].skew()), ax = ax )
g = g.legend(loc = 'best')

  • log를 취하니 비대칭성이 많이 사라짐
  • 이런 작업을 사용하여 모델이 좀 더 좋은 성능을 내도록 할 수 있음
    (feature engineering 의 일종)

2.9 Cabin

  • Nan 이 80% 임. 모델에 포함시키지 않겠음
df_train['Cabin'].isnull().sum()/len(df_train['Cabin'])
>>> 0.7710437710437711

2.10 Ticket

  • Nan 은 없으나, string data이므로 실제 모델에 어떻게 사용할지 아이디어가 필요함
df_train['Ticket'].isnull().sum()
>>> 0
df_train['Ticket'].value_counts()

'레퍼런스 > Kaggle' 카테고리의 다른 글

[캐글필사] EDA to Prediction (DieTanic)  (0) 2021.06.13
kaggle 필사 커리큘럼 -(진행중)  (0) 2021.05.10