본문 바로가기
머신러닝

타이타닉 생존자 예측_생존율 관련 요소

by 미생22 2024. 2. 23.
728x90

 

이번 시간에는 타이타닉 탑승자의 데이터를 기반으로, 생존율에 큰 영향을 미치는 요소와 만약 디카프리오가 실제 타이타닉에 탑승했다면 살아남았을까?에 대해서 머신러닝을 통해 알아보겠습니다. 
당시 타이타닉은 국제선으로 영국에서 미국 뉴욕으로 가던 배였습니다. 국제선이다보니 승객의 정보가 자세히 남아있어 이 데이터로 EDA와 머신러닝을 할 수 있다는 장점이 있습니다.

 

타이타닉 데이터는 제로베이스 데이터취업스쿨 민형기 강사의 Github에서 가져올 수 있습니다.

Github 주소는 강사님 블로그인 pinkwink 블로그에서 확인할 수 있습니다.

 

먼저, Github 주소로 데이터를 불러올 겁니다.

 

1. 데이터 불러오고 확인하기

import pandas as pd

titanic_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/titanic.xls'
titanic = pd.read_excel(titanic_url)
titanic.head()


참고로, xls 같은 엑셀 파일은 github에서 바로 raw 주소를 얻기 어려워서 그 옆에 csv 파일로 들어가 raw 주소를 딴 뒤 편집해주는 것이 좋습니다.

 

이제 데이터를 확인해보겠습니다.

각 컬럼의 의미부터 확인해보면 아래 표로 볼 수 있습니다.

이제 우리는 생존율에 관련된 데이터를 알아보려고 하기 때문에, 먼저 생존자와 사망자를 볼까요?

titanic['survived'].value_counts()

value_counts 함수를 쓰면 0,1로 이루어져있으면 0이 몇개고 1이 몇개인지 확인할 수 있습니다.
0은 사망자, 1은 생존자의 수기 때문에 사망자는 809명, 생존자는 500명으로 보입니다.

위 컬럼 표는 우리가 가져온 타이타닉 데이터에서 각 컬럼의 의미입니다.

 

이 상태에서 이걸 pie 그래프로 그려보겠습니다.

 

titanic['survived'].value_counts().plot.pie(autopct='%1.1f%%',shadow=True, explode=[0, 0.05])

 

0, 1도 나타나있고, autopct로 비율이 어느정도인지 넣어줄 수 있습니다.
shadow는 원의 그림자를, explode는 원이 약간 떨어져서 나오게 합니다.

 

이 상태에서 두 그림을 하나의 그림에 같이 나오게 할 수 있습니다.
(1, 2) 형태로 얻고 싶으면 plt.subplots(1, 2)를 쓰면 되고, figsize도 옵션으로 넣을 수 있습니다.

 

plt.subplots(1, 2, figsize=(18,8))
titanic['survived'].value_counts().plot.pie(autopct='%1.1f%%',shadow=True, explode=[0, 0.05])

 

 

subplots이 반환하는 값이 두개가 있는데 figure와 axis입니다. 이를 f, ax 변수로 삼아보겠습니다.

 

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

 

 

2개가 나옵니다. 즉 그림을 2개 그려달라고 했으니 2개에 대한 속성을 갖고있습니다.

 

pie 그래프를 앞에 두고 싶은 경우 axis를 설정해주면 되는데요.

 

f, ax = plt.subplots(1, 2, figsize=(18,8))
titanic['survived'].value_counts().plot.pie(ax=ax[0], autopct='%1.1f%%',shadow=True, explode=[0, 0.05])

 

ax=ax[0]으로 설정해주니 pie plot이 앞에 나오는 것을 확인할 수 있습니다.

 

두 번째 plot에는 seaborn이 제공하는 countplot()을 가지고 survived를 그릴겁니다.

f, ax = plt.subplots(1, 2, figsize=(18,8))
titanic['survived'].value_counts().plot.pie(ax=ax[0], autopct='%1.1f%%',shadow=True, explode=[0, 0.05])

ax[0].set_title('Pie plot - survived')
ax[0].set_ylabel('') #왼쪽에 survived 글씨를 null처리

sns.countplot(x='survived', data=titanic, ax=ax[1])
ax[1].set_title('count plot - survived')

plt.show()

 

이제 성별에 따른 생존상황도 살펴보겠습니다.

 

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

#pie 대신 countplot을 앞에 두고
sns.countplot(x='sex', data=titanic, ax=ax[0])
ax[0].set_title('Count of passengers of sex')
ax[0].set_ylabel('') 

#hue='survived'옵션을 추가해보겠습니다. 즉, 0,1로 나누겠다는 거죠
sns.countplot(x='sex', data=titanic, hue='survived', ax=ax[1])
ax[1].set_title('Sex : survived')

 

