캐글에서 가장 유명한 주제중 하나인 타이타닉 탑승자 생존률 구하기 데이터 분석을
캐글 코리아의 타이타닉 튜토리얼을 참고 하여 진행해보았다.
총 4가지 부분으로 나눴으며 순서는 아래와 같다.
2) EDA (Exploratory data analysis)
In [49]:
import numpy as np
import pandas as pd
from pandas import Series
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn')
sns.set(font_scale=2.5)
In [50]:
import plotly.offline as py
py.init_notebook_mode(connected=True)
import plotly.graph_objs as go
import plotly.tools as tls
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
In [51]:
df_train = pd.read_csv('../data/train.csv')
df_test = pd.read_csv('../data/test.csv')
# 가족수 컬럼 추가
df_train['FamilySize'] = df_train['SibSp'] + df_train['Parch'] +1
df_test['FamilySize'] = df_test['SibSp'] + df_test['Parch'] +1
# Fare(요금) log 함수 취하기
df_train['Fare']= df_train['Fare'].map(lambda i : np.log(i) if i > 0 else 0)
df_test['Fare']= df_test['Fare'].map(lambda i : np.log(i) if i > 0 else 0)
3. Feature engineering
- dataset에 존재하는 null data 처리, train, test 데이터 모두 적용
3.1 Fill Null
3.1.1 Fill Null in Age using title
- Age에는 null data가 177개가 존재함. 이를 해결하기 위해 title과 statistics를 사용
- 영어권에서 title은 Mis, Mrr, Mrs 와 같은 title이 존재.
- pandas series에 str method, extract method 활용하여 Initial Column 생성
In [52]:
df_train['Initial'] = df_train.Name.str.extract('([A-Za-z]+)\.') ## 직함, 성 추출
df_test['Initial'] = df_test.Name.str.extract('([A-Za-z]+)\.') ## 직함, 성 추출
In [53]:
df_train['Initial']
Out[53]:
- pandas의 crosstab을 이용해 추출한 Initial과 Sex간의 count를 살펴보자
In [54]:
pd.crosstab(df_train['Initial'], df_train['Sex']).T.style.background_gradient(cmap='summer_r')
Out[54]:
- 위 테이블을 참고 하여 남자, 여자가 쓰는 Initial을 구분
- replace 메소드를 사용하면, 특정 데이터 값을 원하는 값으로 치환한다.
In [55]:
df_train['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don', 'Dona'],
['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr', 'Mr'],inplace=True)
df_test['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don', 'Dona'],
['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr', 'Mr'],inplace=True)
In [56]:
df_train.groupby('Initial').mean()
Out[56]:
- 여성과 관련있는 Miss, Mr, Mrs가 생존률이 높은 것으로 확인되었다.
In [57]:
df_train.groupby('Initial')['Survived'].mean().plot.bar()
Out[57]:
In [58]:
df_train.groupby('Initial').mean()
Out[58]:
- Age의 평균을 이용하여 Null Value를 채운다.
- pandas dataframe : boolean array를 이용하여 indexing 한다.
In [59]:
# loc + boolean + column 을 사용하여 값을 치환
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Mr'),'Age'] = 33
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Mrs'),'Age'] = 36
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Master'),'Age'] = 5
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Miss'),'Age'] = 22
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Other'),'Age'] = 46
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Mr'),'Age'] = 33
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Mrs'),'Age'] = 36
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Master'),'Age'] = 5
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Miss'),'Age'] = 22
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Other'),'Age'] = 46
3.1.2 Fill Null in Embarked
In [60]:
print('Embarked has ', sum(df_train['Embarked'].isnull()), 'Null Values')
- Embarked 변수에 Null 값은 2개 존재한다.
- dataframe의 fillna method를 이용하여 가장 많은 탑승객이었던 S를 채워준다.
In [61]:
df_train['Embarked'].fillna('S', inplace=True)
3.2 Change Age(continuous to categorical)
- Age는 연속 변수. 몇개의 group으로 나누어 category화 시켜준다.
- categorial을 진행하면 information loss가 생길 수도 있음을 간과해선 안된다.
- 방법 : dataframe의 indexing 방법인 loc 사용, apply 함수 사용
In [62]:
# 나이는 10살 간격으로 나눈다.
df_train['Age_cat'] = 0
df_train.loc[df_train['Age'] < 10, 'Age_cat'] = 0
df_train.loc[(10 <= df_train['Age']) & (df_train['Age'] < 20), 'Age_cat'] = 1
df_train.loc[(20 <= df_train['Age']) & (df_train['Age'] < 30), 'Age_cat'] = 2
df_train.loc[(30 <= df_train['Age']) & (df_train['Age'] < 40), 'Age_cat'] = 3
df_train.loc[(40 <= df_train['Age']) & (df_train['Age'] < 50), 'Age_cat'] = 4
df_train.loc[(50 <= df_train['Age']) & (df_train['Age'] < 60), 'Age_cat'] = 5
df_train.loc[(60 <= df_train['Age']) & (df_train['Age'] < 70), 'Age_cat'] = 6
df_train.loc[70 <= df_train['Age'] , 'Age_cat'] = 7
df_test['Age_cat'] = 0
df_test.loc[df_test['Age'] < 10, 'Age_cat'] = 0
df_test.loc[(10 <= df_test['Age']) & (df_test['Age'] < 20), 'Age_cat'] = 1
df_test.loc[(20 <= df_test['Age']) & (df_test['Age'] < 30), 'Age_cat'] = 2
df_test.loc[(30 <= df_test['Age']) & (df_test['Age'] < 40), 'Age_cat'] = 3
df_test.loc[(40 <= df_test['Age']) & (df_test['Age'] < 50), 'Age_cat'] = 4
df_test.loc[(50 <= df_test['Age']) & (df_test['Age'] < 60), 'Age_cat'] = 5
df_test.loc[(60 <= df_test['Age']) & (df_test['Age'] < 70), 'Age_cat'] = 6
df_test.loc[70 <= df_test['Age'] , 'Age_cat'] = 7
In [63]:
# 두번째 방법 apply 메소드 활용
def category_age(x):
if x < 10:
return 0
elif x < 20:
return 1
elif x < 30:
return 2
elif x < 40:
return 3
elif x < 50:
return 4
elif x < 60:
return 5
elif x < 70:
return 6
else:
return 7
df_train['Age_cat_2'] = df_train['Age'].apply(category_age)
In [64]:
# 두가지 방법이 같은 결과가 나왔는지 확인해본다.
# Series 간 boolean 비교 후 all() 메소드 사용.
print ( '1번 방법, 2번 방법 둘다 같은 결과면 True -> ', (df_train['Age_cat'] == df_train['Age_cat_2']).all())
In [65]:
# 위의 두가지 방법이 동일한 것임을 확인하였으므로, 원래 있는 Age 컬럼과 Age_cat2컬럼을 삭제
df_train.drop(['Age', 'Age_cat_2'], axis=1, inplace=True)
df_test.drop(['Age'], axis=1, inplace=True)
3.3 Change Initial, Embarked and Sex (String to Numeric)
- 현재 Initial은 Mr, Mrs, Miss, Master, Other 총 5개로 이루어짐.
- 텍스트 카테고리로 표현된 것을 수치화
- map method 사용
- 사전순서대로 매핑
In [66]:
df_train['Initial'] = df_train['Initial'].map({'Master': 0, 'Miss': 1, 'Mr': 2, 'Mrs': 3, 'Other': 4})
df_test['Initial'] = df_test['Initial'].map({'Master': 0, 'Miss': 1, 'Mr': 2, 'Mrs': 3, 'Other': 4})
- C, Q, S로 이루어진 Embarked 변수도 수치화 표현
In [67]:
# 어떤 값들이 있는지 확인
df_train['Embarked'].unique()
Out[67]:
In [68]:
df_train['Embarked'].value_counts()
Out[68]:
In [69]:
df_train['Embarked'] = df_train['Embarked'].map({'C': 0, 'Q': 1, 'S': 2})
df_test['Embarked'] = df_test['Embarked'].map({'C': 0, 'Q': 1, 'S': 2})
In [70]:
# isnull(), null이 하나라도 있으면 True, 없으면 False
df_train['Embarked'].isnull().any()
Out[70]:
In [71]:
# 성별도 수치화 표현
df_train['Sex'] = df_train['Sex'].map({'female': 0, 'male': 1})
df_test['Sex'] = df_test['Sex'].map({'female': 0, 'male': 1})
- 각 feature 간의 상관관계
- 두 변수간의 Pearson correlation 을 구하면 (-1, 1) 사이의 값을 얻을 수 있음.
- 1로 갈 수록 음의 상관관계, 1로 갈수록 양의 상관관계를 의미. 0은 상관관계가 없다는 것을 의미.
- heatmap plot : 여러 변수의 상관관계를 matrix로 표현
- dataframe의 corr() 메소드와 seaborn으로 표현
In [72]:
df_train['Sex']
Out[72]:
In [75]:
heatmap_data = df_train[['Survived', 'Pclass', 'Sex', 'Fare', 'Embarked', 'FamilySize', 'Initial', 'Age_cat']]
colormap = plt.cm.RdBu
plt.figure(figsize=(14,12))
plt.title('Pearson Correlation of Features', y=1.05, size=15)
sns.heatmap(heatmap_data.astype(float).corr(), linewidths=0.1, vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True, annot_kws={"size": 16})
del heatmap_data
- 이전에 EDA에서 살펴봤듯이 Sex, Pclass는 Survived와 어느정도 상관관계가 있다.
- 하지만 여기서 얻을수 있는 정보는 서로 강한 상관관계를 가지는 feature는 없다는 것이다.
- 이것은 모델을 학습시킬 때, 불필요한 (redundant, superfluous) feature가 없다는 것을 의미한다.
3.4 One-hot encoding on Initial and Embarked
- 모델 학습하기 이전에 데이터 전처리 (data preprocessing)을 진행한다.
- 수치화 시킨 카테고리 데이터를 그대로 넣어도 되지만, 모델의 성능을 높이기 위해 one-hot encoding을 해준다.
- 수치화는 간단히 Master=0, Miss=1, Mr=2, Mrs=3, Other=4로 매핑해주는 것을 의미한다.
- One-Hot encoding은 위 카테고리를 아래와 같이 (0,1) 로 이루어진 5차원 벡터로 나타내는 것을 의미한다.
- pandas의 get_dummies를 사용하면 쉽게 해결 가능하다.
- 카테고리 갯수만큼 새로운 컬럼이 생긴다.
In [77]:
# Initial을 prefix로 두어서 구분을 쉽게 할수 있도록 한다.
df_train = pd.get_dummies(df_train, columns=['Initial'], prefix='Initial')
df_test = pd.get_dummies(df_test, columns=['Initial'], prefix='Initial')
In [79]:
df_train.head()
Out[79]:
In [80]:
# Embarked도 적용
df_train = pd.get_dummies(df_train, columns=['Embarked'], prefix='Embarked')
df_test = pd.get_dummies(df_test, columns=['Embarked'], prefix='Embarked')
In [81]:
df_train.head()
Out[81]:
- 이 방법외에 sklearn로 Labelencoder + OneHotencoder를 이용해도 된다.
3.5 Drop columns
In [82]:
# 필요한 컬럼만 남기고 삭제
df_train.drop(['PassengerId', 'Name','SibSp','Parch','Ticket','Cabin'], axis=1, inplace=True)
df_test.drop(['PassengerId', 'Name','SibSp','Parch','Ticket','Cabin'], axis=1, inplace=True)
In [83]:
df_train.head()
Out[83]:
In [86]:
df_test.head()
# train의 Survived feature(target class)만 제외하면 나머지 컬럼은 동일하다.
Out[86]:
'Competition > Kaggle' 카테고리의 다른 글
[kaggle] Titanic: Machine Learning from Disaster (4) (0) | 2020.09.04 |
---|---|
[kaggle] Titanic: Machine Learning from Disaster (2) (0) | 2020.09.01 |
[kaggle] Titanic: Machine Learning from Disaster (1) (0) | 2020.08.31 |