본문 바로가기

[Titanic] EDA (Exploratory Data Analysis) - 타이타닉 데이터 분석(1)

Derrick 발행일 : 2022-10-06
728x90
반응형

 

※ 타이타닉에 승선한 사람들의 데이터를 활용하여 승객들의 생존여부를 예측하는 모델 구축 / 개발

※ Dataset Check(타이타닉 첫번째 chapter) 이후, EDA 과정을 통해 Feature 분석 단계

 

- Target Label인 'Survived'를 제외한 총 11개의 Feature 중 어떤 Feature를 고려하면 강력한 insight를 얻을
  수 있을지 가설과 결과를 도출하기 위해 EDA(Exploratory Data Analysis) 단계는 매우 중요하다.

  → 여기서 insight란, 통계적인 insight. 즉, 본인이 원하는 수치나 그림(그래프)를 출력하고 특정 결과를 도출

      하는 과정을 말한다.

  → 더불어 출력한 시각화된 데이터를 통해 말하고자/보고자 하는 것들을 해석할 수 있는 역량이 무엇보다 중요

  → 가설을 세웠다면, 그 가설에 맞는 그래프를 선택하고 상황에 맞게 plot하는 작업을 해보자

 

※ 그래프 출력 전, 도화지를 준비하는 3가지 방법 (예시)

 1) f, ax = plt.subplot(1, 1, figsize=(10, 10))  →  축을 반환해서 축을 기준으로 출력

 2) f = plt.figure(figsize=(10, 10))  →  Axes를 가지지 않고 도화지 펴는 방법

 3) plt.figure(figsize=(10, 10))  →  축과 Feature 모두를 반환하지 않는다.

 

1. EDA - Pclass (좌석등급)

 - Pclass(좌석등급) : 카테고리형 데이터이며, 순서가 있는 데이터

   → Pclass에 따른 생존률에 대해 변화를 확인

       (Pandas DataFrame을 이용하여 'Groupby' 라는 메서드를 사용해서 구현)

 

 # 'Pclass'  - Class별 생존자 현황 파악

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

 

 >> 

 

  → .groupby(['Pclass']) : 불러온 리스트를 묶어준다는 의미 (Pclass 기준으로)

      DataFrameGroupby 라는 개체가 생성되며, 많은 메서드를 내포하고 있다. (sum, count, mean 등)

  → as_index=True : 기준으로 잡는 'Pclass' Feature를 index로 설정할 것인지 지정 (True : 지정)

 

 # 특정 Feature에 있는 모든 값 출력 - unique() 사용

df_train['Survived'].unique()


>>
array([0, 1])
# 'Survived' Feature 내 값들은 0, 1만 가지고 있다.

 

 # Pclass Table 생성. 합계(All) 포함 - pd.crosstab()

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

 

>>

 

  → margins = True : All (포함) or False (미포함)

  → style.background_gradient(cmap=' ~ ')

     : cmap에 따라 background 색을 입히는 과정. cmap은 "color map scheme" 검색 후 더 찾아볼 수 있다.

 

 # Pclass별 생존률 파악 → mean = sum / count

  - 그룹화된 개체에 mean() 메서드를 통해 평균값(=생존률)을 출력할 수 있다.

 

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

 

>>

 

  → mean()을 통해 출력되는 값의 의미는 Binary한 Feature에서, 생존율을 의미한다.

  → Pclass 1 기준으로, ( 0 * 80 + 1 * 136 ) / 213 = 0.6296 (class 1에서의 생존율 - 위의 Table 참고)

  → 이를 통해 class별 생존률이 어떠한지 출력해볼 수가 있다.

 

 # Pclass별 생존률 bar plot 형태로 출력

  - 출력되는 타입은 'Pandas DataFrame'으로 plot() 메서드를 가지고 있다. 

  - 위 DataFrame에서 'Survived'만 Series 형태이기 때문에, plot하면 Survived 값만 출력. 

    → 더 출력하고 싶은 Feature가 있다면, List형식으로 묶어서 불러와야 한다. (groupby)

 

df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=True).mean().sort_values(by='Survived', ascending=False).plot.bar()

 

