codestates / codestates/ds-blog

[윤현태] day5 타이타닉 데이터 분석

Open
#54 9 comments 3 reactions 0 assignees View on GitHub
tech
Dominant language
No language data
Stars
2
Forks
4
PR merge metrics
No merged PRs in 30d

Description

### **타이타닉에서 살아남은 사람들의 생존율을 구하려면 어떻게 할까?**

생존율은 간단하게 생각해보면 사람들이 얼마나 살아남았는지의 정도를 표현해주는 명확한 기준이 된다. 그러나 많은 키워드들이 여전히 빠져있다. 특히 데이터 사이언스에서는 '어떤' 사람들인지에 따라서 굉장히 많은 이야기를 풀어나갈 수 있을 것이다.

어떤 사람들인지 판단을 하기 위해서는 여러가지 정보가 필요하다. 그걸 알아보기 전에, 먼저 정보를 분석하기 위해 여러가지 도구들을 준비한다.
```python
import numpy as np
from string import ascii_letters
import pandas as pd
import matplotlib.pyplot as plt
```
그리고 오늘의 주인공, 타이타닉 데이터를 받아보자.
```python
df = pd.read_csv('https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv')
```
그리고 나서, 생각해보자. 우리가 어떤 사람인지 판단을 하기 위한 여러가지 정보가 필요하다. 그럼 이 타이타닉에는 어떤 정보가 들어있을까? 그건 데이터가 어떤 값들을 저장하고 있는지 먼저 살펴 보는 것이 될 것이다.
```python
print(df.columns)
```
Index(['Survived', 'Pclass', 'Name', 'Sex', 'Age', 'Siblings/Spouses Aboard','Parents/Children Aboard', 'Fare'],dtype='object')

찬찬히 살펴보면, 먼저 생존여부, 어떤 클래스에 탔는지, 이름, 성별, 나이, 형제자매들과 배우자가 얼마나 있는지, 자식과 부모가 얼마나 있는지, 그리고 요금은 얼마나 냈는지에 대한 정보가 들어있다는 것을 알 수 있다.

자, 그러면 생존율을 구하려면 어떻게 해야할까?
저 위에 정보가 없다는 것은, 결국 내가 새로 만들어야 된다는 말과 다름이 없다. 그리고 우리는 여기서 고민을 다시 한 번 하게 된다. '어떤' 사람의 생존율을 구할 것인가?
먼저, 가장 간단한 해답이 떠오른다. 나이에 따른 생존율을 만들어 보는게 어떨까? 바로 한번 실행해보자
``` python
df_d=df[df['Survived']==0]
df_a=df[df['Survived']==1]
df_d=df_d[['Age']]
df_a=df_a[['Age']]
```
먼저 살아남은 사람들, 그리고 안타깝게 운명을 달리한 사람들로 나눈다. 그리고 지금 내가 가장 관심있는, '나이' 부분만 살려보자. 최종적으로 df_d 와 df_a는 성별, 클래스, 요금등에 상관 없이, 모두다 '나이'에 대한 정보만을 가지고 있다. 하지만 이렇게 보기에는, 사실상 그냥 데이터를 맨눈으로 들여보는 것과 다름이 없다. 이 것이 데이터로서 기능을 하려면 분류를 해야하는데, 나이대로 묶는 것이 가장 좋겠다는 생각이 들었다. 바로 실천해 보자.
```python
dfd_cut=df_d.groupby(pd.cut(df_d["Age"],np.arange(0,90,10),right=False)).count()
dfa_cut=df_a.groupby(pd.cut(df_a["Age"],np.arange(0,90,10),right=False)).count()
```
groupby 함수는 같은 부류의 데이터들로 나눠줄 때 아주 유용한 함수이다. 어떤 방식으로 나눌지는 내가 결정할 수 있는데, 나이는 비교적 연속적인 데이터이기 때문에 범위별로 나누는 것이 효과적이라고 생각했다. dataframe에서 cut 함수를 사용하면 내가 원하는 데이터 부분을 범위별로 분리할 수 있다. 그리고 마지막으로, 내가 알고 싶은 건 범위 내의 평균 값이나 가장 큰 값, 가장 작은 값도 아닌 몇 명인지를 알 고 싶기 때문에 데이터의 개수를 세주는 count() 함수를 사용한다. 그러면, 아래와 같은 결과가 나오게 된다.
Age
Age
[0, 10) 30
[10, 20) 80
[20, 30) 195
[30, 40) 116
[40, 50) 69
[50, 60) 31
[60, 70) 18
[70, 80) 6
Age
Age
[0, 10) 41
[10, 20) 48
[20, 30) 98
[30, 40) 83
[40, 50) 45
[50, 60) 20
[60, 70) 6
[70, 80) 0
AxesSubplot(0.125,0.125;0.775x0.755)

