데이터분석의 일환으로 캐글코리아 유한님의 캐글 커널 필사를 따라 공부하기 시작했습니다.
간혹 글귀가 정형화되지 않은 점 유의해주시기 바랍니다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn')
sns.set(font_scale=2.5)
# 유한님 스타일, 일일히 graph의 font size를 지정할 필요없이 seaborn의 font_scale을 사용하면 편함.
import missingno as msno
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
from google.colab import drive
drive.mount('/gdrive')
앞으로 해나갈 프로세스
- 데이터셋 확인 - null data 확인
- 탐색적 데이터 분석(exploratory data analysis) - feature 들 간의 상관관계 확인
- feature engineering - one-hot encoding, class로 나누기, 구간으로 나누기, 텍스트 데이터 처리
- model 만들기 - sklearn
- 모델 학습 및 예측
- 모델 평가
df_train = pd.read_csv('/gdrive/My Drive/Colab Notebooks/Kaggle_필사/train.csv')
df_test = pd.read_csv('/gdrive/My Drive/Colab Notebooks/Kaggle_필사/test.csv')
df_train.head()
df_train.describe()
df_test.describe()
1.1Null Data check
for col in df_train.columns:
msg = 'column: {:>10}\t Percent of NaN value: {:.2f}%'.format(col, 100 * (df_train[col].isnull().sum() / df_train[col].shape[0]))
print(msg)
for col in df_test.columns:
msg = 'column:{:>10}\t Percent of NaN value: {:2f}%'.format(col, 100*(df_test[col].isnull().sum() / df_test[col].shape[0]))
print(msg)
-
train, test set에서 age(둘다 20%), cabin(둘다 약 80%), embarked(train만 0.22%) null data 존재
-
Mano 라는 라이브러리를 사용하면 null data의 존재를 더 쉽게 볼 수 있다.
msno.matrix(df=df_train.iloc[:,:], figsize=(8,8), color=(0.8,0.5,0.2))
msno.bar(df=df_train.iloc[:,:], figsize = (8,8), color=(0.8,0.5,0.2))
msno.bar(df=df_test.iloc[:,:], figsize = (8,8), color=(0.5,0.5,0.2))
1.2 target label 확인
- target label이 어떤 distribution을 가지고 있는 지 확인 필요
- 지금 같은 binary classification 문제의 경우에서 1과 0의 분포가 어떠나에 따라 모델의 평가 방법이 달라 질 수 있다.
f, ax = plt.subplots(1,2, figsize = (12,6))
df_train['Survived'].value_counts().plot.pie(explode=[0,0.1], autopct='%1.1f%%', ax=ax[0], shadow=True)
ax[0].set_title('Pie plot - Survived')
ax[0].set_ylabel('')
sns.countplot('Survived', data=df_train, ax=ax[1])
ax[1].set_title('Count plot - Survived')
plt.show()
- 죽은 사람이 많음
- 38.4%가 살아남음
- target label의 분포가 균일함. 불균일할 경우, 예를 들어, 100중 1이 99, 0이 1개인 경우에는 만약 모델이 모든 것을 1이라 해도 정확도가 99%가 나오게 됨. 0을 찾는 문제라면 이 모델은 원하는 결과를 줄 수 없음. 지금 문제에서는 그렇지 않으니 계속 진행
2. Exploratory data analysis
- 이제 본격적으로 데이터 분석 시작. 데이터는 매우 많다. 이 많은 데이터 안에 숨겨진 사실을 찾기 위해선 적절한 시각화 필요
- 시각화 라이브러리는 matplotlib, seaborn, plotly 등이 있음. 특정 목적에 맞는 소스코드를 정리해두어 필요할 때마다 참고하면 편함
2.1 Pclass
- Pclass는 ordinal, 서수형 데이터임. 카테고리이면서, 순서가 있는 데이터 타입
- 먼저 Pclass에 따른 생존률의 차이를 살펴보겠음. 엑셀의 피벗차트와 유사한 작업을 하게 되는데, pandas dataframe에서는 groupby를 사용하면 쉽게 할 수 있음. 또한 pivot이라는 메소드도 있음
- 'Pclass','Survived'를 가져온 후, pclass 로 묶습니다. 그러고 나면 각 pclass 마다 0,1이 count가 되는데, 이를 평균내면 각 pclass 별 생존률이 나옴
- 아래와 같이 count()를 하면, 각 class에 몇명이 있는 지 확인할 수 있으며, sum()을 하면, 216명 중 생존한 사람(survived=1)의 총합을 주게됨
Survived의 생존자는 1의 값을 가지므로 총합은 곧 생존자의 합
df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=True).count()
df_train[['Pclass','Survived']].groupby(['Pclass'], as_index=True).sum()
- pandas의 crosstab을 사용하면 좀 더 위 과정을 수월하게 볼 수 있음
pd.crosstab(df_train['Pclass'], df_train['Survived'], margins=True).style.background_gradient(cmap='summer_r')
- grouped 객체에 mean()을 하게 되면, 각 클래스별 생존율을 얻을 수 있음. class 1이면 아래와 같음
80/(80+136) = 0.63
df_train[['Pclass', 'Survived']].groupby(['Pclass'], as_index=True).mean().sort_values(by='Survived', ascending=False).plot.bar()
- 보다시피, Pclass가 좋을수록 생존률이 높은 것을 확인
- 좀더 보기 쉽게 그래프를 그려보겠다. seaborn의 countplot을 이용하면, 특정 label에 따른 개수를 확인해볼 수 있다.
y_position = 1.02
f, ax = plt.subplots(1,2,figsize=(12,6))
df_train['Pclass'].value_counts().plot.bar(color=['#CD7F32','#FFDF00','#D3D3D3'], ax=ax[0])
ax[0].set_title('Number of Passengers By Pclass', y=y_position)
ax[0].set_ylabel('Count')
sns.countplot('Pclass', hue='Survived',data=df_train, ax=ax[1])
ax[1].set_title('Pclass: Survived vs Dead', y=y_position)
plt.show()
- 클래스가 높을 수록, 생존확률이 높은걸 확인할 수 있다. pclass 1,2,3 순서대로 63%, 48%, 25%
- 우리는 생존에 pclass가 큰 영향을 미친다고 생각해볼수 있으며, 나중에 모델을 세울때 이 feature를 사용하는 것이 좋을 것이라 판단할 수 있음
2.2 Sex
- 이번에는 성별로 생존률이 어떻게 달라지는 지 확인해보겠습니다.
- 마찬가지로 pandas groupby와 seaborn countplot을 사용해서 시각화해봄
f, ax = plt.subplots(1,2,figsize=(18,8))
df_train[['Sex','Survived']].groupby(['Sex'], as_index=True).mean().plot.bar(ax=ax[0])
ax[0].set_title('Survived vs Sex')
sns.countplot('Sex',hue='Survived', data=df_train, ax=ax[1])
ax[1].set_title('Sex: Survived vs Dead')
plt.show()
df_train[['Sex', 'Survived']].groupby(['Sex'], as_index=False).mean().sort_values(by='Survived', ascending=False)
pd.crosstab(df_train['Sex'], df_train['Survived'], margins=True).style.background_gradient(cmap='summer_r')
- Pclass와 마찬가지로 Sex도 예측모델에 쓰일 중요한 feature 임을 알 수 있음
2.3 Both Sex and Pclass
- 이번에는 Sex, Pclass 두가지에 관하여 생존이 어떻게 달라지는 지 확인해봅시다
- seaborn의 factorplot을 이용하면, 손쉽게 3개의 차원으로 이루어진 그래프를 그릴 수 있음
sns.factorplot('Pclass','Survived', hue='Sex', data=df_train, size=6, aspect=1.5)
- 모든 클래스에서 female이 살 확률이 male보다 높은 걸 알 수 있음
- 또한, 남자, 여자 상관없이 클래스가 높을 수록 살 확률 높음
- 위 그래프는 hue대신 column으로 하면 아래와 같아짐
sns.factorplot(x='Sex', y='Survived', col='Pclass', data=df_train, satureation=.5, size=5, aspect=1)
2.4 Age
- 이번에는 Age feature를 살펴봅시다
print('the oldest passensger: {:.1f} years'.format(df_train['Age'].max()))
print('The yongest Passenger: {:.1f} years'.format(df_train['Age'].min()))
print("Aveage Passenger's Age: {:.1f} years".format(df_train['Age'].mean()))
- 생존에 따른 Age의 histogram을 그려보겠습니다
fig, ax = plt.subplots(1,1,figsize=(9,5))
sns.kdeplot(df_train[df_train['Survived']==1]['Age'], ax=ax)
sns.kdeplot(df_train[df_train['Survived']==0]['Age'], ax=ax)
plt.legend(['Survived==1', 'Survived==0'])
plt.show()
#Age distribution withing classes
plt.figure(figsize=(8,6))
df_train['Age'][df_train['Pclass']==1].plot(kind='kde')
df_train['Age'][df_train['Pclass']==2].plot(kind='kde')
df_train['Age'][df_train['Pclass']==3].plot(kind='kde')
plt.xlabel('Age')
plt.title('Age Distribution within classes')
plt.legend(['1st Class','2nd Class', '3rd class'])
- Class가 높을 수록 나이 많은 사람의 비중이 커짐
- 나이대가 변하면서 생존률이 어떻게 되는 지 보려고 함
- 나이범위를 점점 넓혀가며, 생존률이 어떻게 되는지 한번 봅시다.
cummulate_survival_ratio=[]
for i in range(1, 80):
cummulate_survival_ratio.append(df_train[df_train['Age'] < i]['Survived'].sum() / len(df_train[df_train['Age'] < i]['Survived']))
plt.figure(figsize=(7,7))
plt.plot(cummulate_survival_ratio)
plt.title('Survival rate change depending on range of Age', y=1.02)
plt.ylabel('Survival rate')
plt.xlabel('Range of Age(0~x)')
plt.show()
- 보시다시피, 나이가 어릴 수록 생존률이 확실히 높음
- 우리는 이 나이가 중요한 feature로 쓰일 수 있음을 확인
2.5 Pclass, Sex, Age
- 지금까지 본 Sex, Pclass, Age, Survived 모두에 대해서 보고 싶습니다. 이를 쉽게 그려주는 것이 seaborn의 violinplot입니다.
- x축은 우리가 나눠서 보고 싶어하는 case(여기선, Pclass, Sex)를 나타내고, y축은 보고 싶어하는 distribution (Age)입니다.
f,ax = plt.subplots(1,2,figsize=(18,8))
sns.violinplot('Pclass','Age', hue='Survived', data=df_train, scale='count', split=True, ax = ax[0])
ax[0].set_title('Pclass and Age vs Survived')
ax[0].set_yticks(range(0,110,10))
sns.violinplot('Sex',"Age", hue="Survived", data=df_train,scale='count', split=True, ax=ax[1])
ax[1].set_title('sex and Age vs Survived')
ax[1].set_yticks(range(0,110,10))
plt.show()
- 왼쪽그림은 클래스 별로 연령의 분포가 어떻게 다른지 거기에 생존여부에 따라 구분한 그래프
- 오른쪽그림도 마찬가지 성별, 생존에 따른 분포가 어떻게 다른지 보여주는 그래프
- 생존만 봤을 때, 모든 클래스에서 나이가 어릴 수록 생존을 많이 한것을 볼 수 있다.
- 오른쪽그림에서 보면, 명확히 여자가 생존을 많이 한 것을 볼 수 있다.
- 여성과 아이를 먼저 챙긴것을 볼 수 있다
2.6 Embarked
- Embarked는 탑승한 항구를 나타냄
- 위에서 해왔던 것과 비슷하게 탑승한 곳에 따른 생존률을 보겠다
f, ax = plt.subplots(1, 1, figsize=(7, 7))
df_train[['Embarked', 'Survived']].groupby(['Embarked'], as_index=True).mean().sort_values(by='Survived', ascending=False).plot.bar(ax=ax)
- 보시다시피 조금의 차이는 있지만, 생존률은 좀 비슷한거 같다. 그래도 C가 제일 높다
- 모델에 얼마나 큰영향을 미칠지는 모르겠지만, 그래도 사용
- 사실, 모델을 만들고 나면 우리가 사용한 feature들이 얼마나 중요한 역할을 했는지 확인해 볼 수 있다. 이는 추후에 모델을 만들고 난 다음에 살펴볼 예정
- 다른 feature로 split하여 한번 살펴보자
f, ax = plt.subplots(2,2,figsize=(20,15))
sns.countplot('Embarked', data=df_train, ax=ax[0,0])
ax[0,0].set_title('(1) No. of passengers Boarded')
sns.countplot('Embarked', hue='Sex', data=df_train, ax=ax[0,1])
ax[0,1].set_title('(2) Male-Female split for embarked')
sns.countplot('Embarked', hue='Survived', data=df_train, ax=ax[1,0])
ax[1,0].set_title('(3) Embarked vs Survived')
sns.countplot('Embarked', hue='Pclass', data=df_train, ax=ax[1,1])
ax[1,1].set_title('(4) Embarked vs Pclass')
plt.subplots_adjust(wspace=0.2, hspace=0.5)
plt.show()
- Figure(1) - 전체적으로 봤을때, S에서 가장 많은 사람이 탑승했다.
- Figure(2) - C와 Q는 남녀의 비율이 비슷하고, S는 남자가 더 많다
- Figure(3) - 생존확률이 S 경우 많이 낮다
- Figure(4) - Class로 Split해서 보니, C가 생존확률이 높은건 클래스가 높은 사람이 많이 타서 그렇다. S는 3rd class가 많아서 생존확률이 낮게 나온다.
2.7 Family-SibSP(형제자매)+Parch(부모 자녀)
- SibSp와 Parch를 합하면 Family가 될것 Family 로 합쳐서 분석해보자
df_train['FamilySize'] = df_train['SibSp']+df_train['Parch']+1 # 자신을 더해야 하니깐
df_test['FamilySize']=df_test['SibSp']+df_test['Parch']+1
print('Maximum size of Family: ', df_train['FamilySize'].max())
print("Minumum size of Family: ",df_train['FamilySize'].min())
- FamilySize와 생존의 관계를 한번 살펴봅시다
f,ax=plt.subplots(1,3,figsize=(40,10))
sns.countplot('FamilySize', data=df_train, ax=ax[0])
ax[0].set_title('(1) No. of passengers Boarded', y=1.02)
sns.countplot('FamilySize', hue='Survived',data=df_train, ax=ax[1])
ax[1].set_title('(2) Survived countplot depending on FamilySize', y=1.02)
df_train[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=True).mean().sort_values(by='Survived', ascending=False).plot.bar(ax=ax[2])
ax[2].set_title('(3) Survived rate depending on FamilySize', y=1.02)
plt.subplots_adjust(wspace=0.2, hspace=0.5)
plt.show()
- Figure(1): 가족크기가 1~11까지 있음 대부분 1명이고 그다음으로 2,3,4 순
- Figure(2), (3): 가족크기에 따른 생존비교. 가족이 4명인 경우가 가장 생존확률이 높음
2.8 Fare
- Fare는 탑승요금. continuous feature
fig,ax = plt.subplots(1,1,figsize=(8,8))
g = sns.distplot(df_train['Fare'], color='b', label='Skewness : {:.2f}'.format(df_train['Fare'].skew()), ax=ax)
g = g.legend(loc='best')
- 보시다시피 ditribution이 매우 비대칭인 것을 알 수 있다. (high skewness). 만양 이대로 모델에 넣어준다면 자칫 모델이 잘못 학습할 수도 있다. 몇개 없는 outlier 에 대해서 너무 민감하게 반응한다면, 실제 예측 시에 좋지 못한 결과를 부를 수 있다.
- outlier의 영향을 줄이기 위한 fare에 log를 취하겠다.
- 여기서 우리는 pandas의 유용한 기능을 사용할 겁니다. dataframe의 특정 칼럼에 공통된 작업을 적용하고 싶으면 아래의 map, apply를 사용하면 매우 손쉽게 적용 가능
- 우리가 지금원하는 것은 fare columns의 데이터 모두를 log 값 취하는 것인데, 파이썬의 간단한 lambda 함수를 이용해 간단한로그를 적용하는 함수를 mpa에 인수로 넣어주면 fare columns 데이터에 그대로 적용 됨 매우 유용한 기능이니 꼭 숙지!!!
df_test.loc[df_test.Fare.isnull(), 'Fare'] = df_test['Fare'].mean()
# testset 에 있는 nan value 를 평균값으로 치환합니다.
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)
fig, ax = plt.subplots(1,1, figsize = (8,8))
g = sns.distplot(df_train['Fare'], color='b', label='Skewness : {:.2f}'.format(df_train['Fare'].skew()),ax=ax)
g=g.legend(loc='best')
- 로그를 취하니, 이제 비대칭성이 많이 사라진 것을 볼 수 있다.
- 우리는 이런 작업을 사용해 모델이 좀 더 좋은 성능을 내도록 할 수 있다.
- 사실 방금한 것은 feature engineering에 들어가는 부분인데 여기서 작업했다.
- 모델을 학습시키기 위해, 그리고 그 모델의 성능을 높이기 위해 feature들에 여러 조작을 가하거나, 새로운 feature를 추가하는 것을 feature engineering이라고하는데 우리는 이제 그것을 살펴볼 것이다
2.9 Cabin
- 이 feature는 NaN이 대략 80%이므로 생존에 영향을 미칠 중요한 정보를 얻어내기가 쉽지는 않습니다.
- 그러므로 우리가 세우려는 모델에 포함시키지 않겠다
df_train.head()
2.10 Ticket
- 이 feature는 NaN은 없다. 일단 string data이므로 우리가 어떤 작업들을 해주어야 실제 모델에 사용할 수 있는데 이를 위해선 사실 아이디어가 필요하다
df_train['Ticket'].value_counts()
- 보시다시피 ticket number는 매우 다양하다 우리는 여기서 어떤 특징을 이끌어내서 생존과 연결시킬 수 있을까?
- 직접 한번 아이디어를 내보자! 이것이 본격적인 캐글 레이스의 시작점이다
'10. IT > 12. 머신러닝' 카테고리의 다른 글
[머신러닝] 지도학습 (0) | 2019.02.01 |
---|---|
[머신러닝] 머신러닝은 무엇인가? (0) | 2019.01.31 |