原文地址以下:python
https://www.kaggle.com/startupsci/titanic-data-science-solutions正则表达式
----------------------------------------------------------------算法
泰坦尼克数据科学解决方案:数组
1. 工做流程步骤:app
在 Data Science Solutions book 这本书里,描述了在解决一个竞赛问题时所须要作的具体工做流程:less
以上的工做流程仅仅描述了通常的问题解决步骤,然而依然会有一些特殊的案例并不严格遵循以上流程:dom
2. 问题的定义:机器学习
通常的竞赛网站(如 Kaggle)会在提供训练数据以及测试数据的同时,给出问题的定义。对与这次的“泰坦尼克号幸存者”问题的定义以下:学习
“在训练数据中,提供了在泰坦尼克号上乘客的具体数据以及他们是否在那次灾难中存活的信息。参赛者可否经过已有的训练数据训练出一个模型,此模型须要根据输入的测试数据里乘客信息,来预测此乘客是否能在灾难中存活”测试
咱们可能也想要经过问题描述获取更多有关此问题的信息。在此问题的描述中,比较有意义的描述以下:
3. 工做流程里的目标:
数据科学解决方案的流程主要有7个目标:
4. 会用到的库:
如下是在接下来的实验里会用到的一些库:
# data analysis and wrangling
import pandas as pd
import numpy as np
import random as rnd
# visualization
import seaborn as sns
import matplotlib.pyplot as plt
# machine learning
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
5. 获取数据:
咱们能够用python 的 Pandas 来帮助咱们处理数据。首先能够将训练数据以及测试数据读入到Pandas 的 DataFrames 里。咱们也会将这两个数据集结合起来,用于在两个数据集上同时作一些特定的操做。
# set pandas
pd.set_option('display.width', 1000)
# use pandas to manage data
train_df = pd.read_csv('data/train.csv')
test_df = pd.read_csv('data/test.csv')
combine = [train_df, test_df]
6. 经过描述数据来分析:
Pandas 也能够帮助咱们描述数据集。咱们能够经过如下问答的方式来查看数据集:
1. 在数据集中有哪些可用的特征?
首先须要注意的是,数据集里特征的描述已经在问题描述里给出了,这次数据集里的特征描述以下:
https://www.kaggle.com/c/titanic/data
------------------------------------------------------------------------------------------------------
主要内容为:
Data Dictionary
Variable |
Definition |
Key |
survival |
Survival |
0 = No, 1 = Yes |
pclass |
Ticket class |
1 = 1st, 2 = 2nd, 3 = 3rd |
sex |
Sex |
|
Age |
Age in years |
|
sibsp |
# of siblings / spouses aboard the Titanic |
|
parch |
# of parents / children aboard the Titanic |
|
ticket |
Ticket number |
|
fare |
Passenger fare |
|
cabin |
Cabin number |
|
embarked |
Port of Embarkation |
C = Cherbourg, Q = Queenstown, S = Southampton |
Variable Notes
pclass: A proxy for socio-economic status (SES)
1st = Upper
2nd = Middle
3rd = Lower
age: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5
sibsp: The dataset defines family relations in this way...
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)
parch: The dataset defines family relations in this way...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
Some children travelled only with a nanny, therefore parch=0 for them.
------------------------------------------------------------------------------------------------------
在 Pandas里:
>>> print(train_df.columns.values)
['PassengerId' 'Survived' 'Pclass' 'Name' 'Sex' 'Age' 'SibSp' 'Parch'
'Ticket' 'Fare' 'Cabin' 'Embarked']
2. 哪些特征是离散型的?
这些离散型的数值能够将样本分类为一系列类似的样本。在离散型特征里,它们的数值是基于名词的?仍是基于有序的?又或是基于比率的?仍是基于间隔类的?除此以外,这个能够帮助咱们为数据选择合适的图形作可视化。
在这个问题中,离散型的变量有:Survived,Sex 和 Embarked。基于序列的有:Pclass
3. 哪些特征是数值型?
哪些特征是数值型的?这些数据的值随着样本的不一样而不一样。在数值型特征里,它们的值是离散的仍是连续的?又或者是基于时间序列?除此以外,这个能够帮助咱们为数据选择合适的图形作可视化。
在这个问题中,连续型的数值特征有:Age,Fare。离散型数值有:SibSp,Parch
>>> train_df.head()
4. 哪些特征是混合型数据?
数值型、字母数值型数据在同一特征下面。这些有多是咱们须要修正的目标数据。
在这个问题中,Ticket是混合了数值型以及字母数值型的数据类型,Cabin是字母数值型数据
5. 哪些特征可能包含错误数据或打字错误?
在大型数据集里要发现这些可能比较困难,然而经过观察小型的数据集里少许的样本,可能也能够彻底告诉咱们哪些特征须要修正。
在这个问题中,Name的特征可能包含错误或者打字错误,由于会有好几种方法来描述名字
>>> train_df.tail()
6. 哪些特征包含空格,null或者空值
这些空格,null值或者空值极可能须要修正。
在这个问题中:
7.每一个特征下的数据类型是什么?
这个能够在咱们作数据转换时起到较大的帮助。
在这个问题中:
>>> train_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
>>> test_df.info()
<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
8. 在样本里,数值型特征的数值分布是什么样的?
这个能够帮助咱们初步了解:训练数据集如何体现了实际问题。
在这个问题中:
咱们能够经过如下方式获取上述信息:
>>> train_df.describe()
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
25% 223.500000 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 经过使用 percentiles=[.61, .62] 来查看数据集能够了解到生存率为 38%
>>> train_df.describe(percentiles=[.61, .62])
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
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
61% 543.900000 0.000000 3.000000 32.000000 0.000000 0.000000 23.225000
62% 552.800000 1.000000 3.000000 32.000000 0.000000 0.000000 24.150000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 经过使用 percentiles=[.75, .8] 来查看Parch的分布
>>> train_df.describe(percentiles=[.75, .8])
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
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
80% 713.000000 1.000000 3.000000 41.000000 1.000000 1.000000 39.687500
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 经过使用 percentile=[.68, .69] 来查看SibSp的分布
>>> train_df.describe(percentiles=[.68, .69])
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
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
68% 606.200000 1.000000 3.000000 35.000000 0.000000 0.000000 26.307500
69% 615.100000 1.000000 3.000000 35.000000 1.000000 0.000000 26.550000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200
# 经过使用 percentile=[.1, .2, .3, .4, .5, .6, .7, .8, .9, .99] 来查看Age和Fare的分布
>>> 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
8. 在样本里,离散型数据的分布是什么?
在这个问题中:
能够经过如下方法得到以上信息:
>>> train_df.describe(include=['O'])
Name Sex Ticket Cabin Embarked
count 891 891 891 204 889
unique 891 2 681 147 3
top Andrew, Mr. Edgardo Samuel male CA. 2343 G6 S
freq 1 577 7 4 644
7. 基于以上数据分析后的假设
根据以上的数据分析步骤后,咱们能够暂时得出如下假设。固然,咱们也能够在以后验证这些假设。
相互关系:
咱们想知道每一个特征与Survival的相关性如何。咱们但愿可以今早的作这一步,而且将这些相关性特征匹配到建模后的相关性特征上。
补全数据:
修正数据:
创造数据:
分类:
根据以前的问题描述或者已有的数据,咱们也能够提出如下假设:
8. 基于pivoting features的分析
为了验证以前的观察与假设,咱们能够经过pivoting feature的方法简单的分析一下特征之间的相关性。
这种方法仅仅对那些没有特别多空值的属性有效,而且仅仅对那些分类型的(Sex)、有序型的(Pclass)以及离散型(SibSp,Parch)的特征才有意义。
1. Pclass:咱们观察到Pclass=1与Survived的相关性较大(>0.5),因此能够考虑将此特征放入到以后的模型里
2. Sex:咱们能够确认Sex=female有着高达74%的生存率
3. SibSp 和 Parch:这些特征下有些值与survived有相关性,可是有些又毫无相关性。因此咱们可能须要基于这些单独的特征或一系列特征建立一个新特征,以作进一步分析
以上结论能够经过下面的操做获取:
>>> 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(['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
9.经过将数据可视化进行分析
如今咱们能够经过将数据可视化对数据作进一步分析,并继续验证咱们以前的假设是否正确
数值型特征与Survived之间的联系:
柱状图在用于分析连续型的数值特征时很是有用,如特征Age,它的柱状图数值范围(不一样的年龄范围)能够帮助咱们识别一些有用的模式。
经过使用默认或自定的数值范围(年龄范围),柱状图能够帮助咱们描绘出样本所遵循的分布。
它能够帮助咱们发现是否某些特定的年龄范围(如婴儿)有更高的存活率。
咱们能够经过如下代码来画出Age的柱状图:
>>> g = sns.FacetGrid(train_df, col='Survived')
>>> g.map(plt.hist, 'Age', bins=20)
>>> plt.show()
观察:
结论:
以上简单的分析验证了咱们以前的假设:
数值型与序列型特征之间的联系:
咱们能够将多个特征组合,而后经过一个简单的图来识别它们之间的关系。这种方法能够应用在数值型以及分类型(Pclass)的特征里,由于它们的值都是数值型。
咱们能够经过如下代码来画出Pclass的柱状图:
>>> 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()
>>> plt.show()
观察:
结论:
离散型特征与Survived之间的联系:
如今咱们能够查看离散型特征与survived之间的关系
咱们能够经过如下方式将数据可视化:
>>> grid = sns.FacetGrid(train_df, row='Embarked', size=2.2, aspect=1.6)
>>> grid.map(sns.pointplot, 'Pclass', 'Survived', 'Sex', palette='deep')
>>> grid.add_legend()
>>> plt.show()
观察:
结论:
离散型特征与数值型特征之间的联系:
咱们可能也想找出离散型与数值型特征之间的关系。
咱们能够考虑查看Embarked(离散非数值型),Sex(离散非数值型),Fare(连续数值型)与Survived(离散数值型)之间的关系
咱们能够经过下方式将数据可视化:
>>> 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()
>>> plt.show()
观察:
结论:
10.加工数据
咱们根据数据集以及题目的要求已经收集了一些假设与结论。到如今为止,咱们暂时尚未对任何特征或数据进行处理。
接下来咱们会根据以前作的假设与结论,以“修正数据”、“创造数据”以及“补全数据”为目标,对数据进行处理。
经过丢弃特征来修正数据:
这个步骤比较好的一个开始。经过丢弃某些特征,可让咱们处理更少的数据点,并让分析更简单。
根据咱们以前的假设和结论,咱们但愿丢弃Cabin和Ticket这两个特征。
在这里须要注意的是,为了保持数据的一致,咱们须要同时将训练集与测试集里的这两个特征均丢弃。
具体步骤以下:
>>> print("Before", train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
Before (891, 12) (418, 11) (891, 12) (418, 11)
>>> 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', train_df.shape, test_df.shape, combine[0].shape, combine[1].shape)
After (891, 10) (418, 9) (891, 10) (418, 9)
经过已有的特征建立新特征:
咱们在丢弃Name与PassengerId这两个特征以前,但愿从Name特征里提取出Titles的特征,并测试Titles与survival之间的关系。
在下面的代码中,咱们经过正则提取了Title特征,正则表达式为(\w+\.),它会在Name特征里匹配第一个以“.”号为结束的单词。
同时,指定expand=False的参数会返回一个DataFrame。
>>> 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
咱们可使用高一些更常见的名字或“Rare”来代替一些Title,如:
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转换为有序的数值型:
# convert categorical titles to ordinal
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()
如今咱们能够放心的从训练集与测试集里丢弃Name特征。同时,咱们也再也不须要训练集里的PassengerId特征:
>>> train_df = train_df.drop(['Name', 'PassengerId'], axis=1)
>>> test_df = test_df.drop(['Name'], axis=1)
>>> combine = [train_df, test_df]
>>> train_df.shape, test_df.shape
((891, 9), (418, 9))
>>> train_df.head()
Survived Pclass Sex Age SibSp Parch Fare Embarked Title
0 0 3 male 22.0 1 0 7.2500 S 1
1 1 1 female 38.0 1 0 71.2833 C 3
2 1 3 female 26.0 0 0 7.9250 S 2
3 1 1 female 35.0 1 0 53.1000 S 3
4 0 3 male 35.0 0 0 8.0500 S 1
新的发现:
当咱们画出Title,Age和Survived的图后,咱们有了如下新的发现:
结论:
转换一个离散型的特征
如今咱们能够将一些包含字符串数据的特征转换为数值型特征,由于在不少建模算法里,输入的参数要求为数值型。
这个步骤可让咱们达到补全数据的目标。
咱们能够从转换Sex特征开始,将female转换为1,male转换为0。咱们能够将新的特征命名为Gender:
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
补全连续数值型特征
如今咱们能够开始为那些含null值或者丢失值的特征补全数据。咱们首先会为Age特征补全数据。
如今咱们总结一下三种补全连续数值型特征数据的方法:
1. 一个简单的方法是产生一个随机数,这个随机数的范围在这个特征的平均值以及标准差之间
2. 更精准的一个作法是使用与它相关的特征来作一个猜想。在这个案例中,咱们发现Age,Gender和Pclass之间有关联。
因此咱们会使用一系列Pclass和Gender特征组合后的中值,做为猜想的Age值。
因此咱们会有一系列的猜想值如:当Pclass=1且Gender=0时,当Pclass=1且Gender=1时,等等…
3. 第三种方法是结合以上两种方法。咱们能够根据一系列Pclass与Gender的组合,并使用第一种方法里提到的随机数来猜想缺失的Age值
方法1与方法3会在模型里引入随机噪音,屡次的结果可能会有所不一样。因此咱们在这更倾向于使用方法2:
>>> 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()
>>> plt.show()
咱们先准备一个空的数组来存储猜想的年龄,由于是Pclass与Gender的组合,因此数组大小为2x3:
>>> guess_ages = np.zeros((2, 3))
而后咱们能够对Sex(0或1)和Pclass(1,2,3)进行迭代,并计算出在6中组合下所获得的猜想(Age)值:
for dataset in combine:
for i in range(0, 2):
for j in range(0, 3):
guess_df = dataset[(dataset['Sex'] == i) & (dataset['Pclass'] == j+1)]['Age'].dropna()
age_guess = guess_df.median()
# 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):
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
如今咱们对Age分段,并查看每段与Survived之间的相关性:
>>> train_df['AgeBand'] = pd.cut(train_df['Age'], 5)
>>> train_df[['AgeBand', 'Survived']].groupby(['AgeBand'], as_index=False).mean().sort_values(by='AgeBand', ascending=True)
AgeBand Survived
0 (-0.08, 16.0] 0.550000
1 (16.0, 32.0] 0.337374
2 (32.0, 48.0] 0.412037
3 (48.0, 64.0] 0.434783
4 (64.0, 80.0) 0.090909
而后咱们根据上面的分段,使用有序的数值来替换Age里的值:
for dataset in combine:
dataset.loc[ dataset['Age'] <= 16, 'Age'] = 0
dataset.loc[(dataset['Age'] > 16) & (dataset['Age'] <= 32), 'Age'] = 1
dataset.loc[(dataset['Age'] > 32) & (dataset['Age'] <= 48), 'Age'] = 2
dataset.loc[(dataset['Age'] > 48) & (dataset['Age'] <= 64), 'Age'] = 3
dataset.loc[ dataset['Age'] > 64, 'Age']
>>> 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.0, 32.0]
1 1 1 1 2 1 0 71.2833 C 3 (32.0, 48.0]
2 1 3 1 1 0 0 7.9250 S 2 (16.0, 32.0]
3 1 1 1 2 1 0 53.1000 S 3 (32.0, 48.0)
4 0 3 0 2 0 0 8.0500 S 1 (32.0, 48.0)
接着咱们能够丢弃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
经过已有的特征组合出新特征
如今咱们能够经过组合Parch和SibSp特征,建立一个新的FamilySize特征。这个步骤可让咱们从数据集里丢弃Parch与SibSp特征。
for dataset in combine:
dataset['FamilySize'] = dataset['SibSp'] + dataset['Parch'] + 1
>>> train_df[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=False).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的特征,保留IsAlone的特征:
>>> 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
咱们还能够经过结合Pclass 和 Age来建立一个新的特征:
for dataset in combine:
dataset['Age*Class'] = dataset.Age * dataset.Pclass
>>> train_df.loc[:, ['Age*Class', 'Age', 'Pclass']].head(10)
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
补全一个离散型的特征
Embarked特征主要有三个值,分别为S,Q,C,对应了三个登船港口。在训练集里,这个有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
将离散型特征转换为数值型
咱们如今能够将离散型的Embarked特征转换为数值型特征
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特征补全数据。在补全时,咱们可使用最频繁出现的数据用于补全缺失值。
(咱们也能够将Fare的数值作四舍五入,将它精确到2位)
>>> 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
接下来咱们将Fare分段:
>>> 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.001, 7.91] 0.197309
1 (7.91, 14.454] 0.303571
2 (14.454, 31.0] 0.454955
3 (31.0, 512.329) 0.581081
根据分段后的特征FareBand,将Fare转换为有序的数值型特征:
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
11.建模,预测,并解决问题
如今咱们已经作好了训练模型的准备,在模型训练完后,咱们便可将其应用到解决问题中。对于预测的问题,咱们至少有60多种算法可供选择。
因此咱们必须理解问题的类型和解决方案的需求,这样才能缩小模型的选择范围。如今这个问题是一个分类与回归的问题,
咱们但愿找出输出(即Survived)与其余特征(即Gender,Age,Port等)之间的关系。由于给定了训练集,因此这在机器学习里是一个有监督学习。
因此如今对算法的需求是:有监督学习加上分类与回归。根据这个条件,咱们有如下模型可供选择:
如今咱们将训练集与测试集再作一下区分:
>>> X_train = train_df.drop('Survived', axis=1)
>>> Y_train = train_df['Survived']
>>> X_test = test_df.drop('PassengerId', axis=1).copy()
>>> X_train, Y_train, X_test
((891, 8), (891,), (418, 8))
Logistic Regression 是一个很是有用的模型,能够在工做流程里优先使用。它经过使用估计几率的方法衡量了离散型特征与其余特征之间的关系,是一个渐增型的逻辑分布。
>>> logreg = LogisticRegression()
>>> logreg.fit(X_train, Y_train)
>>> Y_pred = logreg.predict(X_test)
>>> acc_log = round(logreg.score(X_train, Y_train) * 100, 2)
>>> acc_log
80.359999999999999
咱们能够用Logistic Regression来验证咱们之间作的假设与结论。这个能够经过计算特征值的系数来达成。正系数能够提高对数概率(因此增加了几率),负系数会下降对数概率(所以下降了几率):
>>> coeff_df = pd.DataFrame(train_df.columns.delete(0))
>>> coeff_df.columns = ['Feature']
>>> coeff_df["Correlation"] = pd.Series(logreg.coef_[0])
>>> coeff_df.sort_values(by='Correlation', ascending=False)
Feature Correlation
1 Sex 2.201527
5 Title 0.398234
2 Age 0.287162
4 Embarked 0.261762
6 IsAlone 0.129140
3 Fare -0.085150
7 Age*Class -0.311202
0 Pclass -0.749007
从上面的结果咱们能够看出:
下一步咱们使用SVM来分析数据并作分类与回归分析。
>>> 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.840000000000003
能够看到使用SVM后的正确率获得了提高。
在模式识别中,KNN算法是一种非参数的方法,用于作分类与回归。使用KNN来分析此问题的话:
>>> knn = KNeighborsClassifier(n_neighbors = 3)
>>> knn.fit(X_train, Y_train)
>>> Y_pred = knn.predict(X_test)
>>> acc_knn = round(knn.score(X_train, Y_train) * 100, 2)
84.739999999999995
能够看到使用KNN的正确率比Logistic Regression更高,可是比SVM更低
下面咱们试试朴素贝叶斯:
>>> gaussian = GaussianNB()
>>> gaussian.fit(X_train, Y_train)
>>> Y_pred = gaussian.predict(X_test)
>>> acc_gaussian = round(gaussian.score(X_train, Y_train) * 100, 2)
72.280000000000001
看来在这个问题中使用朴素贝叶斯不是一个很好的选择,从当前来看,它的正确率是最低的。
接下来咱们试试 perceptron(感知机)算法,它能够用于二分类问题:
>>> 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)
78.0
能够看到perceptron的正确率也不高
接下来试试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)
79.010000000000005
与随机梯度降低分类器:
>>> 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)
78.230000000000004
这几个算法计算到的正确率都不够理想。
接下来咱们看看很常见的决策树算法:
>>> 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)
86.760000000000005
能够看到,使用决策树的算法使得正确率达到了一个更高的值。在目前为止,它的正确率是最高的。
而后咱们看看随机森林,随机森林经过组合多个决策树算法来完成:
>>> random_forest = RandomForestClassifier(n_estimators=100)
>>> random_forest.fit(X_train, Y_train)
>>> Y_pred = random_forest.predict(X_test)
>>> acc_random_forest = round(random_forest.score(X_train, Y_train) * 100, 2)
86.760000000000005
经过比较模型的正确率,咱们决定使用最高正确率的模型,即随机森林的输出做为结果提交。
12.模型评价
如今咱们能够对以上的模型的正确率进行排名,并从中选择一个正确率最高的模型:
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)
Model Score
3 Random Forest 86.76
8 Decision Tree 86.76
1 KNN 84.74
0 Support Vector Machines 83.84
2 Logistic Regression 80.36
7 Linear SVC 79.01
6 Stochastic Gradient Decent 78.23
5 Perceptron 78.00
4 Naive Bayes 72.28
其中决策树与随机森林的正确率最高,可是咱们在这里会选择随机森林算法,由于它相对于决策树来讲,弥补了决策树有可能过拟合的问题。
最后咱们作提交:
>>> submission = pd.DataFrame({"PassengerId": test_df["PassengerId"], "Survived": Y_pred})