Titanic Data Science Solutions By Python

Machine Learning from Disaster Predict survival on the Titanic and get familiar with ML basics

Posted by z-Tracy on December 19, 2017

泰坦尼克号数据科学解决方案

关于著名的泰坦尼克号数据分析应该会有一系列的文章,本文是参考于 《Titanic Data Science Solutions》, 作为自己的学习,也希望能对你有帮助。

数据科学一般的工作流程

  1. 定义问题
  2. 获取训练集和测试集
  3. 分析、探索数据
  4. 建立模型和预测数据
  5. 数据可视化以及结论报告
  6. 提交答案

当然实际情况并不完全遵守这个流程的,有可能会省略一些步骤,也可能会互换顺序,希望我们也不要太墨守陈规。

定义问题

从一组样本中得知,谁在泰坦尼克号灾难中遇难或幸存下来,我们的模型可以根据一个给定的测试数据集的信息来确定测试数据集中这些乘客是否幸存下来。

审题是必须的

对于做数据分析来讲,审题是非常重要的,尽可能的去读懂数据,已经获取数据样本中的所有信息。比如我们这个命题的数据描述中有几个要点我们需要留意。

  1. 泰坦尼克号这次航行事故中在2224个乘客和工作人员中有1502遇难生亡。我们得出幸存率是32%。
  2. 造成这么重大的损失的原因是因为没有足够的救生艇。
  3. 虽然幸存下来有一些运气因素,但是一些人比其他人更可能生存,比如妇女,儿童和上层阶级等。

明确目标

总结了下我们有7大目标

  • Classifying:我们可能想对我们的样本进行分类,我们可能还想了解不同类与解决方案目标之间的关联。
  • Correlating:可以根据培训数据集中的可用特性来解决这个问题,数据集中的哪些特性对我们的解决方案会提供显著的帮助?即哪些特征与我们要解决的目标有显著相关关系。
  • Converting:建模阶段时候根据选择的模型算法要求所有的特征值必须转化为数值变量。那么需要将文本分类转化为数值型。
  • Completing:处理缺失值。
  • Correcting:同样要去分析判断训练集中有哪些不精确的值或者说是异常值,改正他们或剔除。我们还可能彻底抛弃那些完全扭曲结果的特征。
  • Creating:如果需要,我们能基于现有的特性或一组特性来创建新特性。
  • Charting:根据数据的性质和目标选择正确的可视化图表。

Let’s start

import pandas as pd
import numpy as np
import random as rnd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC,LinearSVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')
combine = [train_df,test_df]
train_df.columns.values
array(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'], dtype=object)
  • 首先看下哪些是类别值,那些是数值。

Categorical: Survived, Sex, and Embarked. Ordinal: Pclass.

Continous: Age, Fare. Discrete: SibSp, Parch.

train_df.head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

哪些是混合数据类型?

Ticket 是字母和数字混合类型. Cabin 是字母.

哪些可能包含拼写错误?

name这一列很可能有哦

train_df.tail()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
886 887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.00 NaN S
887 888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.00 B42 S
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.45 NaN S
889 890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.00 C148 C
890 891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.75 NaN Q

哪些特征包含空白,null 值?

  • 训练集:Cabin > Age > Embarked
  • 测试集:Cabin > Age > Embarked
train_df.info()
print('_'*40)
test_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB
________________________________________
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
  • 强烈建议你做一波数据探索,尽可能多的罗列一些信息出来,对于后面的方向与思路会有帮助。例如“接近30%的乘客有兄弟姐妹或者伴侣同时在这条船上”之类的。
train_df.describe(percentiles=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99])

PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
10% 90.000000 0.000000 1.000000 14.000000 0.000000 0.000000 7.550000
20% 179.000000 0.000000 1.000000 19.000000 0.000000 0.000000 7.854200
30% 268.000000 0.000000 2.000000 22.000000 0.000000 0.000000 8.050000
40% 357.000000 0.000000 2.000000 25.000000 0.000000 0.000000 10.500000
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
60% 535.000000 0.000000 3.000000 31.800000 0.000000 0.000000 21.679200
70% 624.000000 1.000000 3.000000 36.000000 1.000000 0.000000 27.000000
80% 713.000000 1.000000 3.000000 41.000000 1.000000 1.000000 39.687500
90% 802.000000 1.000000 3.000000 50.000000 1.000000 2.000000 77.958300
99% 882.100000 1.000000 3.000000 65.870000 5.000000 4.000000 249.006220
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 对于分类特征的描述
train_df.describe(include=['O'])
Name Sex Ticket Cabin Embarked
count 891 891 891 204 889
unique 891 2 681 147 3
top Williams, Mr. Charles Eugene male 1601 C23 C25 C27 S
freq 1 577 7 4 644

