Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

本次分享的项目来自 Kaggle 的经典赛题:泰坦尼克号生还者预测。分为数据分析和数据挖掘两部分介绍。本篇为数据分析篇。


赛题解读

比赛概述

RMS 泰坦尼克号的沉没是历史上最为人熟知的海难事件之一。 1912 年 4 月 15 日,在她的处女航中,泰坦尼克号在与冰山相撞后沉没,在船上的 2224 名乘客和机组人员中,共造成 1502 人死亡。这场耸人听闻的悲剧震惊了国际社会,从而促进了船舶安全规定的完善。

造成海难失事的原因之一是乘客和机组人员没有足够的救生艇。尽管在沉船事件中幸存下有一些运气因素,但有些人比其他人更容易存活下来,比如女人,孩子和上流社会。

在这个挑战中,要求完成哪些人可能存活下来的分析。特别的,要求运用机器学习工具来预测哪些乘客能够幸免于悲剧。

所需技巧

最终目标

  • 预测一名乘客是否能够在泰坦尼克号沉没事件中幸存。
  • 对测试集中的每一个PassengerId,将其相应的Survived变量预测为值 0 或 1(这里 1 表示幸存,0 表示遇难)。

数据分析

数据描述

首先,导入数据:

data_train = pd.read_csv('train.csv')
data_test = pd.read_csv('test.csv')

数据需要进行转换能够作为模型输入。我们对训练集data_train进行变换,同样需要对测试集data_test进行变换,保证模型输入的一致性。

合并在一起可以进行统一变换,变换后再拆分。也可使用之前没有经过变换的data_train。对数据集进行如下处理:

# 将目标变量 Survived 单独提取,从特征变量中移除目标变量 Survived
df_target = data_train['Survived']
data_train_feature = data_train.drop(['Survived'], axis=1)

# 合并 train 和 test 数据集,以方便数据处理对两个数据集同时进行
df = data_train_feature.append(data_test)

合并后的数据集如下图所示:

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

我们看到,共有 11 个特征:

  • PassengerId: 乘客 ID
  • Pclass: 舱位等级 (1 = 1st, 2 = 2nd, 3 = 3rd)
  • Name: 乘客姓名
  • Sex: 性别
  • Age: 年龄
  • SibSp: 在船上的兄弟姐妹/配偶个数
  • Parch: 在船上的父母/小孩个数
  • Ticket: 船票信息
  • Fare: 票价
  • Cabin: 客舱
  • Embarked: 登船港口 (C = Cherbourg, Q = Queenstown, S = Southampton)

以上就是对比赛和数据意义的解读,下面我们对数据进行探索,来达到最终目标。

数据预处理

特征分析


我们已经在上一步查看了有哪些特征,接下来要做的是:

  • 查看哪些特征存在缺失值。
  • 判断哪些特征存在异常值,通过对数值型特征进行简单的描述性统计。
  • 判断这些特征的数据类型,从而为后面的可视化分析找到合适的方法。
  • 对数据进行可视化分析。
  • 分析得出有用的结论。

1. 查看哪些特征存在缺失值
查看 traintest 合并后的数据:

df.info()

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

查看哪些特征存在缺失值,缺失程度是否严重:

print(pd.isnull(df).sum())

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

可以看出 agecabinembarkedFare 四个特征有缺失值,其中 cabin 的值缺失较严重。


2. 判断哪些特征存在异常值

对数值型特征进行简单的描述性统计,包括均值,中位数,标准差,最大值,最小值等,从而判断哪些特征存在异常值。

df.describe()

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

观察上述值,其中 Age 的最小值为 0.17,表示的应该是婴儿的年龄,最大值为 80,年龄有些偏大。Fare 表示船票价格,它的平均值为 33.2,中位数 14,平均值比中位数大很多,说明该特征分布是严重的右偏,又有最大值约 512,所以这个值很可能是一个异常值。在 SibSpParch 中,Sibsp 最大值为 8,有可能是异常值,但 Parch 最大值也为 9。这两个特征同时出现相近的较大的数值,而又由二者所表示的含义,说明这个数值是有可能的,需要进一步的观察。

由上所述,我们看到了一些可能的异常值,但还不能确定。需要我们进一步通过可视化来清楚的显示,并结合对业务的理解来确定。


3. 判断特征的数据类型

  • 定类:Name, Sex, Ticket, Embarked, Cabin
  • 定序:Pclass
  • 定比:Age, Fare, SibSp, Parch

