Kaggle - 타이타닉 생존자 예측하기 (1)

Posted on June 16, 2018
티스토리로 블로그 이사중입니다.
http://kminito.tistory.com/ ×

개요

타이타닉 침몰 사건은 1912년 4월 10일 영국의 사우샘프턴을 떠나 미국 뉴욕으로 향하던 ‘RMS 타이타닉’이라는 여객선이 빙산과 충돌하여 침몰한 사건으로, 총 탑승자 2,223명 중 1,514이 사망했습니다. (위키피디아)

당시 타이타닉 탑승자의 정보를 바탕으로 생존자를 예측하는 문제를 캐글에서 입문용으로 제공하고 있어서, 저도 연습 겸 배운 내용 복습 겸 시작하게 되었습니다. 캐글 홈페이지에 있는 다른 분들이 쓰신 글들을 바탕으로 따라하면서 제가 따로 궁금한 것들도 해보도록 하겠습니다.

문제 및 데이터셋 : Titanic: Machine Learning from Disaster
참고한 글 : Titanic Data Science Solutions, Titanic best working Classifier , Introduction to Ensembling/Stacking in Python

데이터 불러오기

1
2
3
4
5
6
7
import numpy as np
import pandas as pd
%matplotlib inline

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
full_data = [train, test]

두 파일 모두 불러온 후 데이터를 살펴보았습니다.

1
2
3
print(train.info())
print('\n')
print(test.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 66.2+ KB
None


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 27.8+ KB
None

train는 891개, test는 418개의 데이터로 이루어져 있고, Age나 Cabin 등은 missing value가 있는 것을 알 수 있습니다. 하나씩 살펴보도록 하겠습니다.

EDA / Feataure Engineering (1)

1. Survived

Survived는 생존 여부를 표시한 것입니다. 값이 1이면 생존을 의미합니다.

1
train['Survived'].value_counts()
0    549
1    342
Name: Survived, dtype: int64

891명 중 342명이 살아남은 것을 알 수 있습니다.

2. Pclass

Pclass는 티켓 클래스로, 1은 1등석, 2는 2등석, 3은 3등석을 의미합니다. 문제에 따르면, 티켓 클래스로 사회경제적 지위를 알 수 있다고 합니다.
빠진 데이터가 없고, 이미 정수형이므로 바로 어떤 관계가 있는지 살펴보겠습니다.

1
train[['Pclass', 'Survived']].groupby(['Pclass']).mean()
Pclass    Survived   
1         0.629630
2         0.472826
3         0.242363

티켓 클래스가 높을수록 생존할 확률이 높은 것을 알 수 있습니다.

평균을 바로 구하지 않고, 아래와 같이 각 티켓 클래스별로 살아남은 사람 수를 구한 후 각 티켓 별 총 인원으로 나누어 주어도 같은 값을 확인할 수 있습니다.

1
train.groupby(['Pclass']).count()['Survived']
1
train['Pclass'].value_counts()

3. Sex

성별도 같은 위와 같은 방식으로 확인해 보겠습니다.

1
2
train[["Sex", "Survived"]].groupby(['Sex'], as_index=False).mean()
# as_index = False 는 결과의 인덱스가 두 줄로 나오는 것이 불편해서 추가함
Sex	Survived
0	female	0.742038
1	male	0.188908

여성의 생존확률이 월등히 높습니다.

4. SibSp and Parch

SibSp는 함께 탑승한 형, 누나, 동생 등 형제자매(Siblings)의 숫자와 아내, 남편 등의 배우자 (Spouse)의 숫자를 합친 것이고, Parch는 함께 탑승한 아버지, 어머니, 아들, 딸 등 직계가족(Parents + Children)의 숫자를 합친 것입니다.

먼저 데이터 안에 어떤 값들이 있는지 알아보겠습니다.

1
2
print(train['SibSp'].unique())
print(train['Parch'].unique())
[1 0 3 4 2 5 8]
[0 1 2 5 3 4 6]

위의 티켓클래스, 성별과 같은 방식으로 한번 생존자의 비율을 구해보면

1
print(train[["SibSp", "Survived"]].groupby(['SibSp']).mean())
SibSp	Survived
0	0	0.345395
1	1	0.535885
2	2	0.464286
3	3	0.250000
4	4	0.166667
5	5	0.000000
6	8	0.000000

위의 경우들과는 달리 뚜렷한 관계가 보이지 않습니다. Parch도 마찬가지입니다.

그래서 이번에는 SibSp와 Parch를 합쳐 함께 탄 가족들의 숫자를 보여주는 열을 새로 만들어보도록 하겠습니다. 열의 이름은 FamilySize이고, 가족 없이 혼자 탄 경우에는 1이 되도록 하기 위해 SibspParch의 값을 합친 후 1이 되도록 할 것입니다.

1
2
for dataset in full_data:
    dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
1
print (train[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).mean())
FamilySize	Survived
0	1	0.303538
1	2	0.552795
2	3	0.578431
3	4	0.724138
4	5	0.200000
5	6	0.136364
6	7	0.333333
7	8	0.000000
8	11	0.000000

아까와 달라진 것이 있다면, 혼자 올 때 보다 다른 가족과 함께 왔을 때 네명까지는 생존 확률이 증가한다는 것입니다.

조금 더 나아가, 혼자 탔는지 아닌지를 보여주는 열을 하나 더 추가하도록 하겠습니다.

1
2
3
for dataset in full_data:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1, 'IsAlone'] = 1
1
train[['IsAlone', 'Survived']].groupby(['IsAlone'], as_index=False).mean()
IsAlone	Survived
0	0	0.505650
1	1	0.303538

5. Embarked

Embared는 탑승한 항구를 나타냅니다. C는 Cherbourg, Q는 Queenstown, S는 Southampton 입니다. 탑승 항구같은 경우에는 빠진 값(Missing value)이 2개 있습니다. 먼저 어느 항구에서 가장 많이 탑승했는지 확인한 후, 빠진 값을 최빈값으로 채우도록 하겠습니다.

1
train['Embarked'].value_counts()
S    644
C    168
Q     77
Name: Embarked, dtype: int64

S인 싸우쓰햄튼이 가장 많으므로, 없는 값 두개를 S로 채우도록 하겠습니다.

1
2
for dataset in full_data:
  dataset['Embarked'] = dataset['Embarked'].fillna('S')

이제 다시 항구별로 생존률을 확인해보겠습니다.

1
train[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean()
Embarked	Survived
0	C	0.553571
1	Q	0.389610
2	S	0.339009

항구별로 차이가 있긴 한데, 사실 직관적으로는 어떤 차이가 있어서 그런 건지 잘 모르겠습니다. 탑승하는 항구에 따라 배 안에서의 좌석 위치가 다른가 싶기도 합니다.