基于数据分析的假设

我们根据目前所做的数据分析得出了以下假设。在采取适当的行动之前,我们可能会进一步验证这些假设。

  • 首先我们要了解哪些特征是与Survival有怎样的相关性。
  • 猜想Age和Embarked这两个特征与目标应该有较强的相关性。
  • Ticket这个特征应该被丢弃掉,因为有很高的重复率另外它与是否存亡也没有什么关系。
  • Cabin也应该被丢弃,它有太多的null值了。
  • PassengerId 也应该被丢弃,分析过它没有太多的意义
  • Name特征也去除。

  • 创建一个叫Family的新特征,基于Parch 和SibSp。
  • 基于Name特征创建一个Title的特征。
  • 创建一个新的Age bands特征,将Age由连续型数据转变为有序的分类型数据。
  • 最后创建一个Fare range 特征。

设计分类假设

  1. 妇女(Sex = female)更容易幸存。
  2. 小孩(Age<?)更容易幸存。
  3. 社会层级在(Pclass=1)更容易幸存。

分析确认

我们需要确认我们的分类假设。首先快速地通过各自的相关性分析。这个分析对于数值型、分类型、序数和离散型的数据都有意义,但是不包含空值。

  • Pclass:当Placss=1时,我们得到与Survival的相关性>0.5,所以保留它在模型内。 Sex:当Sex= female时,幸存率高达74%,故保留。
train_df[['Pclass','Survived']].groupby(['Pclass'],as_index=False).mean().sort_values(by='Survived',ascending = False)
Pclass Survived
0 1 0.629630
1 2 0.472826
2 3 0.242363
train_df[['Sex','Survived']].groupby(by='Sex',as_index=False).mean().sort_values(by='Survived', ascending=False)
Sex Survived
0 female 0.742038
1 male 0.188908
train_df[['SibSp','Survived']].groupby('SibSp',as_index=False).mean().sort_values(by='Survived', ascending=False)
SibSp Survived
1 1 0.535885
2 2 0.464286
0 0 0.345395
3 3 0.250000
4 4 0.166667
5 5 0.000000
6 8 0.000000
train_df[['Parch','Survived']].groupby('Parch',as_index=False).mean().sort_values(by='Survived', ascending=False)
Parch Survived
3 3 0.600000
1 1 0.550847
2 2 0.500000
0 0 0.343658
5 5 0.200000
4 4 0.000000
6 6 0.000000

图表可视化

  • 直方图对于连续性数据变现非常有用

  • 观察结论

    1. Age<= 4 有很高的幸存率。
    2. Age=80 幸存。
    3. 大多数15-25岁的没有幸存。
    4. 15-35年龄层的人数最多。
g = sns.FacetGrid(train_df,col='Survived',hue='Survived',hue_order=[0,1])
g.map(plt.hist,'Age',bins=20)
<seaborn.axisgrid.FacetGrid at 0x2c4dfb8cf98>

png

g = sns.FacetGrid(train_df,row='Survived',hue='Survived',hue_order=[0,1])
g.map(plt.hist,'SibSp',bins=20)
<seaborn.axisgrid.FacetGrid at 0x2c4dfd396a0>

png

关联数值和序数特征

  • 观察结论有:
    1. Pclass=3的乘客大多数没有幸存。
    2. Pclass=2和Pclass=3乘客中的婴儿基本幸存。
    3. Pclass=1中的乘客大多数获救
grid = sns.FacetGrid(train_df, col='Survived', row='Pclass', size=2.2,aspect=1.6)
grid.map(plt.hist, 'Age', alpha=.5, bins=20)
grid.add_legend()
<seaborn.axisgrid.FacetGrid at 0x2c4dfe21ef0>

