Titantic乘客生还预测数据分析报告—基于python实现

Titantic乘客生还预测数据分析报告—基于python实现

1. 问题提出

Titantic数据集是Kaggle(www.kaggle.com)上的一个非常基本的数据集,其中记录了泰坦尼克号沉船事件中部分乘客的信息和生还记录。虽然在生存和死亡面前人人应该享有平等权利,但实际上,当灾难来临时,一些乘客往往会有着更高的生存机会,比如妇女、小孩和富人阶级。乘客所携带的特征是否能够影响,以及如何影响他们的生存机会,可以通过机器学习进行数据分析。

2. 数据描述

Titantic数据集包括训练集和测试集,但测试集没有提供乘客生还的标签信息,我们将Kaggle提供的训练集视为初始数据进行分析。

#导入模块
impor numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import BernoulliNB
from sklearn.linear_model import LinearRegression,LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
data=pd.read_csv('E:/Kaggle数据集/Titantic/train.csv')
data.info()  #获取数据信息
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

调用数据集的info方法,可知数据集中提供了12个字段,相关含义如下。

字段含义
PassengerId乘客Id
Survived乘客生还情况,1表示生还,0表示死亡
Pclass船舱等级
Name姓名
Sex性别
Age年龄
SibSp同乘兄弟姐妹/配偶数目
Parch乘同父母/小孩数目
Ticket票号
Fare票价
Cabin船舱编号
Embarked登船港口

这12个字段中,Age有177个缺失值,Cabin有687个缺失值,Embarked有2个缺失值,其他字段的信息是完整的。我们再调用describe方法获取数据中数值字段的统计信息。

data.describe()
PassengerIdSurvivedPclassAgeSibSpParchFare
count891.000000891.000000891.000000714.000000891.000000891.000000891.000000
mean446.0000000.3838382.30864229.6991180.5230080.38159432.204208
std257.3538420.4865920.83607114.5264971.1027430.80605749.693429
min1.0000000.0000001.0000000.4200000.0000000.0000000.000000
25%223.5000000.0000002.00000020.1250000.0000000.0000007.910400
50%446.0000000.0000003.00000028.0000000.0000000.00000014.454200
75%668.5000001.0000003.00000038.0000001.0000000.00000031.000000
max891.0000001.0000003.00000080.0000008.0000006.000000512.329200

可以发现,乘客的生还率只有38%左右,绝大多数乘客在海难中丧生。乘客的平均年龄在30岁左右,最小不到1岁,最大的达到80岁,而且大部分乘客的年龄低于38岁。另外,超过一半的乘客没有和他们的兄弟姐妹或配偶同乘,超过3/4的乘客没有和他们的父母和小孩同乘。乘客们的平均票价为32.2,而且大部分乘客的票价低于31,表明少数购买高价票的乘客提高了整体的消费水平,而大多数乘客的消费水平是较低的。

3. 特征独立分析

生还情况Survived是目标变量,而其它的字段是与乘客相关的原始的特征变量,我们可以独立分析每个特征和Survived变量的关系,来选择出能够影响乘客生还率的特征作为模型的自变量。

- PassengerId, Name, Ticket
乘客Id和名字是关于乘客的标识,票号是关于船票的标识,直观上可以判断这些标识与乘客的生还率无明显关系,所以PassengerId, Name和Ticket不考虑作为自变量。

- Pclass
Pclass表示船舱等级,有1,2,3三种取值,不同取值的数量如下。

data.Pclass.value_counts()  #统计Pclass字段中不同取值的数目
3    491
1    216
2    184
Name: Pclass, dtype: int64

可知等级为3的船舱对应乘客数最多,利用交叉表获取Pclass中不同取值和Survived取值之间的对应关系。

pd.crosstab(data.Pclass,data.Survived,normalize='index')
Survived	0	1
Pclass		
1	0.370370	0.629630
2	0.527174	0.472826
3	0.757637	0.242363

