codestates / codestates/ds-blog
[윤현태] day5 타이타닉 데이터 분석
- 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())
```

결과가 나오긴 나왔다. 하지만 이 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은 제목을 알려주기 위해 사용 된다. 이렇게 해서 나온 최종적인 결과물이다.

아무래도 격자무늬가 있으면 조금더 직관적으로 데이터를 살펴 볼 수 있기 때문에, 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 역시 두배가 늘어나 손으로 타자치기 번거롭기 때문에, 반복문을 사용해 만들어준다. 그렇게 하면 아래와 같은 결과값이 나온다.

### **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()
```

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

아주 이상한 그래프가 되어버렸다! 이유를 보니, 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()
```

데이터 평가:한눈에 봐도 Class 1의 생존율이 가장 높았고, Class 3 의 생존율이 가장 낮았다. Class1 이 비싼 값을 하는 만큼 안전도 보장해준다는... 그런 데이터가 아닐까 조심스럽게 추측해본다.
마치며.
이 결과가 있기 까지 거의 5시간 이상을 고민 한 것 같은데, 그래도 겨우겨우 끝낼 수 있어서 다행이다. 그래도 이 것을 하면서 많은 고민들과 읽어본 정보들이 나중에도 굉장히 유용하게 써먹을 수 있을 것 같다.
~~그리고 남자라면... 한번쯤은 titanic 호를 타는 것을 고려해봐야 할 지도 모른다~~
#자가평가#
처음으로 남들에게 알려주는 글을 써보았는데, 마음만 앞서서 너무 두서 없고, 정신 없는 글이 되어버린 것 같다. 유명한 블로거들은 어떻게 정리를 하는지 보고 더 가다듬어야겠다는 생각이 들었다. 그래도 고민하던 문제를 해결한 부분은 굉장히 기뻤다
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.