本文是有关于在泰坦尼克号上的生存概率预测,这是基于Kaggle上的一个经典比赛项目,有兴趣的朋友可以去了解什么是Kaggle以及如何入门(https://www.zhihu.com/question/23987009/answer/285179721)。
在这次项目中,先在Kaggle上下载所需要的训练数据集和测试数据集,通过训练数据集分析什么类型的人能在这场灾难中生存下来,建立机器学习的模型,再使用这个模型预测测试数据集中所有人的生存情况。
本次项目属于一个典型的二分分类问题,采用逻辑回归的方法建立机器学习模型。
一、项目流程
提出问题
理解数据
a. 采集数据
b. 导入数据
c. 查看数据集信息
(describe描述统计信息,info发现缺失值)
- 数据清洗
a. 缺失值处理(缺失值填充fillna)
b. 特征提取(One-hot编码(get_dummies),map函数(Series))
c. 特征选择(相关系数法)
- 构建模型
a. 训练数据和测试数据:train_test_split
b. 机器学习算法:sklearn
c. 评估模型:score
- 方案实施
选取模型进行预测
二、项目具体实现过程
- 提出问题
电影《泰坦尼克号》是由真实事件改编的一部电影,讲述的是1912年4月10日,泰坦尼克号的处女航从英国南安普敦出发,计划驶往美国纽约。但在4月14日深夜,这艘堪称“世界上最大的不沉之船”不幸撞上北大西洋上漂浮的冰山而沉没,船上2208名旅客中,仅有705人生还,生还率不到32%,这也成为人类史上最大的海难之一。
导致这么多人遇难的原因之一是乘客和船员没有足够的救生艇。虽然幸存下来的人有运气成分存在,但有一些人比其他人更有可能生存,比如妇女、儿童和上层阶级。沉船前,除了杰克和露丝的爱情外,泰坦尼克号还经历了什么?
所以我们研究的问题是:什么样的人在泰坦尼克号中更容易存活?
- 理解数据
(1)采集数据
从Kaggle泰坦尼克号项目页面下载数据:https://www.kaggle.com/c/titanic
(2)导入数据
先导入numpy和pandas模块,并读取训练数据集和测试数据集,最后把两个数据集合并在一个数据集full,方便进行数据清洗:(3)查看数据集信息
以下是数据集中出现的一些变量名称及其含义: 数据类型一般划分为数值类型和分类数据两类,其中分类数据也分为有类别和字符串两种,下面是本文中出现的变量的数据类型划分结果: 使用describe查看数据集描述统计信息,对整体数据有一个了解及分析: 使用info查看每一列的数据及数据类型,确定这些数据是否存在缺失值:通过上面的结果发现数据总共有1390行,其中数值类型列:年龄(Age)、船舱号(Cabin)里面有较多缺失数据:
① 年龄(Age)里面数据总数是1046条,缺失了1309-1046=263,缺失率263/1309=20%;
② 船票价格(Fare)里面数据总数是1308条,缺失了1条数据;
字符串列:
③ 登船港口(Embarked)里面数据总数是1307,只缺失了2条数据,缺失比较少;
④ 客舱号(Cabin)里面数据总数是295,缺失了1309-295=1014,缺失率=1014/1309=77.5%,缺失比较大。
⑤ 生存情况(Survived)里面只有891条数据,这是因为只有训练数据集中才有Survived,通过训练数据集中Survived作为标签构建机器学习的模型,最终预测出测试数据集中的Survived情况,这也是本次项目的目的,所以不需要处理这一列的数据。
以上的结果为我们下一步数据清洗指明了方向,只有知道哪些数据缺失数据,我们才能有针对性的处理。
- 数据清洗
(1)处理缺失值
① 数值型缺失值处理
年龄(Age)和船票价格(Fare)都存在缺失值,且数据类型都为浮点型,可以使用平均值填充的方法填充缺失值,其中fillna表示数据填充的方法,mean表示使用平均值填充:② 字符串类型缺失值处理
客舱号(Cabin)的数据类型为字符串,通过查看客舱号(Cabin)这一列的数据发现缺失数值较多,缺失率达到77.5%,且无规律可循,这里可以直接填充为U,表示未知(Unknown):③ 有类别数据缺失值处理
登船港口(Embarked)属于有类别数据,共分为三类,从结果中可以看出这一列只有两个缺失值,可以使用最频繁出现的值填充: 最后使用info()函数查看最终缺失值处理情况:从结果中看出,所有缺失值已处理完成,接下来是提取特征。
(2)特征提取
什么是特征工程?
特征工程指的是最大限度地从原始数据中提取特征,以供机器学习算法和模型使用。
特征工程处理方法: 特征提取方法:这里主要利用One-host的方法对特征值进行分类提取。
提取性别(Sex)的特征值: 提取登船港口(Embarked)的特征值: 提取客舱等级(Pclass)的特征值: 从姓名(Name)中提取头衔的特征值: 提取客舱号(Cabin)的特征值: 提取家庭类别的特征值: 提取年龄(Age)的特征值: 最后查看现在已有的特征:(3)特征选择
通过计算各个特征与标签的相关系数,进行特征选择,先求出相关性矩阵: 查看各个特征与生成情况(Survived)的相关系数:根据各个特征与生存情况(Survived)的相关系数大小,选择以下几个特征作为模型的输入:
- 构建模型
使用训练数据和机器学习算法得到一个机器学习模型,再使用测试数据评估模型
(1)建立训练数据集和测试数据集
① 坦尼克号测试数据集因为是我们最后要提交给Kaggle的,里面没有生存情况的值,所以不能用于评估模型。
我们将Kaggle泰坦尼克号项目给我们的测试数据,叫做预测数据集(记为pred,也就是预测英文单词predict的缩写)。也就是我们使用机器学习模型来对其生存情况就那些预测。使用Kaggle泰坦尼克号项目给的训练数据集,做为我们的原始数据集(记为source): ② 从原始数据集(source)中拆分出训练数据集(记为train:用于模型训练)和测试数据集(记为test:用于模型评估): ③ 查看原始数据集标签:(2)选择机器学习算法
根据之前筛选出的特征值,选择逻辑回归(Logistic Regression)算法训练模型: (3)训练模型(4)评估模型
逻辑回归属于分类问题,使用score得到模型正确率:从结果中可以看到,模型的正确率为84.92%,正确率还是很高的,接下来使用这个模型预测在预测数据集中的生存率。
- 实施方案
总结
通过这个泰坦尼克号生存预测项目的练习,我从中学到的两点:
一、理解机器学习整个过程,尤其是提取特征时使用One-hot编码(get_dummies)对特征的处理以及机器学习算法的运用;
二、利用kaggle平台学习别人的代码,体会其中的思路,完善自身的不足,提高自学效率。
源码:
**# coding: utf-8**
**# ## 1、导入数据**
**# In[1]:**
**import numpy as np**
**import pandas as pd**
**# In[2]:**
**# 读取训练数据集**
**train = pd.read_csv('D:/Python/notebook/train.csv')**
**# 读取测试数据集**
**test = pd.read_csv('D:/Python/notebook/test.csv')**
**print('训练数据集',train.shape,'测试数据集',test.shape)**
**# In[3]:**
**# 合并数据集,方便同时对两个数据集进行清洗**
**full = train.append(test,ignore_index=True)**
**print('合并后的数据集',full.shape)**
**# ## 2、查看数据集信息**
**# In[4]:**
**# 查看数据**
**full.head()**
**# In[5]:**
**# 获取数据类型列的描述统计信息**
**full.describe()**
**# In[6]:**
**# 查看每一列的数据类型,和数据总数**
**full.info()**
**# ## 3、数据清洗**
**# ### (1)处理缺失值**
**# 数值型缺失值处理**
**# In[7]:**
**'''**
**年龄(Age)和船票价格(Fare)存在缺失值且都为浮点型数据,**
**可以使用平均值填充的方法填充缺失值**
**'''**
**# 年龄(Age)**
**full['Age'] = full['Age'].fillna(full['Age'].mean())**
**# 船票价格(Fare)**
**full['Fare'] = full['Fare'].fillna(full['Fare'].mean())**
**# 字符串类型缺失值处理**
**# In[8]:**
**# 填充客舱号(Cabin),先查看里面的数据**
**full['Cabin'].head()**
**# In[9]:**
**'''**
**客舱号(Cabin)这一列缺失数值较多且无规律可循,**
**直接填充为U,表示未知(Unknown)**
**'''**
**full['Cabin'] = full['Cabin'].fillna('U')**
**# 有类别数据缺失值处理**
**# In[10]:**
**'''**
**先查看 前5行的信息,看看数据的样子**
**出发地点:S=英国 南安普顿Southampton**
**途径地点1:C=法国 瑟堡市Cherbourg**
**途径地点2:Q=爱尔兰 昆士敦Queenstown**
**'''**
**full['Embarked'].head()**
**# In[11]:**
**'''**
**由前面的信息可知,登船港口(Embarked)这一列只有两个缺失值,**
**将缺失值填充为最频繁出现的值:S=英国 南安普顿Southampton**
**'''**
**full['Embarked'] = full['Embarked'].fillna('S')**
**# In[12]:**
**# 查看最终缺失值处理情况**
**full.info()**
**# ### (2)特征提取**
**# #### a.分类数据特征提取:性别**
**# In[13]:**
**'''**
**将性别的值映射为数值**
**男(male)对应数值 1**
**女(female)对应数值 0**
**'''**
**sex_mapDict = {'male':1,**
** 'female':0}**
**# map函数:对于Series每个数据应用自定义函数计算**
**full['Sex'] = full['Sex'].map(sex_mapDict)**
**full.head()**
**# #### b.分类数据特征提取:登船港口**
**# In[14]:**
**# 查看该类数据内容**
**full['Embarked'].head()**
**# In[15]:**
**# 存放提取后的特征**
**embarkedDf = pd.DataFrame()**
**# 使用get_dummies进行one-hot编码,列名前缀为Embarked**
**embarkedDf = pd.get_dummies(full['Embarked'],prefix='Embarked')**
**embarkedDf.head()**
**# In[16]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,embarkedDf],axis=1)**
**'''**
**因为已经使用登船港口(embarkedDf)进行了one-hot编码产生虚拟变量(dummy variables)**
**所以这里把登船港口(embarkedDf)删除**
**'''**
**full.drop('Embarked',axis=1,inplace=True)**
**full.head()**
**# #### c.分类数据特征提取:客舱等级**
**# In[17]:**
**# 存放提取后的特征**
**pclassDf = pd.DataFrame()**
**# 使用get_dummies进行one-hot编码,列名前缀为Pclass**
**pclassDf = pd.get_dummies(full['Pclass'],prefix='Pclass')**
**pclassDf.head()**
**# In[18]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,pclassDf],axis=1)**
**# 删除客舱等级(Pclass)这一列**
**full.drop('Pclass',axis=1,inplace=True)**
**full.head()**
**# #### d.分类数据特征提取:姓名**
**# In[19]:**
**full['Name'].head()**
**# In[20]:**
**# 定义函数: 从姓名中获取头衔**
**# split()通过指定分隔符对字符串进行切片**
**def getTitle(name): # Braund, Mr. Owen Harris**
** str1 = name.split(',')[1] # Mr. Owen Harris**
** str2 = str1.split('.')[0] # Mr**
** # strip()方法用于移除字符串头尾指定的字符(默认为空格)**
** str3 = str2.strip()**
** return str3**
**# 存放提取后的特征**
**titleDf = pd.DataFrame()**
**# map函数:对于Series每个数据应用自定义函数计算**
**titleDf['Title'] = full['Name'].map(getTitle)**
**titleDf.head()**
**# In[21]:**
**# 姓名中头衔字符串与定义头衔类别的映射关系**
**title_mapDict = {**
** 'Capt': 'Officer',**
** 'Col': 'Officer',**
** 'Major': 'Officer',**
** 'Jonkheer': 'Royalty',**
** 'Don': 'Royalty',**
** 'Sir': 'Royalty',**
** 'Dr': 'Officer',**
** 'Rev': 'Officer',**
** 'the Countess':'Royalty',**
** 'Dona': 'Royalty',**
** 'Mme': 'Mrs',**
** 'Mlle': 'Miss',**
** 'Ms': 'Mrs',**
** 'Mr': 'Mr',**
** 'Mrs': 'Mrs',**
** 'Miss': 'Miss',**
** 'Master': 'Master',**
** 'Lady': 'Royalty'**
**}**
**# map函数:对于Series每个数据应用自定义函数计算**
**titleDf['Title'] = titleDf['Title'].map(title_mapDict)**
**# 使用get_dummies进行one-hot编码**
**titleDf = pd.get_dummies(titleDf['Title'])**
**titleDf.head()**
**# In[22]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,titleDf],axis=1)**
**# 删除姓名(Name)这一列**
**full.drop('Name',axis=1,inplace=True)**
**full.head()**
**# #### e.分类数据特征提取:客舱号**
**# In[23]:**
**full['Cabin'].head()**
**# In[24]:**
**# 存放客舱号信息**
**cabinDf = pd.DataFrame()**
**'''**
**客舱号的类别值是首字母,例如:**
**C85 类别映射为首字母C**
**'''**
**full['Cabin'] = full['Cabin'].map(lambda c:c[0]) # 定义匿名函数lambda,用于查找首字母**
**# 使用get_dummies进行one-hot编码,列名前缀为Cabin**
**cabinDf = pd.get_dummies(full['Cabin'],prefix='Cabin')**
**cabinDf.head()**
**# In[25]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,cabinDf],axis=1)**
**# 删除客舱号(Cabin)这一列**
**full.drop('Cabin',axis=1,inplace=True)**
**full.head()**
**# #### f.分类数据特征提取:家庭类别**
**# In[26]:**
**# 存放家庭信息**
**familyDf = pd.DataFrame()**
**'''**
**家庭人数 = 同代直系亲属数(SibSp)+ 不同代直系亲属数(Parch)+ 乘客自己**
**(乘客自己也属于家庭成员一个,所以要加1)**
**'''**
**familyDf['Familysize'] = full['SibSp'] + full['Parch'] + 1**
**'''**
**家庭类别:**
**小家庭Family_Single:家庭人数=1**
**中等家庭Family_Small:2<=家庭人数<=4**
**大家庭Family_Large:家庭人数>=5**
**'''**
**# if条件为真时返回if前面内容,否则返回0**
**familyDf['Family_Single'] = familyDf['Familysize'].map(lambda s: 1 if s==1 else 0)**
**familyDf['Family_Small'] = familyDf['Familysize'].map(lambda s: 1 if 2 <= s <= 4 else 0)**
**familyDf['Family_Large'] = familyDf['Familysize'].map(lambda s: 1 if 5 <= s else 0)**
**familyDf.head()**
**# In[27]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,familyDf],axis=1)**
**full.head()**
**# #### g.分类数据特征提取:年龄**
**# In[28]:**
**# 存放年龄信息**
**ageDf = pd.DataFrame()**
**'''**
**年龄类别:**
**儿童Child:0<年龄<=6**
**青少年Teenager:6<年龄<18**
**青年Youth:18<=年龄<=40**
**中年Middle_aged:40<年龄<=60**
**老年Older:60<年龄**
**'''**
**# if条件为真时返回if前面内容,否则返回0**
**ageDf['Child'] = full['Age'].map(lambda a: 1 if 0 < a <= 6 else 0)**
**ageDf['Teenager'] = full['Age'].map(lambda a: 1 if 6 < a < 18 else 0)**
**ageDf['Youth'] = full['Age'].map(lambda a: 1 if 18 <= a <= 40 else 0)**
**ageDf['Middle_aged'] = full['Age'].map(lambda a: 1 if 40 < a <= 60 else 0)**
**ageDf['Older'] = full['Age'].map(lambda a: 1 if 60 < a else 0)**
**ageDf.head()**
**# In[29]:**
**# 添加one-hot编码产生的虚拟变量(dummy variables)到泰坦尼克号数据集full**
**full = pd.concat([full,ageDf],axis=1)**
**# 删除年龄(Age)这一列**
**full.drop('Age',axis=1,inplace=True)**
**full.head()**
**# In[30]:**
**# 查看现在已有的特征**
**full.shape**
**# ### (3)特征选择**
**# 相关性矩阵**
**# In[31]:**
**# 相关性矩阵**
**corrDf = full.corr()**
**corrDf**
**# In[32]:**
**'''**
**查看各个特征与生成情况(Survived)的相关系数,**
**ascending = False 表示按降序排列**
**'''**
**corrDf['Survived'].sort_values(ascending = False)**
**# 根据各个特征与生存情况(Survived)的相关系数大小,选择以下几个特征作为模型的输入:**
**# 头衔(titleDf)、客舱等级(pclassDf)、家庭大小(familyDf)、船票价格(Fare)、性别(Sex)、客舱号(cabinDf)、登船港口(embarkdeDf)**
**# In[33]:**
**# 特征选择**
**full_X = pd.concat([titleDf, # 头衔**
** pclassDf, # 客舱等级**
** familyDf, # 家庭大小**
** full['Fare'],# 船票价格**
** full['Sex'], # 性别**
** cabinDf, # 客舱号**
** embarkedDf # 登场港口**
** ],axis=1)**
**full_X.head()**
**# ## 4、构建模型**
**# 使用训练数据和机器学习算法得到一个机器学习模型,再使用测试数据评估模型**
**# ### (1)建立训练数据集和测试数据集**
**# In[34]:**
**# 原始数据共有891行**
**sourceRow = 891**
**'''**
**原始数据集sourceRow是从Kaggle下载的训练数据集,可知共有891条数据**
**从特征集合full_X中提取原始数据集前891行数据时需要减去1,因为行号是从0开始**
**'''**
**# 原始数据集:特征**
**source_X = full_X.loc[0:sourceRow-1,:]**
**# 原始数据集:标签**
**source_y = full.loc[0:sourceRow-1,'Survived']**
**# 测预测数据集:特征**
**pred_X = full_X.loc[sourceRow:,:]**
**# In[35]:**
**# 查看原始数据集有多少行**
**print('原始数据集有多少行:',source_X.shape[0])**
**# 查看预测数据集有多少行**
**print('预测数据集有多少行:',pred_X.shape[0])**
**# In[36]:**
**'''**
**从原始数据集(source)中拆分出用于模型训练的训练数据集(train),用于评估模型的测试数据集(test)**
**train_test_split:是交叉验证中常用的函数,功能是从样本中随机按比例选取train data和test data**
**train_data:所要划分的样本特征集**
**train_target:所要划分的样本结果**
**train_size:样本占比,如果为整数则是样本的数量**
**'''**
**from sklearn.cross_validation 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)**
**print('原始数据集标签',source_y.shape,**
** '训练数据集标签',train_y.shape,**
** '测试数据集标签',test_y.shape)**
**# In[37]:**
**# 查看原始数据集标签**
**source_y.head()**
**# ### (2)选择机器学习算法**
**# 选择一个机器学习算法,用于模型训练,这里选择逻辑回归(logisic regression)**
**# In[38]:**
**# 第1步:导入算法**
**from sklearn.linear_model import LogisticRegression**
**# 第2步:创建模型:逻辑回归(logisic regression)**
**model = LogisticRegression()**
**# In[39]:**
**# 随机森林Random Forests Model**
**#from sklearn.ensemble import RandomForestClassifier**
**#model = RandomForestClassifier(n_estimators=100)**
**#0.7765**
**# In[40]:**
**# 支持向量机Support Vector Machines**
**#from sklearn.svm import SVC,LinearSVC**
**#model = SVC()**
**#0.78**
**# In[41]:**
**# Gradient Boosting Classifier**
**#from sklearn.ensemble import GradientBoostingClassifier**
**#model = GradientBoostingClassifier()**
**#0.81**
**# In[42]:**
**#K-nearest neighbors**
**#from sklearn.neighbors import KNeighborsClassifier**
**#model = KNeighborsClassifier(n_neighbors = 3)**
**#0.75**
**# In[43]:**
**# 朴素贝叶斯Gaussian Naive Bayes**
**#from sklearn.naive_bayes import GaussianNB**
**#model = GaussianNB()**
**#0.81**
**# ### (3)训练模型**
**# In[44]:**
**# 第3步:训练模型**
**model.fit(train_X,train_y)**
**# ### (4)评估模型**
**# In[45]:**
**# 分类问题,score得到的是模型正确率**
**model.score(test_X,test_y)**
**# ## 5、实施方案**
**# 使用预测数据集进行预测结果,并保存到csv文件中,最后上传到Kaggle中**
**# In[46]:**
**# 使用机器学习模型,对预测数据集中的生存情况进行预测**
**pred_y = model.predict(pred_X)**
**'''**
**生成的预测值是浮点数(0.0,1.0)**
**但是Kaggle要求提交的结果是整型(0,1)**
**使用astype对数据类型进行转换**
**''' **
**pred_y = pred_y.astype(int)**
**# 乘客id**
**passenger_id = full.loc[sourceRow:,'PassengerId']**
**# 数据框:乘客id,预测生存情况**
**predDf = pd.DataFrame({'PassengerId':passenger_id, 'Survived':pred_y})**
**predDf.shape**
**predDf.head()**
**# In[47]:**
**# 保存结果**
**predDf.to_csv('D:/Python/notebook/titanic_pred.csv',index=False)**