用Python计算泰坦尼克号生还者年龄段?Rose是你吗?

大家熟知的电影《泰坦尼克号》,是一部经典的奥斯卡电影,也是一部以真实故事改编而拍的电影,真实故事中,1912年4月14日,这艘当时世界上体积最庞大、内部设施最豪华的客运轮船泰坦尼克号,与一座冰山相撞,2224名船员及乘客中,逾1500人丧生,其中仅333具罹难者遗体被寻回。

时隔一个世纪之久,如果用编程的角度,来审视这场灾难,会有什么发现呢?今天的文章,正是用编程来研究泰坦尼克号的生还者情况。


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


关注,转发,私信小编“资料”即可领取数十套PDF 文档!

用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


一、数据预处理


数据初识

这里主要是对数据进行介绍,下载的数据集分为train与test以及gender_submission,分别是训练集,测试集以及生成提交文件的参考文件。

train与test各列分别为:

PassengerId 乘客ID
Pclass 客舱等级(1/2/3等舱位)
Name 乘客姓名
Sex 性别
Age 年龄
SibSp 兄弟姐妹数或配偶数
Parch 父母数或子女数
Ticket 船票编号
Fare 船票价格
Cabin 客舱号
Embarked 登船港口

导包

import seaborn as sns
from matplotlib import pyplot as plt

import pandas as pd
%matplotlib inline
import warnings
from sklearn.model_selection import train_test_split
warnings.filterwarnings('ignore')

数据导入

这里将训练集与测试集导入,并通过concat将数据整合一块,做个缺失值分析处理。

tannike_train=pd.read_csv('./train.csv')
tannike_test=pd.read_csv('./test.csv')
join_data=pd.concat([tannike_train,tannike_test],ignore_index=True)

数据预处理及特征工程

统计缺失值的代码如下:

join_data.isna().sum()

结果:

Age 263
Cabin 1014
Embarked 2
Fare 1
Name 0
Parch 0
PassengerId 0
Pclass 0
Sex 0
SibSp 0
Survived 418
Ticket 0
dtype: int64

从这个结果中我们发现:

Age与Cabin以缺失值较多,而Fare与Embarked缺失值较少,可以考虑用中位数,均值或者众数等方式解决。

缺失值处理

fare_mean = tannike_train['Fare'].mean()
tannike_test.loc[pd.isnull(tannike_test.Fare),'Fare'] = fare_mean
embarked_mode = tannike_train['Embarked'].mode()
tannike_train.loc[pd.isna(tannike_train.Embarked),['Embarked']] = embarked_mode[0]
tannike_train.loc[pd.isna(tannike_train.Age),['Age']] = tannike_train['Age'].mean()
tannike_test.loc[pd.isna(tannike_test.Age),['Age']] = tannike_test['Age'].mean()

查看缺失值,以Embarked为例:

tannike_train.Embarked.isna().sum()

输出为0,则表示缺失值被处理完毕!

数据划分

这里直接划分成训练集与测试集的原因是对比与label数据的误差度。

label = tannike_train['Survived']
tannike_train.drop('Survived',axis=1,inplace=True)
X_train,X_test,Y_train,Y_test = train_test_split(tannike_train,label,test_size = 0.3,random_state = 1)
X_train['Survived'] = Y_train
X_test['Survived'] = Y_test

切分数据为训练集,测试集!

性别与生存

我们先来看一下性别与生存关系:

sex_Sur = pd.crosstab(tannike_train.Sex,tannike_train.Survived)
sex_Sur.rename(columns={0.0:'dead',1.0:'survived'},inplace=True)
sex_Sur

输出:

Survived dead survived
Sex
female 81 233
male 468 109

数据可视化:

# stacked属性设置多个柱状图是否叠加
sex_Sur.plot.bar(stacked=True,color=['#f441f1','#b6f442'])
plt.xticks(rotation=0,size='x-large')
plt.xlabel('')


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


条状图转为饼图

sex_Sur.plot.pie(subplots=True,figsize=(10,5),colors=('#b6f442','#41d0f4'))


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


综合上述两图,我们发现女性的存活率要比男性高!Age可以作为一个重要特征!

特征工程处理:将数据转为0,1数据。

tannike_train['Sex'] = tannike_train['Sex'].apply(lambda x: 1 if x == 'male' else 0)
tannike_test['Sex'] = tannike_test['Sex'].apply(lambda x: 1 if x == 'male' else 0)
# one-hot编码
tannike_train = pd.get_dummies(data= tannike_train,columns=['Sex'])
tannike_test = pd.get_dummies(data= tannike_test,columns=['Sex'])

