天天看点

Kaggle竞赛——Titanic

​ 找回了自己的CSDN账号,想从头记录一下自己做kaggle竞赛的经历,第一篇当然要从人人都爱的Titanic写起。

​ Titanic项目大概是Kaggle上最受欢迎的项目了,截止到我提交结果的时候已经有18000多人提交了预测结果。参与人数众多,也诞生了很多优秀的kernel,使这个项目成为了萌新熟悉kaggle竞赛的唯一指定项目。大佬们分享的经验就像出新手村的首冲礼包,能让你在数据分析的路上快速成长。

项目简介

​ 首先我们看一下这个项目的介绍(因为懒这里就放了官方的介绍):

The sinking of the Titanic is one of the most infamous shipwrecks in history.

On April 15, 1912, during her maiden voyage, the widely considered “unsinkable” RMS Titanic sank after colliding with an iceberg. Unfortunately, there weren’t enough lifeboats for everyone onboard, resulting in the death of 1502 out of 2224 passengers and crew.

While there was some element of luck involved in surviving, it seems some groups of people were more likely to survive than others.

In this challenge, we ask you to build a predictive model that answers the question: “what sorts of people were more likely to survive?” using passenger data (ie name, age, gender, socio-economic class, etc).

​ 简单来说就是根据给定的乘客数据和生存情况,建立模型,根据测试集的乘客数据情况预测生存情况,下面是乘客特征的信息:

PassengerId:乘客编号

Survived:是否幸存,0-1特征,要预测的值

Pclass:船舱编号,分为1/2/3等级

Name:乘客姓名,包含Mr/Miss/Mrs等可以分析年龄性别的特征

Sex:乘客性别

Age:乘客年龄

Sibsp:同辈亲友数,即兄弟姐妹和配偶数

Parch:不同辈亲友数,即父母子女数

Ticket:船票编号

Fare:船票价格

Cabin:客舱号

Embarked:登船港口

​ 下面我们开始进行数据分析和建模预测

数据清洗

​ 我的习惯是首先观察数据集结构和确实情况,进行数据清洗,补充缺失值。

​ 导入数据和需要的包:

%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

train_data = pd.read_csv('../input/titanic/train.csv')
test_data = pd.read_csv('../input/titanic/test.csv')
PassengerId = test_data['PassengerId']
all_data = pd.concat([train_data, test_data], ignore_index = True)
           

​ 观察数据集缺失值情况:

Kaggle竞赛——Titanic

​ Cabin特征缺失值为1014,占样本大多数,通过观察发现Cabin值的有无对结果Survived有较大影响,因此将其转换为0-1值:

all_data['Cabin'] = all_data['Cabin'].fillna(0)
for i in range(0, 1308):
    if all_data.loc[i, 'Cabin'] != 0:
        all_data.loc[i, 'Cabin'] = 1
pd.pivot_table(all_data, index=['Cabin'], values=['Survived']).plot.bar(figsize=(8,5))
plt.title('Survival Rate')
           
Kaggle竞赛——Titanic

​ 通过观察相关性,发现age特征和其他特征相关性不大,因此用Regression Model效果并不好

Kaggle竞赛——Titanic

​ 因此考虑以Sex,Title,Pclass三个特征构建随机森林填充数据。首先构造Title特征,从‘Name’中提取‘Title’进行分类,可以观察到不同称呼的乘客生还率存在差异,Mr和Office与其他标签对比明显。

all_data['Title'] = all_data['Name'].apply(lambda x:x.split(',')[1].split('.')[0].strip())
Title_Dict = {}
Title_Dict.update(dict.fromkeys(['Capt', 'Col', 'Major', 'Dr', 'Rev'], 'Officer'))
Title_Dict.update(dict.fromkeys(['Don', 'Sir', 'the Countess', 'Dona', 'Lady'], 'Royalty'))
Title_Dict.update(dict.fromkeys(['Mme', 'Ms', 'Mrs'], 'Mrs'))
Title_Dict.update(dict.fromkeys(['Mlle', 'Miss'], 'Miss'))
Title_Dict.update(dict.fromkeys(['Mr'], 'Mr'))
Title_Dict.update(dict.fromkeys(['Master','Jonkheer'], 'Master'))