根据以上对各个特征数据类型的判断,选择合适的可视化方法完成可视化。通过可视化可以:

  • 可以发现事实问题,并寻找出现的原因。
  • 更清晰的了解特征对目标变量的影响,有助于特征工程。
  • 可以发现不易发现的特征异常值。

4. 数据可视化分析
首先,定制画布风格:

plt.style.use("bmh")

解决中文乱码问题:

plt.rcParams['font.sans-serif'] = ['SimHei']
# 或者用 plt.rc('font', family='SimHei', size=13)

定类 / 定序特征分析

cat_list = ['Pclass','Name','Sex','Embarked','Ticket','Cabin']
for n,i in enumerate(cat_list):  
    Cabin_cat_num = df[i].value_counts().index.shape[0]
    print('{0}. {1}特征的类型数量是: {2}'.format(n+1,i,Cabin_cat_num))

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

在上面各特征值的类型中,一些比较少数量的特征如 PclassSexEmbarked 等可进行可视化分析。剩下特征如 TicketCabin 分类较多,进行可视化分析达不到想要效果,难以得到有用的结论。


先对上面 3 种容易的分类进的特征行可视化,而对于 NameTicketCabin 等到后续进行进一步分析。

f, [ax1,ax2,ax3] = plt.subplots(1,3,figsize=(20,5))
sns.countplot(x='Sex', hue='Survived', data=data_train, ax=ax1)
sns.countplot(x='Pclass', hue='Survived', data=data_train, ax=ax2)
sns.countplot(x='Embarked', hue='Survived', data=data_train, ax=ax3)
ax1.set_title('Sex特征分析')
ax2.set_title('Pclass特征分析')
ax3.set_title('Embarked特征分析')
f.suptitle('定类/定序数据类型特征分析', size=20, y=1.1)

plt.show()

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

通过分别观察各特征值的分布情况和与目标变量之间的关系,得出以下结论:

  • Sex:我们从 Sex 的特征分析图可以清晰的看,男性总人数大于女性总人数,但女性的存活率远远高于男性。
  • Pclass:3 等舱的存活率明显比 1 等舱和 2 等舱的低很多,这是由于 3 等舱的多为普通市民,而等级越高的舱位越有可能是当时社会地位较高的人。
  • Embarked:登陆港口 S 的数量最多,但获救率较低,而 C 港和 Q 港基本都有一半的人获救。

其中,SexPclass 两个特征影响力较大。


以上是针对单独特征对生还与否的简单分析,但在实际问题中,往往是由多个因素共同决定对目标变量的影响。因此,我们需要知道在某个特定条件下的特征的影响(如 Pclass 是 1 的情况下男性和女性生还概率有何不同)才更加能帮助我们分析,即在数据集的子集内可视化变量的分布或多个变量之间的关系。这时就需要用到FacetGrid子集数据。

在不同社会等级下,男性和女性在不同登陆港口下的数量对比:

grid = sns.FacetGrid(df, col='Pclass', hue='Sex', palette='seismic', size=4)
grid.map(sns.countplot, 'Embarked', alpha=0.8)
grid.add_legend()

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

观察得到:

  • 在 Q 港口登陆的乘客中,3 等舱的乘客占了绝大多数,而其他两个舱位几乎没有乘客在该港口登陆,说明登陆港口可能与社会等级有关联。
  • 在 C 港口登陆的乘客中,女性乘客占大多数,这也间接的说明在 C 港口生还率很大。
  • 在 S 港口登陆的乘客中,3 等舱的乘客最多,且总的来说男性乘客占比也最多,因此可推测 S 港口生还率较小。

定距/定比特征分析


Age 特征的 kde 分布:

f,ax = plt.subplots(figsize=(10,5))
sns.kdeplot(data_train.loc[(data_train['Survived'] == 0),'Age'] , color='gray',shade=True,label='not survived')
sns.kdeplot(data_train.loc[(data_train['Survived'] == 1),'Age'] , color='g',shade=True, label='survived')
plt.title('Age特征分布', fontsize = 15)
plt.xlabel("Age", fontsize = 15)
plt.ylabel('Frequency', fontsize = 15)

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

根据 Age 特征得到不同性别生还与否的分布:

def plot_distribution( df , var , target , **kwargs ):
    row = kwargs.get( 'row' , None )
    col = kwargs.get( 'col' , None )
    facet = sns.FacetGrid( df , hue=target , aspect=4 , row = row , col = col )
    facet.map( sns.kdeplot , var , shade= True )
    facet.set( xlim=( 0 , df[ var ].max() ) )
    facet.add_legend()