年龄与存活

研究年龄与存活关联:

survived_age=tannike_train[tannike_train.Survived==1]['Age']
dead_age=tannike_train[tannike_train.Survived==0]['Age']
age_frame=pd.concat([survived_age,dead_age],axis=1)
age_frame.columns=['Survived','Dead']
age_frame.head()

输出数据(前5行数据):

 Survived Dead
0 NaN 22.0
1 38.0 NaN
2 26.0 NaN
3 35.0 NaN
4 NaN 35.0

可视化:

# 为避免颜色覆盖,使用alpha通道属性
age_frame.plot(kind='hist',bins=30,alpha=0.3,figsize=(10,6))


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


上图发现:

年龄低于5岁的,存活率相对较高!而在75岁以后存活率也高,在中间的15到35之间存活率相对较高,其他的就比较低了,我们可以在做特征工程时候,对其做年龄划分处理,比如:低中高等。

特征工程处理:

tannike_train['Small_Age'] = np.int32(tannike_train['Age'] <= 5) 
tannike_train['Old_Age'] = np.int32(tannike_train['Age'] >= 65)
tannike_train['Middle_Age'] = np.int32((tannike_train['Age'] >= 15) & (tannike_train['Age'] <= 25))
tannike_test['Small_Age'] = np.int32(tannike_test['Age'] <= 5)
tannike_test['Old_Age'] = np.int32(tannike_test['Age'] >= 65)
tannike_test['Middle_Age'] = np.int32((tannike_test['Age'] >= 15) & (tannike_test['Age'] <= 25))

名字与存活

首先来统计一下各个名字开头的数量,比如Ms开头的数量:

# 比如名字为Attalah, Miss. Malake,我们提取出目的是Miss,运用两次lambda
X_train['Name_Title'] = X_train['Name'].apply(lambda x: x.split(',')[1]).apply(lambda x: x.split()[0])
X_test['Name_Title'] = X_test['Name'].apply(lambda x: x.split(',')[1]).apply(lambda x: x.split()[0])
X_train.groupby('Name_Title')['Survived'].count()

输出:

Name_Title
Capt. 1
Col. 2

Don. 1
Dr. 4
Lady. 1
Major. 1
Master. 27
Miss. 126
Mlle. 1
Mme. 1
Mr. 365
Mrs. 87
Rev. 5
the 1
Name: Survived, dtype: int64

交叉数据:训练与测试集可视化对比差异:

# 条形图上的误差棒则表示各类的数值相对于条形图所显示的值的误差
# seaborn的barplot()利用矩阵条的高度反映数值变量的集中趋势,以及使用errorbar功能(差棒图)来估计变量之间的差值统计
fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,5))
sns.barplot('Name_Title', 'Survived', data=X_train.sort_values('Name_Title'), ax=axis1)
sns.barplot('Name_Title', 'Survived', data=X_test.sort_values('Name_Title'), ax=axis2)

下面这个图显示的数据变量的集中趋势!


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


对比这个数据,得出如下结论:对于不同的名字开头,他的生存率不同,这里根据存活率平均程度高低依次下分。

特征工程处理:通过定义一个函数来对名字的存活性高低,进行详细排序:

def Name_Title_Code(x):
if x == 'Mr.':
return 1
if (x=='Ms.') or (x=='Lady.') or (x == 'Mlle.') or (x =='Mme.') or (x == 'the.') or (x =='Sir.') or (x=='Major'):
return 2
if (x == 'Mrs.'):
return 3
if x == 'Miss.':
return 4
if x == 'Master.':
return 5
if x == 'Dr.':
return 6
return 7

对真正的训练集与测试集进行one-hot编码:

tannike_train['Name_Title'] = tannike_train['Name'].apply(lambda x: x.split(',')[1]).apply(lambda x: x.split()[0])
tannike_test['Name_Title'] = tannike_test['Name'].apply(lambda x: x.split(',')[1]).apply(lambda x: x.split()[0])
tannike_train['Name_Title'] = tannike_train['Name_Title'].apply(Name_Title_Code)
tannike_test['Name_Title'] = tannike_test['Name_Title'].apply(Name_Title_Code)
tannike_train = pd.get_dummies(columns = ['Name_Title'], data = tannike_train)
tannike_test = pd.get_dummies(columns = ['Name_Title'], data = tannike_test)