all_data['Title'] = all_data['Title'].map(Title_Dict)
sns.barplot(x="Title", y="Survived", data=all_data)
           
Kaggle竞赛——Titanic

​ 再以Sex,Pclass,Title三个特征构建随机森林填充数据。

from sklearn.ensemble import RandomForestRegressor
age_df = all_data[['Age', 'Pclass','Sex','Title']]
age_df = pd.get_dummies(age_df)
known_age = age_df[age_df.Age.notnull()].values
unknown_age = age_df[age_df.Age.isnull()].values
y = known_age[:, 0]
X = known_age[:, 1:]
rfr = RandomForestRegressor(random_state=0, n_estimators=100, n_jobs=-1)
rfr.fit(X, y)
predictedAges = rfr.predict(unknown_age[:, 1::])
all_data.loc[ (all_data.Age.isnull()), 'Age' ] = predictedAges
           

​ Fare缺失一个数据,其Embarked为S,Pclass为3,因此可以用中位数填充。

fare = all_data[(all_data['Embarked'] == "S") & (all_data['Pclass'] == 3)].Fare.median()
all_data['Fare']  =all_data['Fare'].fillna(fare)
           

​ Embarked缺失两个值,其Pclass均为1,fare均为80,观察后发现Embarked为C时,Pclass为1,Fare中位数约为80,因此填充为C。

Kaggle竞赛——Titanic

​ 至此,数据集缺失填充完毕

Kaggle竞赛——Titanic

EDA(数据探索性分析)

​ 观察数据集结构和测试集数据结构

train_data.info()
print('-' * 40)
test_data.info()
           
Kaggle竞赛——Titanic
Kaggle竞赛——Titanic

​ 观察各特征和Survived的相关性

Kaggle竞赛——Titanic

​ 女性生还率远高于男性

Kaggle竞赛——Titanic

​ 乘客客舱等级越高,生还率越高

facet = sns.FacetGrid(train_data, hue="Survived", aspect=2)
facet.map(sns.kdeplot,'Age', shade= True)
facet.set(xlim=(0, train_data['Age'].max()))
facet.add_legend()
plt.xlabel('Age')
plt.ylabel('density')
           
Kaggle竞赛——Titanic

​ 观察年龄和生还率之间的关系,发现在12岁左右生还率差异较大,考虑分离小于12岁的部分

特征工程

我们将姓氏相同的乘客分为一组组成家庭,并分别提取出其中的妇女儿童和成年男性

all_data['Surname'] = all_data['Name'].apply(lambda x:x.split(',')[0].strip())
Surname_Count = dict(all_data['Surname'].value_counts())
all_data['FamilyGroup'] = all_data['Surname'].apply(lambda x:Surname_Count[x])
Female_Child_Group = all_data.loc[(all_data['FamilyGroup']>=2) & ((all_data['Age']<=12) | (all_data['Sex']=='female'))]
Male_Adult_Group = all_data.loc[(all_data['FamilyGroup']>=2) & (all_data['Age']>12) & (all_data['Sex']=='male')]
           
Female_Child=pd.DataFrame(Female_Child_Group.groupby('Surname')['Survived'].mean().value_counts())
Female_Child.columns=['GroupCount']
Female_Child
           
Kaggle竞赛——Titanic

我们观察到,妇女和儿童的平均生还率集中在1和0,即要么全部幸存,要么全部遇难

Male_Adult=pd.DataFrame(Male_Adult_Group.groupby('Surname')['Survived'].mean().value_counts())
Male_Adult.columns=['GroupCount']
Male_Adult
           
Kaggle竞赛——Titanic

同时成年男性组的生还率也集中在1和0

普遍规律为妇女儿童的生还率远高于成年男性,因此我们将异常值选出来单独处理,将妇女儿童生还率为0设置为遇难组,成年男性生还率为1设置为幸存组

Female_Child_Group = Female_Child_Group.groupby('Surname')['Survived'].mean()
Dead_List = set(Female_Child_Group[Female_Child_Group.apply(lambda x:x==0)].index)
print(Dead_List)
Male_Adult_List = Male_Adult_Group.groupby('Surname')['Survived'].mean()
Survived_List = set(Male_Adult_List[Male_Adult_List.apply(lambda x:x==1)].index)
print(Survived_List)
           