png

关联分类特征

  • 观察结论
    • 女性的幸存率高于男性
    • 观察比较各个港口的男女幸存情况后,发现因为港口不同而存在差异。
  • 故:
    • 将性别特征加入模型。
    • 将Embarked特征加入模型。
# 点图的取值默认是np.mean 即estimator=np.mean
grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
grid.map(sns.pointplot, 'Pclass', 'Survived','Sex', palette='deep',order=[1,2,3], hue_order=["female","male"])
grid.add_legend()
sns.pointplot()
<matplotlib.axes._subplots.AxesSubplot at 0x2c4e053ff98>

png

关联分类和数值特征

  • 观察结论
    • 貌似高票价的有更高的存活率
    • 港口号貌似与Survival存在关联性
  • 故:
    • Consider banding Fare feature.
# grid = sns.FacetGrid(train_df, col='Embarked', hue='Survived', palette={0: 'k', 1: 'w'})
grid = sns.FacetGrid(train_df, row='Embarked', col='Survived', size=2.2, aspect=1.6)
grid.map(sns.barplot, 'Sex', 'Fare', alpha=.5, ci=None)
grid.add_legend()
<seaborn.axisgrid.FacetGrid at 0x2c4e04b49e8>

png

sns.barplot(train_df.SibSp,train_df.Sex)
<matplotlib.axes._subplots.AxesSubplot at 0x2c4e09cfd30>

png

处理样本数据

到目前为止我们已有许多个假设和相对的结论了,那么接下来我们就开始根据我们的结论处理我们的数据集。

  • 修正和丢弃一些特征
    1. 丢弃Cabin和Ticket两个特征
print('Before',train_df.shape,test_df.shape)
train_df = train_df.drop(['Ticket','Cabin'],axis=1)
test_df = test_df.drop(['Ticket','Cabin'],axis=1)
combine =[train_df,test_df]
print('After',combine[0].shape,combine[1].shape)
Before (891, 12) (418, 11)
After (891, 10) (418, 9)

创建新特征

  • 想要基于Name创建新的特征Title,然后测试Title与Survival的相关性,由于Name是文本类型,所以可能会需要用到正则表达式。
for dataset in combine: 
    dataset['Title'] = dataset.Name.str.extract('([A-Za-z]+)\.',expand=False)
pd.crosstab(train_df['Title'], train_df['Sex'])
Sex female male
Title
Capt 0 1
Col 0 2
Countess 1 0
Don 0 1
Dr 1 6
Jonkheer 0 1
Lady 1 0
Major 0 2
Master 0 40
Miss 182 0
Mlle 2 0
Mme 1 0
Mr 0 517
Mrs 125 0
Ms 1 0
Rev 0 6
Sir 0 1
for dataset in combine:
    dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\
 	'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'],'Rare')
    dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Ms', 'Miss')
    dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs')
train_df[['Title','Survived']].groupby('Title',as_index=False).mean()
Title Survived
0 Master 0.575000
1 Miss 0.702703
2 Mr 0.156673
3 Mrs 0.793651
4 Rare 0.347826

我们将Title特征从分类特征转向数值序数

title_mapping = {'Mr':1,'Miss':2,'Mrs':3,'Master':4,'Rare':5}
for dataset in combine:
    dataset['Title'] = dataset['Title'].map(title_mapping)
    dataset['Title'] = dataset['Title'].fillna(0)
train_df.head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Fare Embarked Title
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 7.2500 S 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 71.2833 C 3
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 7.9250 S 2
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 53.1000 S 3
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 8.0500 S 1
train_df = train_df.drop(['Name','PassengerId'],axis=1)
test_df = test_df.drop('Name',axis=1)
combine = [train_df,test_df]
  • 转换分类特征

将分类特征中的字符转换为数值替代。

for dataset in combine:
    dataset['Sex'] = dataset['Sex'].map({'female':1,'male':0}).astype(int)
train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 22.0 1 0 7.2500 S 1
1 1 1 1 38.0 1 0 71.2833 C 3
2 1 3 1 26.0 0 0 7.9250 S 2
3 1 1 1 35.0 1 0 53.1000 S 3
4 0 3 0 35.0 0 0 8.0500 S 1