船票编号与存活

获取船票第一个字母:

def Ticket_First_Let(x):
return x[0]

交叉数据集:训练与测试集处理:

X_train['Ticket_First_Letter'] = X_train['Ticket'].apply(Ticket_First_Let)
X_test['Ticket_First_Letter'] = X_test['Ticket'].apply(Ticket_First_Let)

可视化船票编号与存活关系:

fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,5))
sns.barplot('Ticket_First_Letter', 'Survived', data=X_train.sort_values('Ticket_First_Letter'), ax=axis1)
sns.barplot('Ticket_First_Letter', 'Survived', data=X_test.sort_values('Ticket_First_Letter'), ax=axis2)


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


同理,我们发现票的前面数字的编号对生存也是有影响!这里对不同的票编号封装成函数,进行处理!

def Ticket_First_Letter_Code(x):
if (x == 'F'):
return 1
if x == '1' or x=='P' or x=='9':
return 2
if x == '2':
return 3
if x == 'C':
return 4
if x == 'S':
return 5
if x == '6':
return 6
if x == '7':
return 7
if x == 'A':
return 8
if x == 'W':
return 9
return 10

真实训练集与测试集处理:

one-hot编码处理:

tannike_train['Ticket_First_Letter'] = tannike_train['Ticket'].apply(Ticket_First_Let)
tannike_test['Ticket_First_Letter'] = tannike_test['Ticket'].apply(Ticket_First_Let)
tannike_train['Ticket_First_Letter'] = tannike_train['Ticket_First_Letter'].apply(Ticket_First_Letter_Code)
tannike_test['Ticket_First_Letter'] = tannike_test['Ticket_First_Letter'].apply(Ticket_First_Letter_Code)
tannike_train = pd.get_dummies(columns = ['Ticket_First_Letter'], data = tannike_train)
tannike_test = pd.get_dummies(columns = ['Ticket_First_Letter'], data = tannike_test)

客舱号与生存

Cabin表示客舱号。首先我们来对数据缺失值,进行填充。将缺失值设为NA。

X_train['Cabin'] = X_train['Cabin'].fillna('NA')
X_test['Cabin'] = X_test['Cabin'].fillna('NA')

其次定义一个方法:用于获取客舱号的第一个字母或数字。

def Cabin_First_Letter(x):
if x == 'NA':
return 'NA'
return x[0]

将交叉验证集数据进行上述方法操作。

sns.barplot('Cabin_First_Letter', 'Survived', data=X_train.sort_values('Cabin_First_Letter'), ax=axis1) 


X_train['Cabin_First_Letter'] = X_train['Cabin'].apply(Cabin_First_Letter)
X_test['Cabin_First_Letter'] = X_test['Cabin'].apply(Cabin_First_Letter)

统计客舱号中第一个字母的出现次数:

X_train.groupby('Cabin_First_Letter')['Survived'].count()

结果:

Cabin_First_Letter
A 12
B 28
C 41
D 21
E 22
F 8

G 3
NA 488
Name: Survived, dtype: int64

接着,数据处理完了,我们是时候可视化数据,并确定该特征值与生存的关系!

fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,5))
sns.barplot('Cabin_First_Letter', 'Survived', data=X_train.sort_values('Cabin_First_Letter'), ax=axis1)
sns.barplot('Cabin_First_Letter', 'Survived', data=X_test.sort_values('Cabin_First_Letter'), ax=axis2)


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


这里采用Seaborn的barplot绘制柱状图,图中的竖线代表误差棒!图显示的是每一个客舱号第一位数字或字母出现的集中分布趋势!

针对上述这个图,我们得出如下结论:

存活率排序:E>D>>B>C>A>.....

然后定义一个方法,进行封装,依次返回数值!

def Cabin_First_Letter_Code(x):
if x == 'E':
return 1
if x == 'D':
return 2
if x == 'B':
return 3
if x == 'C':
return 4
if x == 'A':
return 5
return 6

然后根据上述的交叉验证数据集的结果,应用到我们的测试集与训练集上。

tannike_train['Cabin'] = tannike_train['Cabin'].fillna('NA')
tannike_test['Cabin'] = tannike_test['Cabin'].fillna('NA')
tannike_train['Cabin_First_Letter'] = tannike_train['Cabin'].apply(Cabin_First_Letter)
tannike_test['Cabin_First_Letter'] = tannike_test['Cabin'].apply(Cabin_First_Letter)