Kaggle竞赛——Titanic

为保证分离异常值,我们将测试集中的异常样本进行惩罚修改其Sex,Title和Age特征

train=all_data.loc[all_data['Survived'].notnull()]
test=all_data.loc[all_data['Survived'].isnull()]
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Sex'] = 'male'
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Age'] = 60
test.loc[(test['Surname'].apply(lambda x:x in Dead_List)),'Title'] = 'Mr'
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Sex'] = 'female'
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Age'] = 5
test.loc[(test['Surname'].apply(lambda x:x in Survived_List)),'Title'] = 'Miss'
           

选出特征,转换变量,分离测试集和训练集

all_data = pd.concat([train, test])
all_data = all_data[['Survived','Pclass','Sex','Age','Cabin','Fare','Embarked','Title']]
all_data = pd.get_dummies(all_data)
train = all_data[all_data['Survived'].notnull()]
test = all_data[all_data['Survived'].isnull()].drop('Survived',axis=1)
X = train.values[:,1:]
y = train.values[:,0]
           

模型优化和预测

利用网格搜索自动化选取最优参数,建立pipeline减少算法复杂度,这里由于特征较少,我们选择参数k=all利用所有特征

from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.feature_selection import SelectKBest

pipe = Pipeline([('select', SelectKBest(k=17)),
               ('classify', RandomForestClassifier(random_state = 10, max_features = 'sqrt'))])

param_test = {'classify__n_estimators':list(range(20,50,2)),
              'classify__max_depth':list(range(3,60,3))}
gsearch = GridSearchCV(estimator = pipe, param_grid = param_test, scoring='roc_auc', cv=10)
gsearch.fit(X,y)
print(gsearch.best_params_, gsearch.best_score_)
           
Kaggle竞赛——Titanic

得到最优参数n_estimator = 24,max_depth = 6

训练模型

from sklearn.pipeline import make_pipeline
select = SelectKBest(k = 17)
clf = RandomForestClassifier(random_state = 10, warm_start = True,
                                  n_estimators = 24,
                                  max_depth = 6,
                                  max_features = 'sqrt')
pipeline = make_pipeline(select, clf)
pipeline.fit(X, y)
           
Kaggle竞赛——Titanic

交叉验证,按照默认方式切分数据集

from sklearn import model_selection, metrics
cv_score = model_selection.cross_val_score(pipeline, X, y, cv= 10)
print("CV Score : Mean - %.7g | Std - %.7g " % (np.mean(cv_score), np.std(cv_score)))
           
Kaggle竞赛——Titanic

进行预测输出结果

predictions = pipeline.predict(test)
submission = pd.DataFrame({"PassengerId": PassengerId, "Survived": predictions.astype(np.int32)})
submission.to_csv("./Titanic_submission.csv", index=False)
           

输出结果最终得分为0.80382,位于top5%。

一些碎碎念

第一次做数据分析比赛,阅读了很多资料和kernel后拿到了前5%的成绩,属实给了我很大的信心在这条路上走下去。

这次的预测模型最终得到了0.8的模型得分,所以肯定是有很多不足的地,也有很多可以作为经验的小tips:

  • 在进行EDA时,很多细节没有注意到,例如在Name特征中,注意到如果一个成年男性生还,那么与他同姓也就是一家人的生还的可能也很大。
  • 特征工程部分做的也不是很细致,不同特征之间的关系没有挖掘的很彻底
  • 选择了随机森林模型进行训练和预测精度是很高的,很多top3%以上的大佬用的都是随机森林模型,只是因为特征工程的细节问题导致模型参数不是最优
  • Titanic项目只是给新手入门并且熟练手中的工具的一个项目,所以个人认为如果你已经取得了不错的成绩,那么没有必要再在提高分数上花费太多精力,因为在这里一些提高精度的方法是没有普遍性的,不如找一些真实的数据项目来提升自己的业务能力和分析水平。
  • 你问我那些精度为1.00的人是怎么做到的?原因是遇难和生还者的名单是公布的 - -。