在图表中可以更直观的看到Pclass和Survived之间的关系。
Pclass特征下的存活率情况
可以发现在Pclass的不同取值下,乘客的存活率有着很大差别,当取1时,乘客的存货率最高,达到63.0%。船舱等级数字越小,相应的等级越高,这意味着富人阶级有更高的生存机会。所以Pclass可以s视为自变量来影响乘客的生还率。

- Sex
Sex分为男性和女性,不同性别对应的存活率和死亡率如下。

pd.crosstab(data.Sex,data.Survived,normalize='index') 
Survived	0	1
Sex		
female	0.257962	0.742038
male	0.811092	0.188908

Sex特征下的存活率情况
女性的生还率明显高于男性,达到74.2%,体现了女士优先的原则。Sex可作为自变量来影响乘客的生还率。

- Age
Titantic数据集中的Age可以视为连续变量,我们对其进行离散化处理,分为[0,10),[10,20),[20,40),[40,60),[60,100)五个区域,并标记为child,teenager,adult,middle(中年),old(老年)五个标签,再观察这五个标签上乘客的生还率情况。

new_age=pd.cut(data.Age,bins=[0,10,20,40,60,100],labels=['child','teenager','adult','middle','old'])  #Age中存在缺失值,后面会处理
pd.crosstab(new_age,data.Survived,normalize='index').plot(kind='bar',stacked='True',color='rg')

Age特征下的生还率情况
可以看到child的生还率较高,体现小孩优先的原则;teenager,adult和middle的生还率差别不大,而且都偏低;老人的生还率是最低的,说明灾难来临时老人的生存机会很小。总体上来说,Age对乘客生还率有一定的影响,可以将其视为自变量进行分析。

- SibSp
Sibsp表示与乘客同乘的兄弟姐妹或配偶的数量,取值分布如下。

data.SibSp.value_counts().plot(kind='bar',color='g',title='SibSp分布')

SibSp分布
可见SibSp有0,1,2,3,4,5,8七个取值,考虑到有些取值对应的乘客数目很小,将SibSp离散化为三个部分,0,{1,2},{3,4,5,8},观察这三种情况下的Survived的分布。

new_SibSp=pd.cut(data.SibSp,bins=[0,1,3,9],labels=['0','1-2','>=3'],include_lowest=True,right=False)  #分组
pd.crosstab(new_SibSp,data.Survived,normalize='index').plot(kind='bar',color='rg',stacked=1,width=0.4)

SibSp和Survived关系
可见具有1至2名同乘的SibSp的乘客有更高的生还率,因为灾难来临时家人的互相帮助能够提高彼此的生存机会,但是大于2名SibSp的乘客生还率很低,可能是由于基数太小而导致的偏差。总体上,SibSp能够影响乘客的生还率,视为自变量特征。

- Parch
Parch表示与乘客同乘的父母或子女的数量,取值分布如下。

data.Parch.value_counts().plot(kind='bar',color='g',title='Parch分布')

Parch分布
可见Parch有0,1,2,3,4,5,6七个取值,考虑到有些取值对应的乘客数目很小,将Parch离散化为三个部分,0,{1,2},{3,4,5,6},观察这三种情况下的Survived的分布。

new_Parch=pd.cut(data.Parch,bins=[0,1,3,7],labels=['0','1-2','>=3'],include_lowest=True,right=False)  #分组
pd.crosstab(new_Parch,data.Survived,normalize='index').plot(kind='bar',color='rg',stacked=1,width=0.4)

Parch与Survived关系
可见具有1至2名同乘的Parch的乘客有更高的生还率,但是大于2名Parch的乘客生还率很低,理由和SibSp变量分析类似。总体上,Parch也能够影响乘客的生还率,可视为自变量特征。

- Fare
Fare表示乘客的票价,属于连续变量,利用箱线图分别观察生还和死亡的乘客相应的Fare分布。

sur_fare=data.Fare[data['Survived']==1]
dead_fare=data.Fare[data['Survived']==0]
plt.boxplot((sur_fare,dead_fare),labels=['survive_fare','dead_fare'])
plt.title('不同生存情况的票价分布')
plt.grid()
plt.show()