完善数值连续特征

完善这一特征——处理特征中的缺失值,主要有三个方法: - 最简单的方式是直接采用用均值或者标准偏差。 - 更加精准的方法是通过其他相关的特征在同等情况下去猜缺失值,可以用均值,这个案例我们注意到的存在相关性在是Age、Gender和Pclass。所以我们应该是Gender和Pclass组合的一些均值去猜Age值。 比如Pclass=1 和 Gender=0情况下的Age中位数,Pclass=1 和 Gender=1这种情况呢?等等

我们下面就用第二种方法试试

grid = sns.FacetGrid(train_df,row='Pclass',col='Sex',size=2.2,aspect=1.6)
grid.map(plt.hist,'Age',alpha=.5,bins=20)
grid.add_legend()
<seaborn.axisgrid.FacetGrid at 0x2c4e0a72c88>

png

先准备一个空的array去容纳Age基于Pclass和Gender的组合

guess_ages = np.zeros((2,3))
guess_ages
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])

现在我们对性别(0或1)和Pclass(1、2、3)进行迭代,以计算6种组合的猜测值

for dataset in combine:
    for i in range(0,2):
        for j in range(0,3):
            #获取Sex和Pclass所有情况组合下的Age的均值(不包含NA值)
            guess_df = dataset[(dataset['Sex']==i) & (dataset['Pclass']==j+1)]['Age'].dropna()
            #留意mean有一个skipna参数,如果设置True那么上一行代码就不需要做.dropna()处理了。两种方式效果一样
            age_guess = guess_df.mean()
            # Convert random age float to nearest .5 age
            guess_ages[i,j] = int(age_guess/0.5 + 0.5 ) * 0.5

    for i in range(0, 2):
            for j in range(0, 3):
    #             将guess_ages中的值填入dataset对应情况下的Age列中
                dataset.loc[ (dataset.Age.isnull()) & (dataset.Sex == i) & (dataset.Pclass == j+1),'Age'] = guess_ages[i,j]
    dataset['Age'] = dataset['Age'].astype(int)

train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 22 1 0 7.2500 S 1
1 1 1 1 38 1 0 71.2833 C 3
2 1 3 1 26 0 0 7.9250 S 2
3 1 1 1 35 1 0 53.1000 S 3
4 0 3 0 35 0 0 8.0500 S 1

我们创建年龄段列确定下和Survival的相关性

# 将所有年龄分为5段
train_df['AgeBand'] = pd.cut(train_df['Age'],5,precision=1)
train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)
AgeBand Survived
0 (-0.08, 16] 0.550000
1 (16, 32] 0.337374
2 (32, 48] 0.412037
3 (48, 64] 0.434783
4 (64, 80] 0.090909

将年龄分段列的值转换为序数值

for dataset in combine:    
    dataset.loc[ dataset['Age'] <= 16.3, 'Age'] = 0
    dataset.loc[(dataset['Age'] > 16.3) & (dataset['Age'] <= 32.3), 'Age'] = 1
    dataset.loc[(dataset['Age'] > 32.3) & (dataset['Age'] <= 48.2), 'Age'] = 2
    dataset.loc[(dataset['Age'] > 48.2) & (dataset['Age'] <= 64.08), 'Age'] = 3
    dataset.loc[ dataset['Age'] > 64.08, 'Age'] = 4
dataset['Age'] = dataset['Age'].astype(int)
train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title AgeBand
0 0 3 0 1 1 0 7.2500 S 1 (16, 32]
1 1 1 1 2 1 0 71.2833 C 3 (32, 48]
2 1 3 1 1 0 0 7.9250 S 2 (16, 32]
3 1 1 1 2 1 0 53.1000 S 3 (32, 48]
4 0 3 0 2 0 0 8.0500 S 1 (32, 48]

移除AgeBand特征

train_df = train_df.drop(['AgeBand'], axis=1)
combine = [train_df, test_df]
train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 0 1 1 0 7.2500 S 1
1 1 1 1 2 1 0 71.2833 C 3
2 1 3 1 1 0 0 7.9250 S 2
3 1 1 1 2 1 0 53.1000 S 3
4 0 3 0 2 0 0 8.0500 S 1

创建新的特征

创建FamilySize

for dataset in combine:
    dataset['FamilySize'] = dataset['SibSp']+ dataset['Parch']+1

train_df[['FamilySize','Survived']].groupby(['FamilySize'],as_index=False).agg('mean').sort_values(by='Survived',ascending=False)
FamilySize Survived
3 4 0.724138
2 3 0.578431
1 2 0.552795
6 7 0.333333
0 1 0.303538
4 5 0.200000
5 6 0.136364
7 8 0.000000
8 11 0.000000

创建IsAlone

for dataset in combine:
    dataset['IsAlone'] = 0
    dataset.loc[dataset['FamilySize'] == 1,'IsAlone']= 1
    
train_df[['IsAlone','Survived']].groupby(['IsAlone'],as_index=False).mean()
IsAlone Survived
0 0 0.505650
1 1 0.303538

移除Parch, SibSp和FamilySize特征

train_df = train_df.drop(['Parch','SibSp','FamilySize'],axis=1)
test_df = test_df.drop(['Parch','SibSp','FamilySize'],axis=1)
combine = [train_df,test_df]
train_df.head()
Survived Pclass Sex Age Fare Embarked Title IsAlone
0 0 3 0 1 7.2500 S 1 0
1 1 1 1 2 71.2833 C 3 0
2 1 3 1 1 7.9250 S 2 1
3 1 1 1 2 53.1000 S 3 0
4 0 3 0 2 8.0500 S 1 1

创建Age*Class特征

for dataset in combine:
    dataset['Age*Class'] = dataset.Age * dataset.Pclass
train_df.loc[:9, ['Age*Class', 'Age', 'Pclass']]
Age*Class Age Pclass
0 3 1 3
1 2 2 1
2 3 1 3
3 2 2 1
4 6 2 3
5 3 1 3
6 3 3 1
7 0 0 3
8 3 1 3
9 0 0 2

完善类别特征

用该列众数填充缺失值

# 获取这列的众数
freq_port = train_df.Embarked.dropna().mode()[0]
freq_port
'S'
for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].fillna(freq_port)
    
train_df[['Embarked', 'Survived']].groupby(['Embarked'], as_index=False).mean().sort_values(by='Survived', ascending=False)
Embarked Survived
0 C 0.553571
1 Q 0.389610
2 S 0.339009
  • 转换分类特征

将类别特征中的字符转换为数值替代。

for dataset in combine:
    dataset['Embarked'] = dataset['Embarked'].map( {'S': 0, 'C': 1, 'Q': 2} ).astype(int)

train_df.head()
Survived Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 0 3 0 1 7.2500 0 1 0 3
1 1 1 1 2 71.2833 1 3 0 2
2 1 3 1 1 7.9250 0 2 1 3
3 1 1 1 2 53.1000 0 3 0 2
4 0 3 0 2 8.0500 0 1 1 6

快速的完善和转化Fare特征

用中位数填充NA值

test_df['Fare'].fillna(test_df['Fare'].dropna().median(), inplace=True)
test_df.head()
PassengerId Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 892 3 0 2 7.8292 2 1 1 6
1 893 3 1 2 7.0000 0 3 0 6
2 894 2 0 3 9.6875 2 1 1 6
3 895 3 0 1 8.6625 0 1 1 3
4 896 3 1 1 12.2875 0 3 0 3
train_df['FareBand'] = pd.qcut(train_df['Fare'], 4)
train_df[['FareBand', 'Survived']].groupby(['FareBand'], as_index=False).mean().sort_values(by='FareBand', ascending=True)
FareBand Survived
0 [0, 7.91] 0.197309
1 (7.91, 14.454] 0.303571
2 (14.454, 31] 0.454955
3 (31, 512.329] 0.581081
for dataset in combine:
    dataset.loc[ dataset['Fare'] <= 7.91, 'Fare'] = 0
    dataset.loc[(dataset['Fare'] > 7.91) & (dataset['Fare'] <= 14.454), 'Fare'] = 1
    dataset.loc[(dataset['Fare'] > 14.454) & (dataset['Fare'] <= 31), 'Fare']   = 2
    dataset.loc[ dataset['Fare'] > 31, 'Fare'] = 3
    dataset['Fare'] = dataset['Fare'].astype(int)