plot_distribution( data_train , var = 'Age' , target = 'Survived' , row = 'Sex' )

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

整体观察得到,0 到十几岁的孩子生还率最高,20 岁到 30 岁左右的生还率较低,其他年龄段没有太大的区别。而对于男性来说,0 到十几岁的乘客生还率明显较高,20 岁到 30 岁左右的生还率较低;而对女性来说,反而是 30 岁到 40 岁的年龄段生还率相对较高,0 到十几岁生还率并不高,其它各年龄段的生还率没有较大差别。


Fare 特征:

# 填充缺失值
data_test["Fare"].fillna(data_test["Fare"].median(), inplace=True)

data_train['Fare'] = data_train['Fare'].astype(int)
data_test['Fare'] = data_test['Fare'].astype(int)

# 分别获得生还和遇难乘客的 Fare
fare_not_survived = data_train["Fare"][data_train["Survived"] == 0]
fare_survived = data_train["Fare"][data_train["Survived"] == 1]

# 得到 Fare 的均值和方差
avgerage_fare = pd.DataFrame([fare_not_survived.mean(), fare_survived.mean()])
std_fare = pd.DataFrame([fare_not_survived.std(), fare_survived.std()])

data_train['Fare'].plot(kind='hist', figsize=(15,3),bins=100, xlim=(0,50))

avgerage_fare.index.names = std_fare.index.names = ["Survived"]
avgerage_fare.plot(yerr=std_fare,kind='bar',legend=False)

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇
Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

容易观察得到 Fare 低的数量多,而 Fare 高的数量少,但生还率明显要比 Fare 低的高很多。这也符合实际情况,买得起高票价的有钱人居多,社会地位可能相对较高,更容易获救。


SibSpParch 特征:

data_train['Family'] =  data_train["Parch"] + data_train["SibSp"]
data_train['Family'].loc[data_train['Family'] > 0] = 1
data_train['Family'].loc[data_train['Family'] == 0] = 0

data_test['Family'] =  data_test["Parch"] + data_test["SibSp"]
data_test['Family'].loc[data_test['Family'] > 0] = 1
data_test['Family'].loc[data_test['Family'] == 0] = 0

# 删除 Parch 和 SibSp
data_train = data_train.drop(['SibSp','Parch'], axis=1)
data_test = data_test.drop(['SibSp','Parch'], axis=1)

# 绘图
fig, (axis1,axis2) = plt.subplots(1,2,sharex=True,figsize=(10,5))

sns.countplot(x='Family', data=data_train, order=[1,0], ax=axis1)

# 分为和家人一起、独自乘船两种情况
family_perc = data_train[["Family", "Survived"]].groupby(['Family'],as_index=False).mean()
sns.barplot(x='Family', y='Survived', data=family_perc, order=[1,0], ax=axis2)

axis1.set_xticklabels(["With Family","Alone"], rotation=0)

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇

我们将 SibSpParch 这两个特征合并为了一个 Family 特征,表示该乘客是否有和家人一起乘船,来判断是否会增加生还率。

可以明显的看出独自乘船的乘客人数大于和家人一起的乘客人数,但幸存人数相对而言较少。


5. 总结

将上述 6 个特征的相互关联图进行汇总,对角线为特征自身的 kde 分布,如下图所示:

g = sns.pairplot(data_train[[u'Survived', u'Pclass', u'Sex', u'Age', u'Family', u'Fare', u'Embarked']], hue='Survived', palette = 'seismic',
                 size=4,diag_kind = 'kde',diag_kws=dict(shade=True),plot_kws=dict(s=50) )
g.set(xticklabels=[])

Kaggle入门级赛题:泰坦尼克号生还者预测——数据分析篇


数据分析部分就是这样了,根据个人的不同理解还可以有很多种不同的可视化方法,但最终目标都是为了帮助我们理解数据,进行挖掘分析。关于特征工程和建模的部分将在下一篇的数据挖掘中来介绍。


参考链接:
【Kaggle入门级竞赛top5%排名经验分享】— 分析篇
样式美化matplotlib.pyplot.style.use定制画布风格
mac下python matplotlib中文乱码解决方案
Seaborn(sns)官方文档学习笔记(第六章 绘制数据网格)

不足之处,欢迎指正。