>>

 

  → sort_values(by='Survived') : 'Survived' 기준으로 데이터 분류

  → ascending=False : 내림차순 정렬 (+ True : 오름차순 정렬)

  → 이 경우, Pclass 값을 그래프로 출력하지 않으면서 Class별 생존률이 궁금하기에 bar 형태로 시각화함

 

 # 데이터 Visualization (응용) - Pclass 도메인

  - Pclass별 탑승객과 생존여부 현황을 조금 더 응용된 코드로 출력

 

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])
ax[1].set_title('Pclass: Survived vs Dead', y=y_position)
plt.show()

 

 >>

 

   → 왼쪽 그래프(ax[0]) : 각 class별 탑승자의 수 - value_count()

   → 오른쪽 그래프(ax[1]) : 각 class별 생존자 합계 - sns.countplot

   → y_position : 그래프의 title과 그래프 사이 간 간격

   → hue : 'Survived'를 기준으로 데이터 구분 (색깔로 나누어서 구분할 수 있다.)

 

 # Pclass EDA 분석 후 결론

  - Pclass가 높을 수록 생존 확률이 높다.

    → Class가 낮을수록 탑승자는 더 많지만, 생존확률만 봤을때는 class가 높을수록 생존률 ↑

 

 

 

2. EDA - Sex (성별)

  - Pandas Groupby와 seaborn countplot을 사용해서 시각화

  - 일단 소스코드를 필사하고, 결과물을 본 다음 하나씩 공부하자

 

 # 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_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=True).mean().plot.bar(ax=ax[0])

   : df_train의 Sex, Survived Feature(=Column)을 가져오고, groupby를 통해 Sex를 기준으로 가져옴

 

 - sns.countplot('Sex', hue='Survived', data=df_train, ax=ax[1])

   : seaborn으로 countplot. 색깔(hue)로 구분지어 'Survived' 기준으로 분류. 2번째 그래프 출력(ax[1])

 

 - 결론

   → 성별(Sex)로 나누었을 때, 여성(Female)이 살아남을 확률이 더 높다

   → Countplot을 봤을 때도, 남성(male)의 경우 살아남을 확률은 현저히 낮고, 여성은 높다

 

 # 성별에 따른 생존자 현황 - pd.crosstab

  - Pandas의 Crosstab을 적용하여 위의 시각화된 그래프를 더욱 쉽게 출력해보자

  - margins=True : All도 포함해서 출력

 

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

 

 >> 

 

  → 위의 출력한 Table에서 확인할 수 있듯이, 선별된 것만 보면 70% 넘는 확률이 있으므로

      Pclass와 마찬가지로 예측모델을 쓸 수 있는 중요한 Feature가 될 수 있다.

 

 # 3개의 dimension으로 데이터 해석 - factorplot()

  - 지금까지는 2개의 dimension으로 그래프 출력하고 해석했으며, 지금은 3개(생존여부, 성별, 클래스)를 하나의

    그래프로 출력하여 해석해보자. → Seaborn - factorplot() 

 

sns.factorplot('Pclass', 'Survived', hue='Sex', data=df_train, size=6, aspect=1.5)

 

>>

 

   → ( x축, y축, hue(색깔)로 구분 )

   → size : 출력되는 그래프의 사이즈

   → aspect : 높을수록 옆으로 길어진다.

 

  - 해석

   → 1 Pclass일 때, 생존 확률이 male의 경우 40% 미만, female은 95% 정도

   → 2 Pclass일 때, 생존 확률이 male의 경우 20% 미만, female은 90% 정도

   → 전반적으로 Pclass가 낮을수록, 성별(Sex)가 여성(Female)인 경우 생존확률이 높은 것을 확인할 수 있다.

 

3. EDA - Age (나이)

 - 이번엔 Age(나이)별 생존확률을 비교하면서 데이터를 시각화해보고 분석해보자

 

 # 탑승객 나이(Age)별 현황 출력

print('제일 나이 많은 탑승객 : {:.1f} years'.format(df_train['Age'].max()))
print('제일 어린 탑승객 : {:.1f} years'.format(df_train['Age'].min()))
print('탑승객 평균 나이 : {:.1f} years'.format(df_train['Age'].mean()))

 

 >>