由于当前研究的这个数据为等级资料数据,我们这里用one-hot编码可有效的处理问题:

tannike_train['Cabin_First_Letter'] = tannike_train['Cabin_First_Letter'].apply(Cabin_First_Letter_Code)
tannike_test['Cabin_First_Letter'] = tannike_test['Cabin_First_Letter'].apply(Cabin_First_Letter_Code)
tannike_train = pd.get_dummies(columns = ['Cabin_First_Letter'], data = tannike_train)
tannike_test = pd.get_dummies(columns = ['Cabin_First_Letter'], data = tannike_test)

登陆港口与生存

由于登录港口数据已经在前一节的缺失值处理掉了,这里便可以直接对交叉验证集数据可视化:

fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,5))
sns.barplot('Embarked', 'Survived', data=X_train.sort_values('Embarked'), ax=axis1)
sns.barplot('Embarked', 'Survived', data=X_test.sort_values('Embarked'), ax=axis2)


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


然后我们对其进行生存排序:C>Q>S,由于只有三类,直接对训练集与测试集进行one-hot编码即可!

tannike_train = pd.get_dummies(tannike_train,columns = ['Embarked'])
tannike_test = pd.get_dummies(tannike_test,columns = ['Embarked'])

Pclass客舱等级(1/2/3等舱位)同Embarked操作,这个就不阐述了!

兄弟姐妹与生存

SibSp:兄弟姐妹数或配偶数,Parch:父母数或子女数,将其与Parch 合并处理!

X_train['Fam_Size'] = X_train['SibSp'] + X_train['Parch'] 
X_test['Fam_Size'] = X_test['SibSp'] + X_test['Parch']

可视化:

fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,5))
sns.barplot('Fam_Size', 'Survived', data=X_train.sort_values('Parch'), ax=axis1)
sns.barplot('Fam_Size', 'Survived', data=X_test.sort_values('Parch'), ax=axis2)


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?


特征处理:分为三类:Solo,Nuclear,Big,并对训练集与测试集进行one-hot编码!

import numpy as np
def Family_feature(train, test):
for i in [train, test]:
i['Fam_Size'] = np.where((i['SibSp']+i['Parch']) == 0 , 'Solo',
np.where((i['SibSp']+i['Parch']) <= 3,'Nuclear', 'Big'))
del i['SibSp']
del i['Parch']
return train, test
tannike_train, tannike_test = Family_feature(tannike_train, tannike_test)
tannike_train = pd.get_dummies(tannike_train,columns = ['Fam_Size'])
tannike_test = pd.get_dummies(tannike_test,columns = ['Fam_Size'])

Fare同理操作!




二、特征选择


这里以全集特征训练!删除某一特征训练结果测试。

X_train_ = tannike_train.loc[X_train.index]
X_test_ = tannike_train.loc[X_test.index]
Y_train_ = label.loc[X_train.index]
Y_test_ = label.loc[X_test.index]
X_test_.head(3)

X_test_ = X_test_[X_train_.columns]
X_test_.head(3)
tannike_test = tannike_test[tannike_train.columns]
tannike_test.head()




三、模型选择


xgboost

这里选用xgboost进行测试,下面是选择的最优情况的代码,线下测试:0.8097014925373134,线上测试0.80621!

import xgboost as xgb
from sklearn.preprocessing import LabelEncoder
gbm = xgb.XGBClassifier(max_depth=3, n_estimators=300, learning_rate=0.06,scoring='r3', cv=10, verbose=1, n_jobs=4,)
gbm.fit(X_train_, Y_train_)
gbm.score(X_test_,Y_test_)

随机森林

线下测试:0.8022388059701493,线上测试0.80621!

rf_ = RandomForestClassifier(random_state = 10, warm_start = True, 
n_estimators = 900,
max_depth = 6,
max_features = 'sqrt',min_samples_leaf=1)

rf_.fit(X_train_, Y_train_)
rf_.score(X_test_,Y_test_)




四、结果上传


首先通过上述建立的模型进行预测,并输出到上传文件中,然后再上传 !

s = pd.read_csv('./gender_submission.csv')
predictions = p.predict(tannike_test) # p为模型分类器
submission = pd.DataFrame({"PassengerId": s.PassengerId, "Survived": predictions.astype(np.int32)})
submission.to_csv("submission-pipe.csv", index=False)


用Python计算泰坦尼克号生还者年龄段?Rose是你吗?



分享到:


相關文章: