Kaggle泰坦尼克数据科学解决方案
Posted ZacksTang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kaggle泰坦尼克数据科学解决方案相关的知识,希望对你有一定的参考价值。
原文地址如下:
https://www.kaggle.com/startupsci/titanic-data-science-solutions
----------------------------------------------------------------
泰坦尼克数据科学解决方案:
1. 工作流程步骤:
在 Data Science Solutions book 这本书里,描述了在解决一个竞赛问题时所需要做的具体工作流程:
- 问题的定义
- 获取训练数据以及测试数据
- 加工、准备以及清洗数据
- 分析、识别数据的模式,并对数据做可视化
- 建模、预测,并解决问题
- 对结果做可视化,生成报告,并且展示问题的解决步骤和最终的解决方案
- 提交结果
以上的工作流程仅仅描述了一般的问题解决步骤,然而依然会有一些特殊的案例并不严格遵循以上流程:
- 我们可能会结合多个步骤。例如通过将数据可视化后直接进行分析
- 将某个步骤提前执行。例如在做数据加工前即开始对数据进行分析
- 在工作流程中多次进行某个步骤。例如在整个流程中会多次对数据进行可视化
- 完全弃用一个步骤。例如在非竞赛场合可能并不需要做提交结果的步骤
2. 问题的定义:
一般的竞赛网站(如 Kaggle)会在提供训练数据以及测试数据的同时,给出问题的定义。对与此次的“泰坦尼克号幸存者”问题的定义如下:
“在训练数据中,提供了在泰坦尼克号上乘客的具体数据以及他们是否在那次灾难中存活的信息。参赛者能否通过已有的训练数据训练出一个模型,此模型需要根据输入的测试数据里乘客信息,来预测此乘客是否能在灾难中存活”
我们可能也想要通过问题描述获取更多有关此问题的信息。在此问题的描述中,比较有意义的描述如下:
- 泰坦尼克号在1912年4月15日与冰山碰撞后沉没。在一共2224名乘客与船员里,有1502人不幸逝世。这个信息即表明了此次事件中生还率为32%。
- 一个使得在这次灾难中有如此之大死亡率的原因是:在船上没有足够的救生船提供给乘客以及船员
- 尽管在此次灾难中生还存在运气的成分,但是仍旧会有些群体的生还率高于其他人,如女人、小孩,以及上等仓的人
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值或者空值很可能需要修正。
在这个问题中:
- 这些特征包含null值的数量大小为:Cabin > Age > Embarked
- 在训练集里有不完整数据的数量的大小为:Cabin > Age
7.每个特征下的数据类型是什么?
这个可以在我们做数据转换时起到较大的帮助。
在这个问题中:
- 有7个特征是int型或float 型。在测试数据集里有6个
- 有5个特征是string(object)类型
>>> 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. 在样本里,数值型特征的数值分布是什么样的?
这个可以帮助我们初步了解:训练数据集如何体现了实际问题。
在这个问题中:
- 一共有891个样本
- Survived的标签是通过0或1来区分
- 大概38%的样本是survived
- 大多数乘客(>75%)没有与父母或是孩子一起旅行
- 大约30%的乘客有亲属和/或配偶一起登船
- 票价的差别非常大,少量的乘客(<1%)付了高达$512的费用
- 很少的乘客(<1%)年纪在64-80之间
我们可以通过以下方式获取上述信息:
>>> 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. 在样本里,离散型数据的分布是什么?
在这个问题中:
- 各个乘客的Name 属性完全是唯一的(count=unique=891)
- Sex特征里65%为男性(top=male,fre=577/count=891)
- Cabin的count与unique并不相等,即说明有些乘客会共享一个cabin
- Embarked一共有种取值,其中从S港口登船的人最多
- Ticket的特征下,有22%左右的重复值(unique=681)
可以通过以下方法获得以上信息:
>>> 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的相关性如何。我们希望能够今早的做这一步,并且将这些相关性特征匹配到建模后的相关性特征上。
补全数据:
- 我们可能会去补全Age特征下的数据,因为它一定是与存活率是相关的
- 我们可能会去补全Embarked特征下的数据,因为它可能与存活率或者其他重要的特征之间存在相关性
修正数据:
- Ticket特征可能需要从我们的分析中丢弃,因为它的数值重复率高达22%,并且Ticket与survival之间很可能并没有联系
- Cabin特征可能也需要丢弃,因为它的数值非常不完整,并且在训练集以及测试集里均包含较多的null值
- PassengerId特征可能也需要被丢弃,因为它对survival没任何作用
- Name特征相对来说不是特别规范,并且很有可能与survival之间没有直接联系,所以可能也应该被丢弃
创造数据:
- 我们可以根据Parch和SibSp的特征来创建一个新的Family特征,以此得到每个乘客有多少家庭成员登了船
- 我们可以对Name特征做进一步加工,提取出名字里的Title作为一个新的特征
- 我们可以为Age特征创建一个新的特征,将它原本的连续型数值特征转换为有序的离散型特征
- 我们也可以创建一个票价(Fare)范围的特征,如果它对我们的分析有帮助的话
分类:
根据之前的问题描述或者已有的数据,我们也可以提出以下假设:
- 女人(Sex=female)更有可能存活
- 孩子(Age<?)也更有可能存活
- 上等仓的乘客(Pclass=1)有更大的存活率
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
0Kaggle泰坦尼克-Python