本次笔记主要记录了一个机器学习的入门实战,泰坦尼克号生存预测。主要涉及的知识点有,python的pandas包,机器学习包sklearn。
背景知识:
泰坦尼克号是英国一艘奥林匹克级邮轮,在1912年4月10日,从南安普顿港码头出发,途径法国和爱尔兰,在驶向美国纽约的时候,与一座冰山相撞,最终沉没。在2224名船员及乘客中,逾1500人丧生。本次案例主要是通过机器学习方法对418人的生存状况进行预测。案例取自kaggle: https://www.kaggle.com/c/titanic。同时,kaggle上提供了泰坦尼克号案例的训练集train.csv和测试集test.csv。
一、提出问题
数据取自kaggle,本次问题主要是探究泰坦尼克号中,各个特征与生存状况的关系,并利用机器学习方法对生存状况进行预测。
二、 理解数据
首先导入数据集train.csv和test.csv:
import pandas as pd
import numpy as np
#导入训练集
train = pd.read_csv("train.csv")
#导入测试集
test = pd.read_csv("test.csv")
#查看测试集和训练集数据形状
print("Train data: ",train.shape)
print("Test data: ",test.shape)
full=train.append(test,ignore_index=True,sort=False)
print("Full data:", full.shape)
full.head()
由以上代码可以得出,训练数据为891行,12个字段,测试数据为418行,11个字段,缺少的这一行字段便是要预测的生存状况。
以下给出12个字段的含义解释:
1. PassengerId:乘客ID
2. Survived:生存状况;1:生存;死亡:0
3. Pclass:船舱等级;1:一等;2:二等;3:三等
4. Name:乘客姓名
5. Sex:乘客性别
6. Age:乘客年龄
7. SibSp:乘客的兄弟姐妹以及配偶数量
8. Parch:乘客的父母与子女数量
9. Ticket:票的编号
10. Fare:船票费用
11. Cabin:乘客船舱座位号
12. Embarked:乘客登船的港口,有三个可选值:S:南安普顿,C:法国瑟堡Q:爱尔兰昆士敦
full.info()
--------------------------------------------------
RangeIndex: 1309 entries, 0 to 1308
Data columns (total 12 columns):
PassengerId 1309 non-null int64
Survived 891 non-null float64
Pclass 1309 non-null int64
Name 1309 non-null object
Sex 1309 non-null object
Age 1046 non-null float64
SibSp 1309 non-null int64
Parch 1309 non-null int64
Ticket 1309 non-null object
Fare 1308 non-null float64
Cabin 295 non-null object
Embarked 1307 non-null object
dtypes: float64(3), int64(4), object(5)
memory usage: 122.8+ KB
通过分析可以得出,总数据为1309,其中Survived, Age, Fare, Cabin, Embarked均有缺失值,其中Survived的缺失值是所需要求的生存状况,其他值则需要通过数据清洗的方法来补全。
三、数据清洗
- Fare为数值类型,缺失值为1,因此采用平均值进行填充。
- Embarked为字符串类型,缺失值为2,因此采用众数进行填充。
- Cabin为字符串类型,缺失过多,缺失值为1014,缺失率为77.46%,原因可能为很多乘客没有舱位号,因此将缺失值用U来替代,表示未知。
- Age为数值类型,缺失值为263,本次采用平均值进行填充,由于年龄对于生存影响可能相对较大,因此下次会介绍如何使用机器学习方法进行填充。
# Fare缺失值填充
full.Fare = full.Fare.fillna(full.Fare.mean())
# Embarked缺失值填充
full.Embarked = full.Embarked.fillna(full.Embarked.mode()[0])
# Cabin缺失值填充
full.Cabin = full.Cabin.fillna('U')
# Age本次使用平均值填充
full.Age = full.Age.fillna(full.Age.mean())
在处理完缺失值以后,还需要对字符串类型的数值进行one-hot编码,方便机器学习算法识别。这里对Sex, Cabin, Embarked进行重新编码。
# 对Sex进行编码
sex = {'male':1,'female':0}
sex_num = full.Sex.map(sex)
sex_num.head()
# 对Embarked进行编码
embarked = pd.DataFrame()
embarked = pd.get_dummies(full.Embarked,prefix="Embarked")
embarked.head()
# 对Pcclass进行编码
pclass = pd.DataFrame()
pclass = pd.get_dummies(full.Pclass,prefix="Pclass")
pclass.head()
接下来将对名字的头衔进行提取,因为不同的头衔和职位可能会对生存情况产生影响。同时,在提取名字头衔后,会定义一个新字典作为身份,用来映射头衔与身份的关系,并对身份进行one-hot编码。
# 提取名字中的头衔
title = pd.DataFrame()
title['title'] = full.Name.map(lambda name:name.split(',')[1].split('.')[0].strip())
# 用groupby方法列出各种头衔
title.groupby('title').count()
# 创建身份字典
position = {
'Capt':'Officer',
'Col':'Officer',
'Dr' :'Officer',
'Don':'Royalty',
'Dona':'Royalty',
'Jonkheer':'Royalty',
'Lady':'Miss',
'Major':'Officer',
'Master':'Master',
'Mlle':'Miss',
'Mme':'Mrs',
'Mr':'Mr',
'Mrs':'Mrs',
'Ms':'Miss',
'Rev':'Officer',
'Sir':'Royalty',
'the Countess':'Royalty'
}
# 将字典映射到头衔上
title['title'] = title['title'].map(position)
------------------------------
title
Master 61
Miss 5
Mr 757
Mrs 198
Officer 23
Royalty 5
Name: Name, dtype: int64
# 进行one-hot编码
title = pd.get_dummies(title['title'])
完成上述步骤后,可以获得one-hot表格如下:
接下来对Cabin列进行分类处理,首先提取首字母,然后按照首字母进行one-hot编码。
# 提取cabin首字母
cabin = full.Cabin.map(lambda cabin:cabin[0])
# 进行one-hot编码
cabin = pd.get_dummies(cabin,prefix='Cabin')
Parch字段和SibSp字段,其实可以归类为家庭成员的个数,可以将两者相加组成一个新字段作为家庭成员数量
family = full.Parch+full.SibSp +1
最后,将原始数据复制保留一份,并将重新编码的字段包括(Pclass, Name,Sex,Age,SibSp,Parch,Cabin,Embarked)以及无用字段Ticket删除。
# 复制一份原始数据
full_origin = train.append(test,ignore_index=True,sort=False)
# 建立一份新数据继续继续操作
full_data = full
# 删除无用字段及重新编码字段
full_data = full_data.drop(['Cabin','Name','Sex','Pclass','Embarked','Parch','SibSp','Ticket'],axis=1)
# 将重新编码后的字段加入新数据
full_data = pd.concat([full_data,sex_num,embarked,pclass,title,cabin,family],axis=1)
# 最终得到数据如下
--------------------------------------------------
RangeIndex: 1309 entries, 0 to 1308
Data columns (total 27 columns):
PassengerId 1309 non-null int64
Survived 891 non-null float64
Age 1309 non-null float64
Fare 1309 non-null float64
Sex 1309 non-null int64
Embarked_C 1309 non-null uint8
Embarked_Q 1309 non-null uint8
Embarked_S 1309 non-null uint8
Pclass_1 1309 non-null uint8
Pclass_2 1309 non-null uint8
Pclass_3 1309 non-null uint8
Master 1309 non-null uint8
Miss 1309 non-null uint8
Mr 1309 non-null uint8
Mrs 1309 non-null uint8
Officer 1309 non-null uint8
Royalty 1309 non-null uint8
Cabin_A 1309 non-null uint8
Cabin_B 1309 non-null uint8
Cabin_C 1309 non-null uint8
Cabin_D 1309 non-null uint8
Cabin_E 1309 non-null uint8
Cabin_F 1309 non-null uint8
Cabin_G 1309 non-null uint8
Cabin_T 1309 non-null uint8
Cabin_U 1309 non-null uint8
Family 1309 non-null int64
dtypes: float64(3), int64(3), uint8(21)
memory usage: 88.3 KB
四、 建模与评估
- 首先利用corr()函数建立相关系数矩阵,并提取出生存字段的相关系数并进行排序。
# 建立相关系数矩阵
corrDf = full_data.corr()
# 对生存字段的相关系数进行排序
print(corrDf['Survived'].sort_values(ascending=False))
----------------------------------------
Survived 1.000000
Mrs 0.341994
Pclass_1 0.285904
Fare 0.257307
Cabin_B 0.175095
Embarked_C 0.168240
Cabin_D 0.150716
Cabin_E 0.145321
Cabin_C 0.114652
Pclass_2 0.093349
Master 0.085221
Miss 0.085083
Cabin_F 0.057935
Cabin_A 0.022287
Family 0.016639
Royalty 0.016040
Cabin_G 0.016040
Embarked_Q 0.003650
PassengerId -0.005007
Cabin_T -0.026456
Officer -0.031316
Age -0.070323
Embarked_S -0.149683
Cabin_U -0.316912
Pclass_3 -0.322308
Sex -0.543351
Mr -0.549199
Name: Survived, dtype: float64
经过排序后发现,呈正相关的字段有:头衔,客舱等级,船票价格,船舱号等;呈负相关的的字段有:头衔,性别,客舱等级,船舱号。
- 原训练数据为891行,因此将处理后的新数据集拆分成训练集和测试集
from sklearn.cross_validation import train_test_split
from sklearn.linear_model import LogisticRegression
# 训练数据数量
trainNum = 891
# 截取训练数据
data_X = full_data.loc[0:trainNum-1,:]
data_X = data_X.drop('Survived',axis=1)
data_y = full_data.loc[0:trainNum-1,'Survived']
# 配置训练数据和测试数据
X_train,X_test,y_train,y_test = train_test_split(data_X,data_y,train_size=0.8)
print('Orginal data x:', data_X.shape)
print('Train data x:',X_train.shape)
print('Test data x:',X_test.shape)
print('Orginal data y: ', data_y.shape)
print('Train data y: ',y_train.shape)
print('Test data y: ',y_test.shape)
- 导入模型算法,代入数据训练模型
# 导入逻辑回归模型
model = LogisticRegression()
# 使用训练数据训练模型
model.fit(X_train, y_train)
# 测试模型准确度
print(model.score(X_test,y_test))
利用训练数据中的测试集进行测试,发现模型准确率为86.6%
- 利用训练好的模型对测试集进行预测
# 取出测试集
X_predict = full_data.loc[trainNum:,:]
X_predict = X_predict.drop(['Survived'],axis=1)
# 将测试基代入模型进行预测
y_predict = model.predict(X_predict)
# 对测试集进行格式化
y_predict = y_predict.astype(int)
# 将预测结果用passengerId进行保存
passengerId = full_data.loc[trainNum:,'PassengerId']
predictDf = pd.DataFrame({'PassengerId':passengerId,'Survived':y_predict})