train_df = train_df.drop(['FareBand'], axis=1)
combine = [train_df, test_df]
train_df.head(10)
Survived Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 0 3 0 1 0 0 1 0 3
1 1 1 1 2 3 1 3 0 2
2 1 3 1 1 1 0 2 1 3
3 1 1 1 2 3 0 3 0 2
4 0 3 0 2 1 0 1 1 6
5 0 3 0 1 1 2 1 1 3
6 0 1 0 3 3 0 1 1 3
7 0 3 0 0 2 0 4 0 0
8 1 3 1 1 1 0 3 0 3
9 1 2 1 0 2 1 3 0 0
test_df.head(10)
PassengerId Pclass Sex Age Fare Embarked Title IsAlone Age*Class
0 892 3 0 2 0 2 1 1 6
1 893 3 1 2 0 0 3 0 6
2 894 2 0 3 1 2 1 1 6
3 895 3 0 1 1 0 1 1 3
4 896 3 1 1 1 0 3 0 3
5 897 3 0 0 1 0 1 1 0
6 898 3 1 1 0 2 2 1 3
7 899 2 0 1 2 0 1 0 2
8 900 3 1 1 0 1 3 1 3
9 901 3 0 1 2 0 1 0 3

模型预测

现在我们已经准备好训练模型并预测所需的解决方案,有60多种预测建模算法可供选择。我们必须了解问题的类型和解决方案的需求,将其缩小到可以评估的少数几种模型,我们的问题是分类和回归问题,我们想要确定输出(存活与否)与其他变量或特性(Gender, Age, Port…)之间的关系。 我们也正在执行一类机器学习,这个机器学习称为监督学习,因为我们正在用给定的数据集来训练我们的模型。有了这两个标准—监督学习加上分类和回归,我们可以将模型的选择范围缩小到以下:

  • Logistic Regression
  • KNN or k-Nearest Neighbors
  • Support Vector Machines
  • Naive Bayes classifier
  • Decision Tree
  • Random Forrest
  • Perceptron
  • Artificial neural network
  • RVM or Relevance Vector Machine
X_train = train_df.drop('Survived',axis=1)
Y_train = train_df['Survived']
X_test = test_df.drop('PassengerId',axis=1).copy()
X_train.shape,Y_train.shape,X_test.shape
((891, 8), (891,), (418, 8))

逻辑回归是在工作流早期运行的一个有用的模型,逻辑回归测量了分类依赖变量(特征)和一个或多个自变量(特征)之间的关系,通过使用逻辑函数估计概率,这是累积的逻辑分布。

请注意基于我们的训练数据集的模型所产生的信心分数。

  • Logistic Regression
logreg = LogisticRegression()
logreg.fit(X_train,Y_train)
Y_pred = logreg.predict(X_test)
# train数据中训练出来的分类器在X_test上的分数
acc_log = round(logreg.score(X_train,Y_train)*100,2)
acc_log
81.260000000000005

我们可以使用逻辑回归来验证我们的假设和决定,以创建和完成目标。这可以通过计算决策函数中特性的系数来完成。

正系数增加了响应的对数(从而增加了概率),而负系数会降低响应的对数(从而降低概率)。

  • Sex是最高的正系数,意味着Sex的值增加(male: 0 to female: 1),Survived=1的可能性增加最显著。
  • 相反Pclass增加,Survived=1的可能性减少最显著。
  • Age*Class是第二高的负相关特征。
  • Title 是第二高的正相关特征。
coeff_df =pd.DataFrame(train_df.columns.delete(0))
coeff_df.columns = ['Feature']
# logreg.coef_获取决策函数中特性系数
coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
coeff_df.sort_values(by='Correlation', ascending=False)
Feature Correlation
1 Sex 2.200978
5 Title 0.414362
4 Embarked 0.281026
6 IsAlone 0.262084
3 Fare -0.022062
7 Age*Class -0.085959
2 Age -0.371647
0 Pclass -1.083515
  • Support Vector Machines

支持向量机是具有关联的学习算法的监督学习模型,其分析用于分类和回归分析的数据。给定一组训练样本,每个标记属于两个类别中的一个或另一个。支持向量机构建一个将新的测试样本分配给一个类别或另一个类别的模型,使之成为非概率的二元线性分类器。

值得注意的是,模型生成的置信分数要比物流回归模型高。

svc = SVC()
svc.fit(X_train,Y_train)
Y_pred = svc.predict(X_test)
acc_svc = round(svc.score(X_train, Y_train) * 100, 2)
acc_svc
83.5
  • K-Nearest Neighbors

在图像识别方面,k-最近邻算法是一种用于分类和回归的非参数方法。样本通过邻居的多数投票进行分类,样本被分配到最近k个近邻中最常见的类别中。

KNN confidence score is better than Logistics Regression but worse than SVM.

#  当然这里的n_neighbors=3是不断调参得到的最优。
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X_train,Y_train)
knn.predict(X_test)
acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
acc_knn

84.060000000000002
  • naive Bayes classifiers

The model generated confidence score is the lowest among the models evaluated so far.

gaussian = GaussianNB()
gaussian.fit(X_train,Y_train)
gaussian.predict(X_test)
acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
acc_gaussian
76.879999999999995
  • 感知机
perceptron = Perceptron()
perceptron.fit(X_train, Y_train)
Y_pred = perceptron.predict(X_test)
acc_perceptron = round(perceptron.score(X_train, Y_train) * 100, 2)
acc_perceptron
78.790000000000006
  • Linear SVC
linear_svc = LinearSVC()
linear_svc.fit(X_train, Y_train)
Y_pred = linear_svc.predict(X_test)
acc_linear_svc = round(linear_svc.score(X_train, Y_train) * 100, 2)
acc_linear_svc
79.459999999999994
  • Stochastic Gradient Descent
sgd = SGDClassifier()
sgd.fit(X_train, Y_train)
Y_pred = sgd.predict(X_test)
acc_sgd = round(sgd.score(X_train, Y_train) * 100, 2)
acc_sgd
67.0
  • Decision Tree

该模型使用决策树作为预测模型,它将特征(树分支)映射到目标值(树叶子),目标变量可以采用有限的一组值的树模型称为分类树; 目标变量可以采用连续值(通常是实数)的决策树称为回归树。

The model confidence score is the highest among models evaluated so far

decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, Y_train)
Y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, Y_train) * 100, 2)
acc_decision_tree
86.640000000000001
  • Random Forest

最通俗理解,随机森林就是很多颗决策树……其他的自行查阅文档。

The model confidence score is the highest among models evaluated so far.

random_forest = RandomForestClassifier(n_estimators=100)
random_forest.fit(X_train, Y_train)
Y_pred = random_forest.predict(X_test)
random_forest.score(X_train, Y_train)
acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
acc_random_forest
  • 以上是做分类算法的初选,最终我们选择随机森林的输出 (Y_pred)。

模型评估

models = pd.DataFrame({
    'Model': ['Support Vector Machines', 'KNN', 'Logistic Regression', 
              'Random Forest', 'Naive Bayes', 'Perceptron', 
              'Stochastic Gradient Decent', 'Linear SVC', 
              'Decision Tree'],
    'Score': [acc_svc, acc_knn, acc_log, 
              acc_random_forest, acc_gaussian, acc_perceptron, 
              acc_sgd, acc_linear_svc, acc_decision_tree]})
models.sort_values(by='Score', ascending=False)
submission = pd.DataFrame({
        "PassengerId": test_df["PassengerId"],
        "Survived": Y_pred
    })
# submission.to_csv('../output/submission.csv', index=False)
submission

总结

本篇在后续算法处理是非常的简陋的,特别是评估仅用训练数据来选择模型,很容易出现过拟合现象。所以主要的学习精华应该是前面数据处理和特征选择的一些思路和方法。关于这个模型评估也有人认为用交叉验证的方式更加好,而且得出结论是SVM的表现最优秀,大家可以自行尝试看看。

文章也花了一些时间处理和整理希望你能有所收获,如果大家有发现比较好的案例或者学习材料也欢迎可以留言跟我分享,谢谢!