2. Feature Engineering and Data Cleaning

 

데이터를 대충 확인했으니 해야 할 일은 무엇인가? 바로 전처리이다! 사실 앞선 내용에서는 전처리가 EDA 보다 먼저라고 했지만 사실 EDA와 전처리는 같이 왔다갔다 하며 하기 때문에 이 문제에서는 우선적으로 EDA로 데이터를 확인했다. EDA로 데이터의 특성을 얼추 파악했으니 이제 전처리를 해야한다. 무엇을 전처리해야할까?

 

전처리할 것을 파악하기 위해 null값의 개수를 확인해보겠다.

 

train_data.isnull().sum()
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

 

Age와 Cabin은 앞선 EDA에서 누락된 데이터가 꽤나 있음을 알았는데 Embarked도 2개의 누락된 데이터가 있었다. 앞서 말했지만 Cabin은 누락된 데이터가 너무 많으므로 과감히 지운다. 그럼 Age와 Embarked의 누락된 데이터를 채워보자. 어떻게 채워야 누락된 데이터를 잘 채웠다고 소문이날까?

 

Age를 채우는 방법은 다양할 것이다. 누락된 데이터에 전체 나이의 평균치를 넣을 수도 있고 EDA를 통해 알게 된 데이터의 특징에 따라 채울 수도 있다. 필자는 참고한 케글의 방식을 따르겠다. 참고한 케글은 사람의 이름을 기준으로 Age를 나누었다. 탑승자 이름의 중간에 다양한 title이 존재하는데 이 title을 한 번 확인해보자.

 

train_data['Initial']= train_data.Name.str.extract('([A-Za-z]+)\.')

test_data['Initial']= test_data.Name.str.extract('([A-Za-z]+)\.')

# A-Z 또는 a-z 사이에 있고 .(점)이 오는 문자열을 찾는다. 중간에 위치한 title 찾는 코드
# 성별로 분류하여 Initial을 확인

pd.crosstab(train_data.Initial,train_data.Sex).T.style.background_gradient(cmap='summer_r')

성별을 기준으로 Initial을 확인한다.

 

여성은 Miss와 Mrs를 제일 많이 쓰고, 남성은 Mr와 Master를 많이 사용한다. 그럼 우리는 이제 누락된 데이터 중에서 Miss와 Mrs, Mr와 Master를 title로 갖는 값에 어떤 값을 채워야할까? 채워야 하는 값을 생각하기에 앞서, Miss, Mrs, Mr, Master와 같이 유의미하게 많은 데이터가 아닌 것들을 유의미한 데이터에 병합시킨다. 여기서 Mlle과 Mme는 Miss의 오타로 추정되기 때문에 Miss로 들어간다.

 

# 라벨(title)을 유의미한 데이터로 대체한다.

train_data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)
test_data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)

# 라벨에 따라 평균 값을 나타낸다.

train_data.groupby('Initial')['Age'].mean()
Initial
Master     4.574167
Miss      21.860000
Mr        32.739609
Mrs       35.981818
Other     45.888889
Name: Age, dtype: float64

 

그럼 이런 결과가 나타난다. Master는 답지 않게 어린 아이들을 뜻하는 것같고 Miss는 약 22세, Mr는 약 33세, Mrs는 약 36세, 기타의 title들은 약 46세의 평균값을 가진다. 이 평균값들을 누락된 데이터에 title을 기준으로 집어넣는다.

 

train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Mr'),'Age']=33
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Mrs'),'Age']=36
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Master'),'Age']=5
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Miss'),'Age']=22
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Other'),'Age']=46

test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Mr'),'Age'] = 33
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Mrs'),'Age'] = 36
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Master'),'Age'] = 5
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Miss'),'Age'] = 22
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Other'),'Age'] = 46


# 쉽게 하는 방법
title = train_data.Name.str.split('.').str.get(0).str.split(',').str.get(1)
train_data['title'] = title

a = train_data.groupby(train_data.title).Age.mean().round().reset_index()

pd.merge(train_data,a,on='title')

 

이제 train_data와 test_data에 누락된 값이 있는지, 평균값들이 잘 채워져 있는지 확인해본다.

 

train_data.Age.isnull().any()
test_data.Age.isnull().any()
False

 

Embarked 값은 train_data에서만 누락되어있고 누락된 데이터가 단지 2개 뿐이니, 가장 많이 탑승한 탑승구인 S를 채워넣겠다.

 

train_data['Embarked'].fillna('S',inplace=True)
train_data.Embarked.isnull().any()

 

 


 

1) Age_band

 

Age 데이터는 연속성을 띈다. 그냥 사용해도 되겠으나 우리는 다양한 방법을 공부하고 있기 때문에 이것을 그룹화시키려고 한다. 

 

# Age를 그룹화하기

train_data['Age_band']=0
train_data.loc[train_data['Age']<=16,'Age_band']=0
train_data.loc[(train_data['Age']>16)&(train_data['Age']<=32),'Age_band']=1
train_data.loc[(train_data['Age']>32)&(train_data['Age']<=48),'Age_band']=2
train_data.loc[(train_data['Age']>48)&(train_data['Age']<=64),'Age_band']=3
train_data.loc[train_data['Age']>64,'Age_band']=4
train_data.head()

train_data.head()를 통해 Age_band를 확인할 수 있다.

 

train_data['Age_band'].value_counts().to_frame().style.background_gradient(cmap='summer')

 

 

sns.factorplot('Age_band','Survived',data=train_data,col='Pclass')
plt.show()

Pclass 별 Age_band의 생존률

 

연속적인 Age 데이터를 Age_band로 그룹화하고 그것에 대해 EDA를 해보았다. 역시나 어떤 클래스던지간에 나이가 어린 사람일수록 생존률이 높음을 확인할 수 있다.

 

 

2) Family_Size and Alone

 

Family_size 와 Alone으로 분류한다. 이것은 Parch와 SibSp의 압축이다. 생존율이 승객의 가족 규모와 연관이 있는지를 판단하기 위해 분류한다.

 

# train_data를 Family_Size와 Alone으로 분류

#family size
train_data['Family_Size']=0
train_data['Family_Size']=train_data['Parch']+train_data['SibSp']

#Alone
train_data['Alone']=0
train_data.loc[train_data.Family_Size==0,'Alone']=1

sns.factorplot('Family_Size','Survived',data=train_data)
plt.show()

혼자라고 생존률이 높은것은 아니다.

 

2~4인 가족의 생존률이 다른 가족 수보다 더 높음을 알 수 있다.

 


 

얼추 데이터를 그룹화했으니 이제 데이터들이 얼마나 서로 유의미하게 연관되어있는지를 확인해볼 차례이다.

앞선 내용에서 생존과 굉장히 밀접한 관련이 있던 3가지 부분 또한 위와 같이 숫자로 그룹화하겠다.

 

train_data['Sex'].replace(['male','female'],[0,1],inplace=True)
train_data['Embarked'].replace(['S','C','Q'],[0,1,2],inplace=True)
train_data['Initial'].replace(['Mr','Mrs','Miss','Master','Other'],[0,1,2,3,4],inplace=True)

 

그 후 무의미하다고 여겨지는 데이터들을 Drop한다.

 

train_data.drop(['Name','Age','Ticket','Cabin','PassengerId','SibSp','Parch'],axis=1,inplace=True)
sns.heatmap(train_data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2,annot_kws={'size':20})
fig=plt.gcf()
fig.set_size_inches(18,15)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

상관관계를 나타내는 표가 만들어진다.

 

위의 이미지는 상관관계를 나타내주는 것이다. 색이 진할수록 관련도가 높은것이다. 이 표를 보면 생존률은 PClass, Sex, Fare, Initial과 많이 연관되어 있음을 알 수 있다. 상관관계 또한 EDA중의 하나이다. 개발자는 상관관계 표를 통해 유의미하게 연관되어 있는 데이터를 찾을 수 있다. 지금도 PClass, Sex, Initial에 대한 것은 이전 EDA를 통해 알았지만 Fare는 처음 알지 않았는가!

 

또 길어졌으니 학습하는 방법은 다음에 설명하겠다.

 

 

+ Recent posts