不同生存情况的票价分布
很明显生存的乘客的bin高于死亡的乘客的bin,说明生存乘客的票价分布在一个较高的水平,这也意味着高Fare的乘客比低Fare的乘客有更大的生存机会,即富人优先。所以,票价能够作为自变量影响乘客的Survived结果。
- Cabin
Cabin表示乘客所在的船舱,该字段中存在大量的缺失项,我们可以认为缺失项传达一种信息,即作为非缺失项(有船舱)的对立面,表示乘客无船舱的信息,这种情况下我们将Cabin离散化为2个值,inCabin表示乘客有船舱,outCabin表示无船舱。此时Cabin字段和Survived的关系如下。

data.Cabin[pd.notnull(data.Cabin)]='inCabin'  
data.Cabin[pd.isnull(data.Cabin)]='outCabin'  #此时对原数据的Cabin字段进行了处理
pd.crosstab(data.Cabin,data.Survived,normalize='index')
Survived	0	1
Cabin		
inCabin	0.333333	0.666667
outCabin	0.700146	0.299854

Cabin特征下的生还率情况
可见有船舱的乘客生还率很高,达到66.7%,这可能是因为船舱内的乘客通常是那些消费水平比较高的富人阶级,即体现了富人优先。所以,在考虑有无船舱时,Cabin可以视为自变量特征影响乘客的生还率。

- Embarked
Embarked表示乘客的登船港口,有S,C,Q三种取值,不同取值和Survived关系分布如下。

pd.crosstab(data.Embarked,data.Survived,normalize='index').plot(kind='bar',color='rg',stacked=True,width=0.4)

Embarked和Survived关系
不同登船港口的生还率存在一定的差别,在C港登船的乘客有着更高的生存机会。实际上,Embarked特征对乘客的生还率不具备很好的解释性,我的理解是在C港登船的乘客之所以生还率高,可能是由于这些乘客中有着较多的小孩、女人或者富人阶级,也就是生还率的提高本质上是由其他特征所引起的,与登船港口并无明显关系。我们可以对该特征保留意见,后续通过模型来比较Embarked是否作为自变量的性能。

4. 数据处理

数据处理包括缺失值的填充,连续变量离散化,非数值变量数值化以及离散变量one_hot 编码。

- 缺失值填充
Age,Cabin和Embarked三个字段中存在缺失值。
对于Age,我们使用乘客年龄的平均值进行填充。

data.loc[pd.isnull(data.Age),'Age']=data.Age.mean()

对于Cabin,缺失值用‘outCabin’表示乘客不在不在船舱内,如第三部分所述。
对于Embarked,缺失值用众数进行填充。

data.Embarked.value_counts()
S    644
C    168
Q     77
Name: Embarked, dtype: int64
data.loc[pd.isnull(data.Embarked),'Embarked']='S'

- 连续变量离散化
实际上,除了对连续变量进行离散化之外,对于取值较多的离散变量我们也进行压缩取值范围的处理。
需要离散化的连续变量有Age和Fare。
对于Age,正如第三部分分析,我们将其分为child,teenager,adult,middle和old五个部分,代码如下:

data.Age=pd.cut(data.Age,bins=[0,10,20,40,60,100],labels=['child','teenager','adult','middle','old'])
data.Age.head(10)
0       adult
1       adult
2       adult
3       adult
4       adult
5       adult
6      middle
7       child
8       adult
9    teenager
Name: Age, dtype: category
Categories (5, object): [child < teenager < adult < middle < old]

对于Fare,我们以票价的平均值作为阈值分为lowFare和highFare两个部分,代码如下:

avefare=data.Fare.mean()
data.Fare=pd.cut(data.Fare,bins=[0,avefare,data['Fare'].max()],include_lowest=True,right=False,labels=['lowFare','highFare'])
data.Fare.head(10)
0     lowFare
1    highFare
2     lowFare
3    highFare
4     lowFare
5     lowFare
6    highFare
7     lowFare
8     lowFare
9     lowFare
Name: Fare, dtype: category
Categories (2, object): [lowFare < highFare]

