美文网首页数据分析
Python数据分析(九):泰坦尼克号获救分析预测

Python数据分析(九):泰坦尼克号获救分析预测

作者: 风之舟 | 来源:发表于2020-03-05 17:23 被阅读0次

    好久没更新了,由于疫情的原因,家里的工作一直比较忙,最近闲下来了,学校也还没开学,正好趁着这段时间,复习一下前段时间的知识,泰坦尼克号的案例在数据分析中是比较经典的,今天我们就借助这个案例看一下数据分析的流程是怎么样的。

    一、数据集

    首先我们先来看一下数据集,不要嘲笑我的中文路径(哈哈哈),当初整理文件的时候嫌麻烦,直接上中文了,不过这里还是提醒一下大家,路径尽量不要带中文,以免有些代码不支持。

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    plt.style.use('fivethirtyeight')    # 样式美化
    import warnings
    warnings.filterwarnings('ignore')
    %matplotlib inline
    data = pd.read_csv('../../数据集汇总/泰坦尼克号数据集/titanic_train.csv')
    data.head()
    

    这里我们分别看一下每个列名代表的含义:

    • PassengerId: 乘客id
    • Survived:是否获救,0表示未获救,1表示获救
    • Pclass:乘客等级(1/2/3等舱位)
    • Name:乘客姓名
    • Sex:性别
    • Age:年龄
    • SlbSp:堂兄弟/妹个数
    • Parch:父母与小孩个数
    • Ticket:船票信息
    • Fare:票价
    • Cabin:客舱
    • Embarked:登船港口

    数据集相对来说比较简单,想要的特征基本上都给出来,简单明了。

    二、数据分析

    数据集已经知道各个字段的含义,接下来我们要进行数据分析,看一下各个特征对结果的影响,统计绘图得出结论。

    data.isnull().sum()
    

    先来看一下,有没有缺失值,通过上图可以看出,Age(年龄)、Cabin(客舱)和Embarked(登船港口)这三列存在缺失值,其中Cabin(客舱)的缺失值相对较多,由于这一列本身价值不高,后期我们会舍去。

    data.describe()
    

    我们看一下具体的数据分布,通过data.describe()可以看到数据一共是891行,还可以看到一些列的最大最小值,比如年龄这一列,年龄最大的有80岁,最小的还不到一岁。

    接下来,我们画图看一下,

    fig,ax = plt.subplots(1,2,figsize=(18,8))
    # data['Survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.1f%%',shadow=True,ax=ax[0])
    ax[0].pie(data['Survived'].value_counts(),explode=[0,0.1],autopct='%1.1f%%',shadow=True,labels=[0,1])
    ax[0].set_title('Survived')
    ax[0].set_ylabel('')
    sns.countplot('Survived',data=data,ax=ax[1])
    ax[1].set_title('Survived')
    plt.show()
    

    通过饼图可以看出来,获救的占比才38.4%,一共有891个人,获救的才300多一点,说明救援率还是比较低的。

    接下来我们对各个特征进行单独分析,看一下各个特征对结果的影响情况。

    • 1、性别特征分析
    data.groupby(['Sex','Survived'])['Survived'].count()
    

    根据上面的统计分析可以看出,女性被救出来的几率非常大,我们还可以画图看一下,

    fig,ax = plt.subplots(1,2,figsize=(10,8))
    data[['Sex','Survived']].groupby(['Sex']).mean().plot.bar(ax = ax[0])
    ax[0].set_title('Survived vs Sex')
    sns.countplot(x='Sex',hue='Survived',data=data,ax=ax[1])
    ax[1].set_title('Sex:Survived vs Dead')
    plt.show
    

    通过左图可以看出,如果是女人,在所有的女人中有70%多的几率会被救,而男人只有20%不到(嗯...深刻的感受到了做男人的不易~),右图可以看出女性被救的人数比男性的两倍还要多。通过分析发现,性别是一个重要的特征。
    • 2、船舱等级特征分析
    pd.crosstab(data.Pclass,data.Survived,margins=True).style.background_gradient(cmap='summer_r')
    

    通过表格我们可以看出一等舱一共216人,有136人被救,占比高于50%;二等舱有184人,87人被救,占比在50%左右;三等舱一共491人,只有119人被救,占比最低。虽然我们讲生命不分贵贱,但是现实还是比较残酷的,有钱人被救的几率还是远高于穷人。

    我们画图看一下,

    fig,ax=plt.subplots(1,2,figsize=(10,8))
    data['Pclass'].value_counts().plot.bar(color=['green','blue','yellow'],ax=ax[0])
    ax[0].set_title('Number Of Passengers By Pclass')
    sns.countplot('Pclass',hue='Survived',data=data,ax=ax[1])
    ax[1].set_title('Pclass:Survived vs Dead')
    plt.show()
    

    两图对比更加明显,一等舱被救的人数高于未被救的人数,二等舱差不多,三等舱被救的人远低于未被救的人数。
    我们再来看一下性别和船舱等级与被救之间的关系,

    pd.crosstab([data.Sex,data.Survived],data.Pclass,margins=True).style.background_gradient(cmap='summer_r')
    

    通过上表可以看出,在一等舱里,女性被救的比例非常高,女性一共有94人,91人被救。
    我们画图看一下男性与女性在各个船舱被救的比例,

    sns.factorplot('Pclass','Survived',hue='Sex',data=data)
    plt.show()
    

    通过上图可以看明显看出,三个船舱中,女性被救的比例远高于男性。(心疼自己一秒钟...)

    • 3、年龄特征分析
      先来看一下年龄的数值特征,
    print('最大年龄',data['Age'].max())
    print('最小年龄',data['Age'].min())
    print('平均年龄',data['Age'].mean())
    # 输出
    最大年龄 80.0
    最小年龄 0.42
    平均年龄 29.69911764705882
    

    接下来画图分析一下,船舱等级和年龄、性别和年龄之间被救的关系,

    fig,ax = plt.subplots(1,2,figsize=(10,8))
    sns.violinplot('Pclass','Age',hue='Survived',data=data,split=True,ax=ax[0])
    ax[0].set_title('Pclass and Age vs Survived')
    ax[0].set_yticks(range(0,110,10))
    sns.violinplot('Sex','Age',hue='Survived',data=data,split=True,ax=ax[1])
    ax[1].set_title('Sex and Age vs Survived')
    ax[1].set_yticks(range(0,110,10))
    plt.show()
    

    通过左图,可以分析出,一等舱被救的人中大部分集中在37、8岁左右,二等舱被救的人大部分集中在30岁,三等舱集中在20 ~ 30岁左右,可以看出一等舱被救的年龄比其他两个舱的要大,有可能是一等舱本身就是比较富有的人,一般都是拼搏到了一定的年龄才会这样(瞎猜的);右图的话,可以看出男性被救的年龄在30岁左右,女性被救的20~30岁左右,差距不是很大。

    • 4、登船地点特征分析
    pd.crosstab([data.Embarked,data.Pclass],[data.Sex,data.Survived],margins=True).style.background_gradient(cmap='summer_r')
    

    通过图表可以看出,S登船地的人数最多,Q最少。

    sns.factorplot('Embarked','Survived',data=data)
    fig=plt.gcf()
    fig.set_size_inches(5,3)
    

    通过上图可以看出,C港的生存率最高,在0.55左右,而S港的生存率最低。(莫名的想起吃鸡~)

    fig,ax=plt.subplots(2,2,figsize=(15,15))
    sns.countplot('Embarked',data=data,ax=ax[0][0])
    ax[0][0].set_title('No. Of Passengers Boarded')
    sns.countplot('Embarked',hue='Sex',data=data,ax=ax[0][1])
    ax[0][1].set_title('Male-Femal Split for Embarked')
    sns.countplot('Embarked',hue='Survived',data=data,ax=ax[1][0])
    ax[1][0].set_title('Embarked vs Survived')
    sns.countplot('Embarked',hue='Pclass',data=data,ax=ax[1][1])
    ax[1][1].set_title('Embarked vs Pclass')
    plt.subplots_adjust(wspace=0.2,hspace=0.5)
    plt.show()
    

    左上,登船地点的人数统计,可以明显看出S港最多;右上,各个登船地点的男女人数统计;左下,各个登船地点获救与未获救的人数统计,结合上表,S港的获救人数最高,但是获救占比却是最低的;右下,各个登船地点的船舱等级人数统计,可以看出S港登船的人数最多,其中三等舱的人数最多,Q港几乎没有一等舱的人登船。

    sns.factorplot('Pclass','Survived',hue='Sex',col='Embarked',data=data)
    plt.show()
    

    上图是各个登船地点,每个船舱等级被救的男女比例,总之完全符合女人和孩子第一的政策。

    • 5、堂兄弟/妹特征分析
    pd.crosstab(data.Survived,data.SibSp,margins=True).style.background_gradient(cmap='summer_r')
    
    plt.figure(figsize=(8,8))
    sns.countplot('SibSp',hue='Survived',data=data)
    plt.show()
    

    通过上图表可以看出,堂兄弟/妹个数越多,基数越少,被救的也就越少,所以我们就简单分析一下,保留这个特征。

    • 6、父母与小孩个数特征分析
    pd.crosstab(data.Survived,data.Parch,margins=True).style.background_gradient(cmap='summer_r')
    
    plt.figure(figsize=(8,8))
    sns.countplot('Parch',hue='Survived',data=data)
    plt.show()
    

    这个特征与堂兄弟/妹个数的特征相似,父母孩子个数越多,基数越少,被救的人数也越少。比如有父母或孩子4、5个的人很少,都没有被救,这个特征我们也保留。

    以上就是我们对各个特征之间的理解,还有几个特征比如Ticket(船票信息)、Cabin(客舱)等我们没有进行分析,主要原因是船票信息对是否获救意义不大,而客舱Cabin缺失值较多,这些信息在接下来的数据处理中我们直接删掉了。

    三、数据清洗与预处理

    1、缺失值填充

    首先我们先处理一下缺失值,通过上面的data.isnull().sum()我们知道有两个特征值存在缺失的情况(Cabin不考虑)。

    • Age年龄问题
      关于年龄填充,我们一般是选择年龄的平均值进行填充,但是这里我们发现,在Name这一列,名字都比较长,中间会带有Mr,Miss,Mrs等特征来标识每一个人,我们可以根据这个特点,求出每个标识中,年龄的平均值进行填充,这样填充的效果会更加实际。
    # 先用正则表达式进行提取
    data['initial'] = 0
    for i in data:
        data['initial']=data.Name.str.extract('([A-Za-z]+)\.')
    data.head()
    

    接下来我们看一下分出来的男女人数,

    pd.crosstab(data.initial,data.Sex).T.style.background_gradient(cmap='summer_r')
    

    我们将人数比较多的提取出来,剩下的用others代替,
    data['initial']=data['initial'].replace(['Capt','Col','Countess','Don','Dr','Jonkheer','Lady','Major','Master','Mlle','Mme','Ms','Rev','Sir'],'others')
    

    接下来,我们求出每个标识的平均年龄,

    data.groupby('initial')['Age'].mean()
    

    最后我们可以填充了,

    data.loc[(data.Age.isnull())&(data.initial == 'Miss'),'Age']=22
    data.loc[(data.Age.isnull())&(data.initial == 'Mr'),'Age']=32
    data.loc[(data.Age.isnull())&(data.initial == 'Mrs'),'Age']=36
    data.loc[(data.Age.isnull())&(data.initial == 'others'),'Age']=20
    

    我们还可以画图展示一下,

    fig,ax = plt.subplots(1,2,figsize=(10,8))
    data[data['Survived']==0].Age.plot.hist(ax=ax[0],bins=20,edgecolor='black',color='red')
    ax[0].set_title('Survived = 0')
    data[data['Survived']==1].Age.plot.hist(ax=ax[1],bins=20,edgecolor='black',color='green')
    ax[1].set_title('Survived = 1')
    plt.show()
    

    通过上图可以看到,被救最多人的的年龄分布在20~40之间,超过60岁的被救的比较少了(年龄最大的人(80岁)被救了)。

    • Embarked缺失值填充
      Embarked列的缺失值填充比较简单,通过上面的特征分析我们可以发现,S港的人数最多,所以我们就用众数填充,
    data['Embarked'].fillna('S',inplace=True)
    

    填充完毕后,我们再统计一下缺失值,

    data.isnull().sum()
    

    就只剩Cabin列,接下我们进行数据预处理。

    2、数据预处理

    首先我们先将没用的特征进行处理,

    data=data.drop(['Name','Ticket','Cabin','initial'],axis=1)
    data.head()
    

    接下来我们将性别特征和登船港口数字化,因为接下来用到的算法没法处理文字,
    #将性别数字化,male=0,female=1
    data.loc[data['Sex']=='male','Sex']=0
    data.loc[data['Sex']=='female','Sex']=1
    # 登船港口数字化 S=0,C=1,Q=2
    data.loc[data['Embarked']=='S','Embarked']=0
    data.loc[data['Embarked']=='C','Embarked']=1
    data.loc[data['Embarked']=='Q','Embarked']=2
    data.head()
    

    四、建立模型

    这里我们使用多个机器学习算法进行预测,看一下效果如何。

    1、线性回归预测

    将训练集划分出特征和标签

    from sklearn.linear_model import LinearRegression
    from sklearn.model_selection import KFold
    linear = LinearRegression()
    features = ['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
    kf=KFold(n_splits=3,shuffle=False,random_state=1)
    source_x = data[features]  # 训练数据集:特征
    source_y = data['Survived']  #训练数据集:标签
    source_x.shape
    #输出
    (891,7)
    

    将数据集划分为训练集和测试集,

    from sklearn.model_selection import train_test_split
    train_x,test_x,train_y,test_y = train_test_split(source_x,source_y,train_size=0.8)
    print('原始数据集特征:',source_x.shape,'训练集特征:',train_x.shape,'测试集特征',test_x.shape)
    # 输出
    原始数据集特征: (891, 7) 训练集特征: (712, 7) 测试集特征 (179, 7)
    

    进行模型训练,

    linear.fit(train_x,train_y)
    linear.score(test_x,test_y)
    

    准确率太低了,不知道哪里出了问题。
    接下来,我们进行交叉验证再试一下,

    # K折交叉验证
    from sklearn.linear_model import LinearRegression
    from sklearn.model_selection import KFold
    # from sklearn.model_selection import cross_val_score
    
    model = LinearRegression()
    
    kf = KFold(n_splits=3,shuffle=False,random_state=1)
    # scores = cross_val_score(model,source_x,source_y,cv=kf)
    # scores.mean()
    scores=[]
    for train,test in kf.split(source_x):
        train_data = source_x.iloc[train,:]
        train_target = source_y.iloc[train]
        model.fit(train_data,train_target)
        test_data = model.predict(source_x.iloc[test,:])
        scores.append(test_data)
    

    因为结果只有0和1,而我们预测的结果分布在[0,1]区间上,所以我们将预测的得分值超过0.5设为1,小于0.5的设为0,

    import numpy as np
    scores = np.concatenate(scores,axis=0)
    scores[scores>.5]=1
    scores[scores<.5]=0
    accuracy = sum(scores==source_y)/len(source_y)
    print(accuracy)
    

    准确率提高了不少。

    2、逻辑斯蒂回归预测

    LR虽然是回归算法,但是也可以做分类的,这里我们也进行了交叉验证,

    from sklearn.model_selection import cross_val_score
    from sklearn.linear_model import LogisticRegression
    model = LogisticRegression(random_state=1)
    scores = cross_val_score(model,source_x,source_y,cv=3)
    print(scores.mean())
    

    准确率也有所提升。

    3、随机森林预测

    # 随机——随机对样本进行采样
    # 随机——对特征进行随机抽取
    from sklearn.model_selection import cross_val_score
    from sklearn.model_selection import KFold
    from sklearn.ensemble import RandomForestClassifier
    model = RandomForestClassifier(random_state=1,
                                  n_estimators=200,#200课树
                                  min_samples_split=4,
                                  min_samples_leaf=2)
    kf = KFold(n_splits=3,shuffle=False,random_state=1)
    scores = cross_val_score(model,source_x,source_y,cv=kf)
    print(scores.mean())
    

    随机森林还是比较强的,结果也有所提升。

    4、集成算法

    集成算法是将几个算法合并在一起进行预测的算法,在生产实际和各种比赛都被广泛应用;随机森林也是一种集成算法,它由多个决策树合并在一起进行决策,这里我们将随机森林与逻辑斯蒂回归算法进行合并,看一下效果如何。

    features = ['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
    model = RandomForestClassifier(random_state=1,
                                  n_estimators=200,#100课树
                                  min_samples_split=8,
                                  min_samples_leaf=4)
    from sklearn.ensemble import GradientBoostingClassifier
    import numpy as np
    #定义两个算法集成在一起
    algorithms = [
        [GradientBoostingClassifier(random_state=1,n_estimators=200,max_depth=3),features],
        [LogisticRegression(random_state=1,solver='liblinear'),features]
    ]
    
    kf=KFold(n_splits=3,shuffle=False,random_state=1)
    predictions=[]
    for train,test in kf.split(source_x):
        train_target = source_y.iloc[train]
        full_test_predictions = []
        for alg,predictors in algorithms:
            alg.fit(source_x.iloc[train,:],train_target)
            test_predictions = alg.predict_proba(source_x.iloc[test,:].astype(float))[:,1]
            full_test_predictions.append(test_predictions)
        test_predictions = (full_test_predictions[0]+full_test_predictions[1])/2
        test_predictions[test_predictions <=.5] =0
        test_predictions[test_predictions >.5] =1
        predictions.append(test_predictions)
        
    predictions = np.concatenate(predictions,axis=0)
    accuracy = sum(predictions == source_y)/len(predictions)
    print(accuracy)
    

    可以看到效果也是不错,跟随机森林的准确率差不多。

    五、总结

    算法预测到这里就结束了,如果小伙伴有其他的想法可以自行测试,这里其实还是有很多可以改变的地方,比如,对于Name列,一般名字比较长的属于大家族,比较富有,可以统计一下名字的长度作为一个特征;在提取Mr,Miss,Mrs等标识时也可以使用one-hot编码进行处理,这样获得特征就会增加很多;还可以将堂兄弟/妹和父母孩子个数做一个求和添加一个Family的特征...这些都是可以的,感兴趣的同学可以尝试一下。

    最后我们总结一下数据挖掘的一般流程:

    1、数据读取

    • 读取数据,并进行展示
    • 统计数据各项指标
    • 明确数据规模与要完成的任务

    2、特征理解分析

    • 单特征分析,逐个变量分析其对结果的影响
    • 多变量统计分析,综合考虑多种情况影响
    • 统计绘图得出结论

    3、数据清洗与预处理

    • 对缺失值进行填充
    • 特征标准化/归一化
    • 筛选有价值的特征
    • 分析特征之间的相关性

    4、建立模型

    • 特征数据与标签准备
    • 数据集切分
    • 多种建模算法对比
    • 集成策略等方案改进

    好了,今天的学习到这里就结束了~

    相关文章

      网友评论

        本文标题:Python数据分析(九):泰坦尼克号获救分析预测

        本文链接:https://www.haomeiwen.com/subject/fhmtrhtx.html