제일 나이 많은 탑승객 : 80.0 years
제일 어린 탑승객 : 0.4 years
탑승객 평균 나이 : 29.7 years

 

 # 나이(Age)별 데이터의 분포 확인 - kdeplot

  - 하나의 도메인에서 데이터의 히스토그램(kdeplot)으로 분포 확인

 

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()

 

 >>

 

   → df_train[df_train['Survived'] == 1] : 생존자들의 세부사항 출력

   → sns.kdeplot(df_train[df_train['Survived'] == 1]['Age'], ax=ax)

      : 생존자들 중에서 'Age' Feature만 반환하고, 이를 sns(=seaborn)으로 kdeplot 한다는 의미

 

 # 나이(Age)별 데이터 estimation - hist()

  - kdeplot (=Kernel Density Estimation(커널밀도추정))으로 시각화해서 현황 파악

  - 데이터들의 분포가 어떻게 되는지 추정(estimation) 하는 용도

 

df_train[df_train['Survived'] == 1]['Age'].hist()

 

>>

 

   → 결과를 보면, 나이(Age)가 어릴수록 생존될 확률이 높다는 것을 확인할 수 있다.

 

 # 전체 탑승객 중, Age와 Class의 전체 분포 현황

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(['lst Class', '2nd Class', '3rd Class'])

 

>>

 

 # Age 도메인 - 1번 Pclass 내 생존 분포 확인

  - 1st Class에서는 나이가 어린 사람일수록 생존확률이 높다. 

  - &(and) 로 2개의 조건을 함께 적용될 수 있도록 구현

 

fig, ax = plt.subplots(1,1,figsize=(10,5))
sns.kdeplot(df_train[(df_train['Survived'] == 0) & (df_train['Pclass'] == 1)]['Age'], ax=ax)
sns.kdeplot(df_train[(df_train['Survived'] == 1) & (df_train['Pclass'] == 1)]['Age'], ax=ax)
plt.legend(['Survived == 0', 'Survived == 1'])
plt.title('1st class')
plt.show()

 

>>

 

 # Age 도메인 - 2번 Pclass 내 생존 분포 확인

  - 2nd Class에서도 나이가 어린 사람일수록 생존확률이 높다.

 

fig, ax = plt.subplots(1,1,figsize=(10,5))
sns.kdeplot(df_train[(df_train['Survived'] == 0) & (df_train['Pclass'] == 2)]['Age'], ax=ax)
sns.kdeplot(df_train[(df_train['Survived'] == 1) & (df_train['Pclass'] == 2)]['Age'], ax=ax)
plt.legend(['Survived == 0', 'Survived == 1'])
plt.title('2nd class')
plt.show()

 

>>

 

 # Age 도메인 - 3번 Pclass 내 생존 분포 확인

  - 3rd Class에서도 나이가 어린 사람일수록 생존확률이 높다.

 

fig, ax = plt.subplots(1,1,figsize=(10,5))
sns.kdeplot(df_train[(df_train['Survived'] == 0) & (df_train['Pclass'] == 3)]['Age'], ax=ax)
sns.kdeplot(df_train[(df_train['Survived'] == 1) & (df_train['Pclass'] == 3)]['Age'], ax=ax)
plt.legend(['Survived == 0', 'Survived == 1'])
plt.title('3rd class')
plt.show()

 

>>

 

 # 나이(Age)별 생존 확률 시각화 - 0~75세

  - 정말 나이가 어린 탑승객일수록 생존 확률이 높을까?

  - 아래 결과를 통해, 나이가 어릴수록 생존확률이 확실히 높다는 결과를 도출할 수 있다. 특히 25세 이하!

 

change_age_range_survival_ratio = []

# 나이별 생존확률 확인
for i in range(1,80):
  change_age_range_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(change_age_range_survival_ratio)
plt.title('Survival rate change depending on range of Age', y=1.02)     # y값은 title의 위치를 옮겨주는 역할
plt.ylabel('Survival rate')
plt.xlabel('Range of Age(0-x)')
plt.show()

 

>>

 

  - 코드 분석

    → df_train[df_train['Age'] < i ]['Survived'].sum()

       : 1살부터 80살까지 반복되는 i살보다 어린 사람들의 rows를 반환한 후, 그 사람들 중 생존한 사람들의 총 명수 (sum)

    → len(df_train[df_train['Age'] < i ]['Survived'] 

       : 해당 나이(i)의 사람들의 총 명수. 사망/생존 포함 (평균을 내기 위해)

 

 - 예시

i = 10  # 10살
df_train[df_train['Age'] < i]['Survived'].sum() / len(df_train[df_train['Age'] < i]['Survived'])

 

>>

0.6129032258064516

 

 

 

 

 

 

 

댓글