另外,需要进一步划分的离散变量有SibSp和Parch,这在第三部分也进行了讨论。
对于SibSp,对于它的七个取值0,1,2,3,4,5,8,我们分成三个部分,0仍记为0,{1,2}记为1,其他的记为2,代码如下:

data.SibSp[(data['SibSp']>0)&(data['SibSp']<=2)]=1
data.SibSp[data['SibSp']>2]=2
data.SibSp.head(10)
0    1
1    1
2    0
3    1
4    0
5    0
6    0
7    2
8    0
9    1
Name: SibSp, dtype: int64

对于Parch,对于它的七个取值0,1,2,3,4,5,6,我们也分成三个部分,0仍记为0,{1,2}记为1,其他的记为2,代码如下:

data.Parch[(data['Parch']>0)&(data['Parch']<=2)]=1
data.Parch[data['Parch']>2]=2
data.Parch.head(10)
0    0
1    0
2    0
3    0
4    0
5    0
6    0
7    1
8    1
9    0
Name: Parch, dtype: int64

- 非数值变量数值化
非数值变量有Sex,Fare,Cabin和Embarked。
对于Sex,包括male和female,考虑到female的生还率高,我们用0和1来分别标记male和female。

data.Sex=data.Sex.map({'male':0,'female':1})
data.Sex.head()
0    0
1    1
2    1
3    1
4    0
Name: Sex, dtype: int64

对于Fare,包括lowFare和highFare,考虑到highFare相应的乘客的生还率高,我们用0和1分别标记lowFare和highFare。

data.Fare=data.Fare.map({'lowFare':0,'highFare':1})
data.Fare.head()
0    0
1    1
2    0
3    1
4    0
Name: Fare, dtype: category
Categories (2, int64): [0 < 1]

对于Cabin,包括inCabin和outCabin,考虑到inCabin相应的乘客生还率高,我们用0和1分别标记outCabin和inCabin。

data.Cabin=data.Cabin.map({'outCabin':0,'inCabin':1})
data.Cabin.head()
0    0
1    1
2    0
3    1
4    0
Name: Cabin, dtype: int64

对于Embarked,因为它有三个取值,无法进行0-1编码,我们会在下一步对它进行one_hot 编码实现数值化。

- 离散变量one_hot 编码
经过上述一系列处理后,被选择的特征均已实现离散化,对于取值超过两个的变量,我们通过one_hot 编码为这些变量创建虚拟变量,使得每个特征的取值仅为0和1。这些变量有Pclass,Age,SibSp,Parch和Embarked。

dummy_data=pd.get_dummies(data,columns=['Pclass','Age','SibSp','Parch','Embarked'])
dummy_data.columns
Index(['PassengerId', 'Survived', 'Name', 'Sex', 'Ticket', 'Fare', 'Cabin',
       'Pclass_1', 'Pclass_2', 'Pclass_3', 'Age_child', 'Age_teenager',
       'Age_adult', 'Age_middle', 'Age_old', 'SibSp_0', 'SibSp_1', 'SibSp_2',
       'Parch_0', 'Parch_1', 'Parch_2', 'Embarked_C', 'Embarked_Q', 'Embarked_S'],
       dtype='object')

5. 模型评估

应用于乘客生还率分析的自变量字段有Pclass,Sex,Age,SibSp,Parch,Fare,Cabin以及Embarked(保留的特征,要通过模型验证其有效性),其中我们对Pclass,Age,SibSp,Parch和Embarked创建虚拟变量,另外,Survived是标签字段。我们可以将这些有效字段的数据提取出来,方便后续实验。

valid_data=dummy_data.loc[:,[ 'Sex', 'Fare', 'Cabin',
       'Pclass_1', 'Pclass_2', 'Pclass_3', 'Age_child', 'Age_teenager',
       'Age_adult', 'Age_middle', 'Age_old', 'SibSp_0', 'SibSp_1', 'SibSp_2',
       'Parch_0', 'Parch_1', 'Parch_2', 'Embarked_C', 'Embarked_Q', 'Embarked_S','Survived']]