위 결과를 보면 남성이 여성의 2배 가까운 수치를 보이나, 생존율은 여성의 생존율이 더 높습니다.
남성의 1/5만 살아남고 여성은 3/4가 살아남았습니다.

 

crosstab을 통해 두 특징을 표로 볼 수 있습니다.

pd.crosstab(titanic['pclass'], titanic['survived'])

 

합계도 같이 보기위해 margins 옵션을 주겠습니다.

pd.crosstab(titanic['pclass'], titanic['survived'], margins=True)

pclass는 등실을 나타내는데, 1등실은 절반이상 살아남았고, 2등실은 절반이 살았고, 3등실은 가장 많은 사람들이 있었으나 거의 다 사망했습니다.
그럼 1등실에 여성이 많이 타고 있었을까요?

seaborn의 FacetGrid라는 그래프함수를 사용해보겠습니다.

 

grid = sns.FacetGrid(titanic, row='pclass', col='sex', height=4, aspect=2)
grid.map(plt.hist, 'age', alpha=0.8, bins=20)
#histogram을 넣겠습니다. 무엇을 컬럼기준으로? age를 기준으로,
#alpha는 투명도, bins는 막대갯수입니다. 즉 나누는 갯수죠.

grid.add_legend();
#범례를 추가합니다.

 

titanic 데이터를 row를 pclass로 column을 sex로해서 분류해서 보고, 그래프는 histogram으로 age를 나누어서 mapping 시키겠다는 코드였습니다. 익숙하지 않지만 꽤 유용하니 자주 써봐야 할 것 같습니다.

 

 

3등실에 남성이 많았다는 걸 알 수 있네요 특히 20대 남성이 많았습니다.

이제 나이별 승객현황도 살표보겠습니다.

이번에는 FacetGrid 말고 plotly expcess라는 모듈의 histogram을 사용해보겠습니다.

 

import plotly.express as px

fig = px.histogram(titanic, x='age')
fig.show()

 

블로그에선 이미지만 가져오지만, 프로그램에서 마우스를 올렸을 때 다음과 같이 x, y 수치를 표현해주는 좋은 기능입니다.

 

이번에는 등실별 생존률을 연령별로 관찰해보겠습니다.

grid = sns.FacetGrid(titanic, row='pclass', col='survived', height=4, aspect=2);
grid.map(plt.hist, 'age', alpha=0.8, bins=20);
grid.add_legend();

 

확실히 선실 등급이 높으면 생존률이 높은 듯 합니다.

 

이번에는 나이를 정해서 구간별로 나눠볼까 합니다.
pandas에 cut()함수가 있습니다. 구간별로 label을 정할 수 있는 함수인데요,

 

titanic['age_cat'] = pd.cut(titanic['age'], bins=[0, 7, 15, 30, 60, 100],#bins는 사용자가 임의로 지정하는 값입니다.
               include_lowest=True,
               labels=['baby', 'teen', 'young', 'adult', 'old']) #이 bins에 label을 붙여보겠습니다. 0세부터 6세까지가 baby입니다.
titanic.head() #age caterogy가 생긴것을 확인할 수 있습니다.

 

cut() 함수에 bins 옵션으로 구간을 나누고 labels로 라벨을 붙여줍니다. include_lowest로 0세부터 6세까지 baby로 나누었습니다. 이걸 age category라고 하는 age_cat 컬럼을 새로 만들어 담아보았습니다.

 

 

pclass 별로, age_cat 별로, sex 별로 확인해보겠습니다.

 

plt.figure(figsize=(14,6))
plt.subplot(131) #1행3열중에 첫번째 그래프라는 뜻입니다.
sns.barplot(x='pclass', y='survived', data=titanic) 
#pclass와 survived에 대한 그래프를 bar로 그려라라는 명령입니다.

plt.subplot(132)
sns.barplot(x='age_cat', y='survived', data=titanic)

plt.subplot(133)
sns.barplot(x='sex', y='survived', data=titanic)

그렇다면, 1등실이고 어리고 여성일수록 생존하기 유리했을까?

 

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14,6))

women = titanic[titanic['sex']=='female']
men = titanic[titanic['sex']=='male']

ax = sns.distplot(women[women['survived']==1]['age'], bins=20, label='survived', ax=axes[0], kde=False) 
ax = sns.distplot(women[women['survived']==0]['age'], bins=40, label='not survived', ax=axes[0], kde=False)

#kde=False는 밀도함수 그래프가 나오는걸 안그린다는 뜻이다.

ax.legend(); ax.set_title('Female')

ax = sns.distplot(men[men['survived']==1]['age'], bins=18, label='survived', ax=axes[1], kde=False) 
ax = sns.distplot(men[men['survived']==0]['age'], bins=40, label='not survived', ax=axes[1], kde=False)

ax.legend(); ax.set_title('Male')

 

 

