文章目录
0. 获取数据集
关注公众号:『AI学习星球
』
回复:泰坦尼克号获救预测
即可获取数据下载。
论文辅导
或算法学习
可以通过公众号滴滴我
1. 项目背景
为了让部分没看过《泰坦尼克号》这部电影的小伙伴也能对数据背景有一个基本认识,我们简单概括一下背景:
泰坦尼克号的沉没是世界上最严重的海难事故之一,造成了大量的人员伤亡。这是一艘号称当时世界上最大的邮轮,船上的人年龄各异,背景不同,有贵族豪门,也有平民旅人,邮轮撞击冰山后,船上的人马上采取措施安排救生艇转移人员,从本次海难中存活下来的,也就是幸存者。
而这也引出了我们今天的主题:通过挖掘数据中的关键信息来预测一下哪些人可能成为幸存者。
下面,正文开始~
1. 数据读取
import matplotlib.pyplot as plt
#设置中文编码和负号的正常显示
plt.rcParams['font.family']='SimHei'
plt.rcParams['axes.unicode_minus'] = False
import pandas as pd
import numpy as np
# 训练集数据
data_train = pd.read_csv('train.csv')
# 测试集数据
data_test = pd.read_csv('test.csv')
data_train.shape, data_test.shape, data_train.columns
- 可以看到训练集总共有12列891行数据,其中Survived字段表示的是乘客是否获救,其余都是乘客个人信息,包括:
- Survived — 获救情况(二分类,因变量)
- PassengerId — 乘客ID
- Pclass — 乘客等级(1/2/3等舱位)
- Name — 乘客姓名
- Sex — 性别
- Age — 年龄
- SibSp — 堂兄弟/妹个数
- Parch — 父母与小孩个数
- Ticket — 船票信息
- Fare — 票价
- Cabin — 客舱
- Embarked — 登船港口
2. 简单描述性分析
# 看一下数据类型及缺失情况
data_train.info()
- 数据显示,训练数据集中共有891名乘客,12列特征,其中有三列数据存在缺失:
- Age(年龄):只有714条完整记录,有177条记录缺失;
- Cabin(客舱):只有204条乘客已知,有687条记录缺失,缺失较多!;
- Embarked(登船港口):只有两条记录缺失;
# 数据的描述性统计
data_train.describe()
- 根据表格,我们可以得出一些基本信息:
- Survived:大概有0.383838比例的人最后获救了;
- Pclass: 2号和3号舱的人要比1号舱的人多;
- Age:所有乘客的平均年龄大概再29.7岁,最小的乘客0.42岁,最大的乘客80岁;
- Fare:平均票价在32元,最高的票价在512元;
- …
# 查看test数据集,看分布是否与训练集一致
data_test.describe()
训练集和测试集大致的分布相差不大
3. 探索性分析
3.1 乘客不同特征的描述性统计
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(15,10))
fig.set(alpha=0.2) # 设定图标透明度
plt.subplot2grid((2,3),(0,0)) # 分为2行3列,从(0,0)算起
data_train.Survived.value_counts().plot(kind='bar')
plt.title('获救情况(1为获救)')
plt.ylabel('人数')
plt.subplot2grid((2,3),(0,1))
data_train.Pclass.value_counts().plot(kind="bar")
plt.ylabel("人数")
plt.title("乘客等级分布")
plt.subplot2grid((2,3),(0,2))
plt.scatter(data_train.Survived, data_train.Age)
plt.ylabel("年龄")
plt.grid(True, which='major', axis='y') # 绘制网格线
plt.title("按年龄看获救分布 (1为获救)")
plt.subplot2grid((2,3),(1,0), colspan=2)
data_train.Age[data_train.Pclass == 1].plot(kind='kde') # 绘制密度图
data_train.Age[data_train.Pclass == 2].plot(kind='kde')
data_train.Age[data_train.Pclass == 3].plot(kind='kde')
plt.xlabel("年龄")
plt.ylabel("密度")
plt.title("各等级的乘客年龄分布")
plt.legend(('头等舱', '2等舱', '3等舱'),loc='best') #
plt.subplot2grid((2,3),(1,2))
data_train.Embarked.value_counts().plot(kind='bar')
plt.title("各登船口岸上船人数")
plt.ylabel("人数")
-
根据训练集数据绘制如上图所示,从图中我们可以得到一些信息:
- 未能获救的人有500+,而获救的人大概有300+,不到人数的一半;
- 三等舱乘客最多,接近500人,而一等和二等舱的乘客相对较少,都在200人左右;
- 从年龄分布可以看出,遇难和获救的乘客年龄分布都比较离散,跨度大;
- 三个不同舱的乘客年龄总体趋势大致相同,其中20岁左右的乘客主要集中再二三等舱,一等舱中40岁左右的最多;
- 再登船港口中,其中S港口上传人数最多,有600+人,另外两个C和Q港口,都不到200人,要远远小于C港口;
-
根据以上结论,提出一些假设:
- 不同舱位/乘客等级可能和财富/地位有关系,最后获救概率可能会不一样;
- 年龄对获救概率也一定是有影响的,毕竟背景知识提到,副船长还说『小孩和女士先走』呢;
- 获救概率与登船港口是不是有关系呢?也许登船港口不同,人的出身地位不同?
- …
3.2 看看各乘客登记的获救情况
fig = plt.figure()
fig.set(alpha=0.2) # 设定图表颜色alpha参数
Survived_0 = data_train.Pclass[data_train.Survived == 0].value_counts()
Survived_1 = data_train.Pclass[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'未获救':Survived_0, u'获救':Survived_1})
df.plot(kind='bar', stacked=True)
plt.title(u'不同等级乘客获救情况')
plt.xlabel(u'乘客等级')
plt.ylabel(u'人数')
plt.show()
data_train[['Pclass', 'Survived']].groupby('Pclass').mean()
从图中可以看出,等级为1的乘客,获救的概率最大,并且随着等级的递减,获救的概率也是递减状态!
所以,乘客等级这必然是一个影响乘获救的重要特征!!
3.3 查看各性别的获救情况
fig = plt.figure()
fig.set(alpha=0.2)
Survived_0 = data_train.Survived[data_train.Sex == 'male'].value_counts()
Survived_1 = data_train.Survived[data_train.Sex == 'female'].value_counts()
df = pd.DataFrame({u'男性':Survived_0, u'女性':Survived_1})
df.plot(kind='bar', stacked=True)
plt.title(u'不同性别乘客获救情况')
plt.xlabel(u'获救与否')
plt.ylabel(u'人数')
plt.show()
data_train[['Sex','Survived']].groupby('Sex').mean()
从图中可以看出,相对男性来说,女性的获救率远远高于男性,看来外国人还是比较践行女性优先的!
所以,性别对于最终生存与否也是有非常重要影响的!
3.4 查看各登船港口的获救情况
泰坦尼克号从英国的南安普顿港出发,途径法国瑟堡和爱尔兰昆士敦,那么在昆士敦之前上船的人,有可能在瑟堡或昆士敦下船,这些人将不会遇到海难。
fig = plt.figure(figsize=(10,15))
fig.set(alpha=0.2)
Survived_0 = data_train.Embarked[data_train.Survived == 0].value_counts()
Survived_1 = data_train.Embarked[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'未获救':Survived_0, u'获救':Survived_1})
df.plot(kind='bar', stacked=True)
plt.title(u'不同登船港口的乘客获救情况')
plt.xlabel(u'登船港口')
plt.ylabel(u'人数')
plt.show()
data_train[['Embarked','Survived']].groupby('Embarked').mean()
import seaborn as sns
sns.factorplot('Embarked','Survived',data=data_train)
plt.title(u'各登录港口乘客的获救情况')
可以看出,再不同港口上船,生还率不同,其中C港口最高,Q次之,S港口最低;
3.5 查看携带家人数量不同的获救情况
data_train['family'] = data_train['SibSp'] + data_train['Parch']
data_train['family']
Survived_0 = data_train.family[data_train.Survived == 0].value_counts()
Survived_1 = data_train.family[data_train.Survived == 1].value_counts()
df = pd.DataFrame({u'未获救':Survived_0, u'获救':Survived_1})
df.plot(kind='bar', stacked=True)
plt.title(u'携带不同家人数量的乘客获救情况')
plt.xlabel(u'携带家人数量')
plt.ylabel(u'人数')
plt.show()
data_train[['family', 'Survived']].groupby('family').mean().plot.bar()
可以看到,独自一人和亲友太多,存活率都比较低;
3.6 不同船舱类型的乘客获救情况¶
船舱的缺失值确实太多,有效值仅仅有204个,很难分析出不同的船舱和存活的关系,我们可以直接将该组特征丢弃掉,也可以简单地将数据分为是否有Cabin记录作为特征,将缺失数据归为一类,未缺失数据归为一类,一同跟与Survived进行分析;
is_null = data_train.Survived[data_train.Cabin.isnull()].value_counts()
not_null = data_train.Survived[data_train.Cabin.notnull()].value_counts()
df = pd.DataFrame({'为空':is_null, '非空':not_null}).transpose()
df.plot(kind='bar', stacked=True)
plt.title('按Cabin是否为空看获救情况')
plt.xlabel('Cabin是否为空')
plt.ylabel('人数')
plt.show()
data_train.Survived[data_train.Cabin.notnull()].value_counts(normalize=True)
data_train.Survived[data_train.Cabin.isnull()].value_counts(normalize=True)
可以看出,有cabin记录的乘客survival比例比无记录的高很多;
3.7 缺失值处理
- 这里只是为了进行探索性分析,具体更详细的数据处理见特征工程部分;
- 通常遇到缺值的情况,有下面几种处理方式:
- 如果缺值的样本占比较高,可以直接舍弃,以免作为特征加入,反倒带入噪声;
- 如果缺值的样本适中,而该属性非连续值特征属性(比如类目属性),那就把NaN作为一个新类别,加到类别特征中
- 如果缺值的样本适中,而该属性为连续值特征属性,可以尝试分桶处理;
- 当缺失的样本并不是特别多的时候,我们可以试着根据已有的值,拟合一下数据,补充上。
- Embarked(共有三个上船地点),缺失俩值,可以用众数填充;
- Cabin将缺失信息当做一个类目;
3.7.1 处理Embarked和数据
由于总共有1309条数据,Embarked只缺失两个,所以用众数填充即可
data_train.Embarked[data_train.Embarked.isnull() == True] = data_train.Embarked.dropna().mode().values
data_train['Cabin'] = data_train.Cabin.fillna('U0') # 先简单填充Cabin
3.7.2 处理Age数据
facet = sns.FacetGrid(data_train,hue="Survived",aspect=4)
facet.map(sns.kdeplot,'Age',shade=True)
facet.set(xlim=(0,data_train['Age'].max()))
facet.add_legend()
- 通常使用回归、随机森林等模型来预测缺失属性的值。
- 经过分析:Age在该数据集里是一个相当重要的特征,所以保证一定的缺失值填充准确率是非常重要的,对结果也会产生较大影响。
- 这里使用随机森林预测模型,选取数据集中的数值属性作为特征(因为sklearn的模型只能处理数值属性,所以这里先仅选取数值特征,但在实际的应用中需要将非数值特征转换为数值特征);
data_train.info()
from sklearn.ensemble import RandomForestRegressor
cols = ['Age', 'Survived', 'Pclass', 'SibSp', 'Parch', 'Fare']
age_df = data_train[cols]
X_train = age_df[age_df.Age.notnull()][cols[1:]]
y_train = age_df[age_df.Age.notnull()][cols[0]]
X_test = age_df[age_df.Age.isnull()][cols[1:]]
rfr = RandomForestRegressor(n_estimators=1000, n_jobs=-1) #n_estimators控制随机森林决策树的数量;n_jobs=-1会使用CPU的全部内核,大幅度提升速度
rfr.fit(X_train, y_train)
y_predict = rfr.predict(X_test)
data_train.loc[data_train.Age.isnull(),'Age'] = y_predict # 缺失值填充
data_train.info()
data_train.info()
3.8 不同年龄下的平均生存率
plt.figure(figsize=(18,6))
data_train['Age_int'] = data_train.Age.astype(int)
rate = data_train[['Age_int', 'Survived']].groupby('Age_int', as_index=False).mean()
sns.barplot(x='Age_int', y='Survived', data=rate)
plt.show()
data_train['Age'].describe()
从上图可以看出,训练样本共有891份,平均年龄在29.5岁,标准差为13.7岁,最小年龄0.42岁,最大年龄80岁
根据年龄,可以将乘客划分为儿童、少年、成年、老年,分析四个群体的生还情况;
bins = [0, 12, 18, 65, 100]
data_train['Age_group'] = pd.cut(data_train['Age'], bins)
age_group_survived_rate = data_train.groupby('Age_group')['Survived'].mean()
age_group_survived_rate
age_group_survived_rate.plot(kind='bar')
从图中可以看出,0到12岁儿童的存活率是最高的,达到了50%左右
其次是少年群体,在45%以上
最低的就属于65岁到100岁的老年群体,存活率最低,在12%左右
所以看得出来,不管在哪,孩子永远都是第一要保护的对象;
3.9 不同称呼的乘客生存情况
通过观察名字数据,我们可以看出其中包括对乘客的称呼,如:Mr、Miss、Mrs等,称呼信息包含了乘客的年龄、性别,同时也包含了入社会地位等的称呼,如:Dr,Lady,Major(少校),Master(硕士,主人,师傅)等的称呼。
data_train['Name']
#方法一:
data_train['Title'] = data_train['Name'].apply(lambda x : x.split(',')[1].split('.')[0].strip())
#方法二:
# data_train['Title'] =data_train['Name'].str.extract(' ([A-Za-z]+)\.',expand=False)
pd.crosstab(data_train['Title'],data_train['Sex'])
# 不同称呼的生存率
data_train[['Title', 'Survived']].groupby('Title').mean().plot(kind='bar')
从图中可以看出,不同称呼的乘客存活情况也不尽相同,存在显著差异
其中称呼为Lady、Mlle、Mme、Ms、Sir以及the countess的人群的存活率最高,均达到了100%的存活率;
3.10 票价分布与Survived的关系
# 票价分布情况
plt.figure(figsize=(10, 8))
data_train['Fare'].hist(bins=70)
data_train.boxplot(column='Fare', by='Pclass', showfliers=False)
data_train['Fare'].describe()
# 绘制survived与票价均值和方差的关系
fare_not_survived = data_train['Fare'][data_train['Survived'] == 0]
fare_survived = data_train['Fare'][data_train['Survived'] == 1]
average_fare = pd.DataFrame([fare_not_survived.mean(),fare_survived.mean()])
std_fare = pd.DataFrame([fare_not_survived.std(),fare_survived.std()])
average_fare.plot(yerr=std_fare,kind='bar',legend=False)
从图中可以看出,生存者的平均票价要大于未生还者的平均票价
4. 小结
- 根据以上探索性分析,我们可以猜测出一些结论:
- 舱位等级:舱位越高的获救的概率最大,并且随着等级的递减,比如一等舱就比二三等舱获救概率大;
- 性别:女性的获救率远远高于男性;
- 登船港口:C港口最高,Q次之,S港口最低;(需结合具体背景解释原因)
- 携带家人数量:独自一人和亲友太多,存活率都比较低;
- 客舱:缺失值较多,很难分析出有用信息,这里简单将缺失的看成一类,未缺失的看成一类;
- 年龄:0到12岁儿童存活率最高,在50%左右,其次是少年,最低的是65岁到100岁的老年群体;
- 称呼:不同称呼的乘客存活情况也不尽相同,存在显著差异;其中,Lady、Mlle、Mme、Ms、Sir以及the countess存活率均在100%;
- 票价:生存者的平均票价要大于未生还者的平均票价;
关注公众号:『AI学习星球
』
回复:泰坦尼克号获救预测
即可获取数据下载。
论文辅导
或算法学习
可以通过公众号滴滴我