거의 다 되어 가고 있다! 이제 이것을 생존율로 바꿔줄 차례이다. 생존율은 간단하게 말하면 살아있는 사람의 수/모든 사람의 수 이니, 이 것을 토대로 공식을 만들어보자. 참고로 dfsr은 data frame survival rate의 앞글자를 따와서 만들었다.
```python
dfsr=dfa_cut/(dfd_cut+dfa_cut)*100
dfsr= dfsr.rename(columns = {'Age': 'Survival rate'}, inplace = False)
```

Survival rate
Age
[0, 10) 57.746479
[10, 20) 37.500000
[20, 30) 33.447099
[30, 40) 41.708543
[40, 50) 39.473684
[50, 60) 39.215686
[60, 70) 25.000000
[70, 80) 0.000000
AxesSubplot(0.125,0.125;0.775x0.755)

rename을 사용한 이유는, 내가 가공한 데이터는 생존율인데 Age로 표기되어 있기 때문에, 직관성을 높여주기 위해 바꿨다. 여기까지 했으면, 더이상 참을 수 없다. 빨리 결과를 보고 싶다! 바로 plot을 출력해보자.

```python
print(dfsr.plot())
```
![image](https://user-images.githubusercontent.com/70379885/92197326-638a1200-eeac-11ea-9fa8-3507f3d1e1e4.png)

결과가 나오긴 나왔다. 하지만 이 plot만 보면 미흡한 점이 굉장히 많다. 우선, 데이터 프레임에는 예를들어 [0,10)으로 명시되어 있지만, 실제로는 아무것도 포함되어 있지 않거나, 1,2,3,4 등 단순한 인덱스로만 표시되어 있게 된다. 그러니 먼저 index를 추가해 주자.
```python
index=["0-10","10-20","20-30","30-40","40-50","50-60","60-70","70-80"]
dfsr.index=index
```
Survival rate
0-10 57.746479
10-20 37.500000
20-30 33.447099
30-40 41.708543
40-50 39.473684
50-60 39.215686
60-70 25.000000
70-80 0.000000

전이랑 다른 건 인덱스의 텍스트 밖에 없는 것 같지만, plot에 대한 기능이 조금 더 풍부한 matplotlib를 사용해보면서 달라진 점을 확인해보자.

```python
plt.style.use(['seaborn'])
plt.plot(dfar)
plt.xlabel('Age')
plt.ylabel('Survival rate(%)')
plt.suptitle('Survival rate by Age')
plt.show()
```
xlabel은 x축이 무엇을 의미하는지, ylabel은 y축이 무엇을 의미하는지를 알려줄 때 사용하고, suptitle은 제목을 알려주기 위해 사용 된다. 이렇게 해서 나온 최종적인 결과물이다.
![image](https://user-images.githubusercontent.com/70379885/92200388-b10a7d00-eeb4-11ea-86fa-91fa14f5a3d7.png)

아무래도 격자무늬가 있으면 조금더 직관적으로 데이터를 살펴 볼 수 있기 때문에, sytle을 'seaborn'으로 변경해 주었다.

데이터에 대한 평가: 구조는 영유아가 우선인 것을 확실하게 알 수 있었다. 10-30 구간에서 갑자기 감소한 것은, 아무래도 titanic호에서 일하던 선원들의 나이대가 가장 많이 포함되어 있어서 그런게 아닐까 추측해 본다. 그리고 급박한 상황 속에서 몸을 가누기 힘든, 나이 많은 사람들의 생존율 역시 가면 갈 수록 떨어졌다는 것을 알 수 있었다.

### **Extra Q No.1 나이대를 조금 더 줄이면 어떻게 될까?**
```python
dfd_cut=df_d.groupby(pd.cut(df_d["Age"],np.arange(0,85,5),right=False)).count()
dfa_cut=df_a.groupby(pd.cut(df_a["Age"],np.arange(0,85,5),right=False)).count()
dfar=dfa_cut/(dfd_cut+dfa_cut)*100
dfar= dfar.rename(columns = {'Age': 'Survival rate'}, inplace = False)
print(dfar)
index=[]
for i in range(0,80,5):
string=str(i)+"-"+str(i+5)
index.append(string)
print(index)
dfar.index=index
print(dfar)
plt.style.use(['seaborn'])
plt.plot(dfar)
plt.xlabel('Age')
plt.ylabel('Survival rate(%)')
plt.suptitle('Survival rate by Age')
plt.show()
```
바뀐 부분은 range의 조정을 해주었다. 그리고 index 역시 두배가 늘어나 손으로 타자치기 번거롭기 때문에, 반복문을 사용해 만들어준다. 그렇게 하면 아래와 같은 결과값이 나온다.

![image](https://user-images.githubusercontent.com/70379885/92200885-06935980-eeb6-11ea-85e6-c8d466b5d002.png)

### **Extra Q No.2 여자와 남자의 생존율을 나눠서 비교하면 어떻게 될까?**
```python
import numpy as np
from string import ascii_letters
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv')
```
이 부분은 전과 동일하다. 하지만 여자와 남자에 대한 데이터를 따로따로 취급해야 하기 때문에 다음 부터는 모든 작업이 2배가 된다.
```python
#기본 데이터 나누기
df_d=df[df['Survived']==0]
df_a=df[df['Survived']==1]
# 남자 부분만 따로 생성
df_d_m=df_d[df_d['Sex']=='male']
df_a_m=df_a[df_a['Sex']=='male']
df_d_m=df_d_m[['Age']]
df_a_m=df_a_m[['Age']]
dfd_m_cut=df_d_m.groupby(pd.cut(df_d_m["Age"],np.arange(0,90,10),right=False)).count()
dfa_m_cut=df_a_m.groupby(pd.cut(df_a_m["Age"],np.arange(0,90,10),right=False)).count()
dfsr_m=dfa_m_cut/(dfd_m_cut+dfa_m_cut)*100
dfsr_m= dfsr_m.rename(columns = {'Age': 'Survival rate'}, inplace = False)

#여자 부분만 따로 생성
df_d_f=df_d[df_d['Sex']=='female']
df_a_f=df_a[df_a['Sex']=='female']
df_d_f=df_d_f[['Age']]
df_a_f=df_a_f[['Age']]
dfd_f_cut=df_d_f.groupby(pd.cut(df_d_f["Age"],np.arange(0,90,10),right=False)).count()
dfa_f_cut=df_a_f.groupby(pd.cut(df_a_f["Age"],np.arange(0,90,10),right=False)).count()
dfsr_f=dfa_f_cut/(dfd_f_cut+dfa_f_cut)*100
dfsr_f= dfsr_f.rename(columns = {'Age': 'Survival rate'}, inplace = False)

# 여자, 남자 인덱싱
index=[]
for i in range(0,80,10):
string=str(i)+"-"+str(i+10)
index.append(string)
dfsr_m.index=index
dfsr_f.index=index
```
이전에 했던 코드와 비교하면 거의 같다. 단지 남자와 여자의 경우를 나눴을 뿐이다. 이제 plot을 그릴 차례인데, 한 그래프에 두가지 데이터를 다 나오게 하고 싶으면 연속헤서 plot함수를 호출하면 된다. 마치 도화지에 그림을 그리고, 그 위에 덧칠하는 느낌이다. 그리고 line 이 두개이기 때문에, 구별을 할 수 있도록 legend에 신경을 썼다. 아직 완벽하게 이해를 못했지만, 컴포넌트들을 세분화 해서 쓰려면 subplots을 사용하는 것 같다.

```python
plt.style.use(['seaborn'])
fig,ax= plt.subplots()
# 순서는 상관 없지만 두개를 동시에 그리고 싶다면 이렇게 plot 함수를 두 번 호출하먄 된다.
ax.plot(dfar_f,"-r",label='Female')
ax.plot(dfar_m,"-b",label='Male')
# 그려진 plot의 line 이 각각 무엇을 뜻하는지 표현하는 legned를 표시해준다.
leg=ax.legend()

plt.xlabel('Age')
plt.ylabel('Survival rate(%)')
plt.suptitle('Survival rate by Age')
plt.show()
```
![image](https://user-images.githubusercontent.com/70379885/92205976-b0c4ae80-eec1-11ea-807b-c445d77ec7ae.png)

데이터 평가: 영유아 시기를 제외하고는 남자의 생존율이 압도적으로 낮다. 그 당시에도 아마 '여자와 아기 먼저'라는 통념에 따라 구조 활동이 이루어진 것 같다. 실제로 영화에서도 비슷한 장면이 연출되어서, 통계학적으로 정확한 고증이라고 할 수 있겠다.

### **Extra Q No.3 금액에 따른 생존율을 비교하면 어떻게 될까?**

이번에는 나이가 아닌 금액이다! 한번 확인해 볼까? 기본적인 것과 비슷하지만 'Age'를 'Fare'로 바꿔주면 문제 끝!

```python
import numpy as np
from string import ascii_letters
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv')
df_a=df[df['Survived']==1]
df_d=df[df['Survived']==0]
df_a=df_a[['Fare']]
df_d=df_d[['Fare']]
# 최고 금액 : 512 최소금액: 0
dfg_a=df_a.groupby(pd.cut(df_a['Fare'],np.arange(0,555,50),right=False)).count()
dfg_d=df_d.groupby(pd.cut(df_d['Fare'],np.arange(0,555,50),right=False)).count()

print(dfg_a)
print(dfg_d)
dfsr=dfg_a/(dfg_a+dfg_d)*100
dfsr= dfsr.rename(columns = {'Fare': 'Survival rate'}, inplace = False)

index=[]
for i in range(0,550,50):
string=str(i)+"-"+str(i+50)
index.append(string)
dfsr.index=index

print(dfsr)

plt.style.use(['seaborn'])
fig,ax= plt.subplots()
ax.plot(dfsr,"-r",label='Fare')
leg=ax.legend()
plt.xlabel('Fare')
plt.ylabel('Survival rate(%)')
plt.suptitle('Survival rate by Age')
plt.show()

```
![image](https://user-images.githubusercontent.com/70379885/92210451-b1614300-eec9-11ea-9bc3-b3bbb3dd870e.png)

아주 이상한 그래프가 되어버렸다! 이유를 보니, 300 이상 500이하까지의 가격을 지불한 사람은 없었다. 즉, outlier 인 512를 지불한 사람이 3명이 존재해서 이렇게 중간 부분이 사라지는 것이다. 그렇다면 구간을 다르게 설정해야 한다. 이 부분은 다음시간에 알아보도록 해야겠다.

### **Extra Q No.4 클래스별 생존율은 어떻게?**

```python
import numpy as np
from string import ascii_letters
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv')
#df[['Pclass']].head
# P class는 1,2,3 으로 이루어져 있다.

#데이터를 나누어 줍니다.
df_d=df[df['Survived']==0]
df_a=df[df['Survived']==1]
#클래스만 사용할 수 있도록 해줍니다.
df_d=df_d[['Pclass']]
df_a=df_a[['Pclass']]

#categorical data 이므로 단순히 key 값과 계산해줄 열을 전달해주기만 하면 됩니다.
dfg_d=df_d.groupby('Pclass')['Pclass'].count()
dfg_a=df_a.groupby('Pclass')['Pclass'].count()
dfsr=dfg_a/(dfg_d+dfg_a)

#categroical data는 pie chart 가 조금더 직관적으로 보일 것 같아서 선택해보았다.
tag=["Class 1","Class 2","Class 3"]
#autopct를 통해서 data 가 얼마의 지분을 차지하는지 표시해 준다.
plt.pie(dfsr, labels=tag, autopct='%0.1f%%')
plt.axis('equal')
# legend 를 해주지 않으면 어떤 색이 어떤 클래스를 대표하는지 알지 못한다.
plt.legend(tag)
plt.suptitle('Survival rate by Pclass')
plt.show()
```
![image](https://user-images.githubusercontent.com/70379885/92233605-88ea4080-eeeb-11ea-9a24-987831ed01ad.png)

데이터 평가:한눈에 봐도 Class 1의 생존율이 가장 높았고, Class 3 의 생존율이 가장 낮았다. Class1 이 비싼 값을 하는 만큼 안전도 보장해준다는... 그런 데이터가 아닐까 조심스럽게 추측해본다.

마치며.

이 결과가 있기 까지 거의 5시간 이상을 고민 한 것 같은데, 그래도 겨우겨우 끝낼 수 있어서 다행이다. 그래도 이 것을 하면서 많은 고민들과 읽어본 정보들이 나중에도 굉장히 유용하게 써먹을 수 있을 것 같다.
~~그리고 남자라면... 한번쯤은 titanic 호를 타는 것을 고려해봐야 할 지도 모른다~~

#자가평가#

처음으로 남들에게 알려주는 글을 써보았는데, 마음만 앞서서 너무 두서 없고, 정신 없는 글이 되어버린 것 같다. 유명한 블로거들은 어떻게 정리를 하는지 보고 더 가다듬어야겠다는 생각이 들었다. 그래도 고민하던 문제를 해결한 부분은 굉장히 기뻤다

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.