按照4:1的比例将有效数据分为训练集和测试集。

X=valid_data.iloc[:,:-1]   #特征信息
y=valid_data.iloc[:,-1]   #标签信息
x_train,x_test,y_train,y_test=train_test_split(X,y,test_size=0.2)   

我们使用一些常见的分类模型在处理后的数据集上进行乘客生还预测,并通过模型得分比较不同模型的性能。这些分类模型包括最近邻算法(knn),线性回归(linearRegression),逻辑回归(logisticRegression),伯努利朴素贝叶斯(BernoulliNB),决策树(decisionTree),支持向量机(SVM)和随机森林(randomForest)。当然,这些模型能够在自身不同的参数水平下获得不同的得分,此处我们使用sklearn库中关于这些模型的参数默认值来进行分类,以了解不同模型的性能概况。

models=[('knn',KNeighborsClassifier()),('线性回归',LinearRegression()),('逻辑回归',LogisticRegression()),
('伯努利朴素贝叶斯',BernoulliNB()), ('决策树',DecisionTreeClassifier()),('SVM',SVC()),
('随机森林',RandomForestClassifier())]
for model,clf in models:
    clf.fit(x_train,y_train)
    score=clf.score(x_test,y_test)
    print(model,':',score)
knn : 0.8268156424581006
线性回归 : 0.34022115233339967
逻辑回归 : 0.8044692737430168
伯努利朴素贝叶斯 : 0.7486033519553073
决策树 : 0.7988826815642458
SVM : 0.8156424581005587
随机森林 : 0.8100558659217877

意外的是,最近邻算法knn在处理后的数据集上实现了最佳的预测性能,达到0.826。不考虑Embarked这个特征,我们再来测试一下各个模型的性能。
首先从训练集和测试集的特征数据中移除Embarkd相关的三个虚拟变量,即Embarked_C,Embarked_Q和Embarked_S,代码如下:

x_train=x_train.loc[:,[i for i in x_train.columns if i not in ['Embarked_C','Embarked_Q','Embarked_S']]]
x_test=x_test.loc[:,[i for i in x_train.columns if i not in ['Embarked_C','Embarked_Q','Embarked_S']]]
x_test.columns
Index(['Sex', 'Fare', 'Cabin', 'Pclass_1', 'Pclass_2', 'Pclass_3', 'Age_child',
       'Age_teenager', 'Age_adult', 'Age_middle', 'Age_old', 'SibSp_0',
       'SibSp_1', 'SibSp_2', 'Parch_0', 'Parch_1', 'Parch_2'],
      dtype='object')

此时再调用各个分类模型:

for model,clf in models:
    clf.fit(x_train,y_train)
    score=clf.score(x_test,y_test)
    print(model,':',score)
knn : 0.8268156424581006
线性回归 : 0.3560219278522567
逻辑回归 : 0.8156424581005587
伯努利朴素贝叶斯 : 0.7374301675977654
决策树 : 0.8100558659217877
SVM : 0.8156424581005587
随机森林 : 0.8379888268156425

最近邻算法knn仍然能够实现最高的预测准确性,而且相较于考虑Embarked这个特征时,预测性能得分没有发生改变,这种情况对于SVM也是如此。此外,在不考虑Embarked这个特征时,除了伯努利朴素贝叶斯的性能略有下降之外,线性回归、逻辑回归、决策树和随机森林模型的性能都有所提升。总的来说,Embarked作为一个解释性不足的特征,对于模型预测能力的提升没有太大改善,相反会干扰对乘客生还率的预测,应该舍弃该特征。
以上就是关于Titantic乘客生还率的一个比较基础的数据分析过程,当然无论是在特征的进一步提取和处理上,还是在模型参数的调整上,关于乘客生还预测性能的提高还有着很大的进步空间,值得我们去细致的探讨和分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值