그리고 titanic에서 특이한 데이터로 사회적 신분이 적힌 데이터가 있습니다. 

 

for idx, dataset in titanic.iterrows():
    print(dataset['name'])

 

이름, 사회적신분. 성 패턴이므로 사회적신분만 따로 뺄 수 있는 방법을 찾겠습니다.
이럴 때 쓰는 것이 regular expression이고, re라고 하는 모듈입니다.

 

import re

for idx, dataset in titanic.iterrows():
    temp = dataset['name']
    print(re.search('\,\s\w+(\s\w+)?\.', temp).group())

 

글자뒤에 ',' 가 나오고 (\,) 한 칸을 비우고 (\s) 어떤글자들이 연속으로 나오다가 (\w+) 또 한 칸을 띄우고 (\s) 글자들이 연속으로 나올(\w+) 수도 있고 아닐 수도 있고(()?) 마지막에 '.' 으로 끝난다 (\.). 이 대상은 temp가 될 것이고, 거기서 뽑아서 글자로 만들어보겠습니다 (group())

 

이제 얘네들을 리스트에 넣어보겠습니다.

 

import re

title = []
for idx, dataset in titanic.iterrows():
    temp = dataset['name']
    title.append(re.search('\,\s\w+(\s\w+)?\.', temp).group()[2:-1]) #[2:-1]이 있는 이유는 두번째 글자부터 제일 마지막 전까지로 자르려고 하기 때문 즉 , .을 지우려고 하는 것
    print(re.search('\,\s\w+(\s\w+)?\.', temp).group()[2:-1])

title

 

print()의 결과

 

title의 결과

 

이제 title을 titanic에 column으로 채워넣어보겠습니다.

 

titanic['title'] = title
titanic.head()

 

그런데 title을 좀 더 단순화 시켜서, 귀족여성, 귀족남성, 평민여성, 평민남성으로 귀족과 성별에 따라 나눠보겠습니다.

우선 title과 sex의 crosstab을 통해 각 title이 여성에게 붙여지는 것인지, 남성에게 붙여지는 것인지 확인해보겠습니다.

pd.crosstab(titanic['title'], titanic['sex'])

 

위 결과가 다 나왔는지 확인하기 위해 unique() 함수를 써서 확인해보겠습니다.

 

titanic['title'].unique()

 

 

여기서 Mlle는 Miss의 옛날말이라고 하니 바꾸겠습니다. Ms도 Miss와 같은 것이니 바꾸고, Mme도 Mrs와 같은 것이라고 하니 바꾸겠습니다.

Rare_f가 귀족 여성, Rare_m가 귀족남성입니다.

titanic['title'] = titanic['title'].replace('Mlle', 'Miss')
titanic['title'] = titanic['title'].replace('Ms', 'Miss')
titanic['title'] = titanic['title'].replace('Mme', 'Mrs')

Rare_f = ['Dona', 'Lady', 'the Countess'] #귀족 여성
Rare_m = ['Capt', 'Col', 'Don', 'Major', 'Rev', 'Sir', 'Dr', 'Master', 'Jonkheer'] #귀족 남성

 

이제 Rare_f와 Rare_m으로 바꿀겁니다. for문과 replace를 사용해 간단하게 바꿀 수 있습니다.

for each in Rare_f:
    titanic['title'] = titanic['title'].replace(each, 'Rare_f')

for each in Rare_m:
    titanic['title'] = titanic['title'].replace(each, 'Rare_m')

 

unique 검사를 통해 다 바뀌었는지 확인합니다.

titanic['title'].unique()

 

이제 각 title별 survived를 합쳐보고싶습니다. 우선 title과 survived 컬럼을 동시에 봐보겠습니다.

 

titanic[['title', 'survived']]

 

 

groupby()후 mean()을 사용해 평균값을 확인해보겠습니다. 지금까지 이걸 확인하기 위한 title 묶음이었습니다 ㅎㅎ

titanic[['title', 'survived']].groupby('title').mean()

 

생존율을 보면, 평민남성<귀족남성<평민여성<귀족여성 순서입니다. 즉 귀족이라고 생존율이 높은건 아니었습니다.

이렇게 데이터의 경향성을 대략적으로 확인해보았습니다. 이제 머신러닝을 해보겠습니다.


*. 새로배운 모듈/함수 목록

 

value_counts()

plt.subplots()

sns.countplot()

pd.crosstab()

sns.FacetGrid()

plotly.express

pd.cut()

sns.barplot()

sns.distplot()

728x90

'머신러닝' 카테고리의 다른 글

kNN  (0) 2024.05.22
타이타닉 생존자 예측_머신러닝  (0) 2024.02.26
Decision Tree를 사용한 Iris 분류_데이터 나누기  (0) 2024.02.23
Decision Tree를 사용한 Iris 분류_과적합  (0) 2024.02.22
Entropy와 Gini 계수  (0) 2024.02.21