一、项目背景和分析需求的提出
泰坦尼克号出事后,收集了乘客的各项数据,包括:
PassengerId、Survived、Pclass、Name、Sex、Age、SibSp、Parch、Ticket、Fare、Cabin、Embarked。
要求用这些数据训练一个能够判断乘客是否生还的二分类器。
二、数据预处理
1.导入数据,熟悉数据
这是进行分析的第一步,我们需要大概了解数据集都有哪些字段,都是什么类型的变量,记录是否完整等。
import pandas as pd
#用pandas库的read_csv()来读取文件,其中('')中的内容如果不在同一个环境下,用绝对路径。
titanic = pd.read_csv('train.csv')
#不包括列名显示前5行,系统从0开始计数
print(titanic.head())
#显示数据的各项基本数字特征:计数、均值、方差等等
print(titanic.describe())
得到结果:
PassengerId Survived Pclass \ 0 1 0 3 1 2 1 1 2 3 1 3 3 4 1 1 4 5 0 3 Name Sex Age SibSp \ 0 Braund, Mr. Owen Harris male 22.0 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 2 Heikkinen, Miss. Laina female 26.0 0 3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 4 Allen, Mr. William Henry male 35.0 0 Parch Ticket Fare Cabin Embarked 0 0 A/5 21171 7.2500 NaN S 1 0 PC 17599 71.2833 C85 C 2 0 STON/O2. 3101282 7.9250 NaN S 3 0 113803 53.1000 C123 S 4 0 373450 8.0500 NaN S
PassengerId Survived Pclass Age SibSp \ count 891.000000 891.000000 891.000000 714.000000 891.000000 mean 446.000000 0.383838 2.308642 29.699118 0.523008 std 257.353842 0.486592 0.836071 14.526497 1.102743 min 1.000000 0.000000 1.000000 0.420000 0.000000 25% 223.500000 0.000000 2.000000 20.125000 0.000000 50% 446.000000 0.000000 3.000000 28.000000 0.000000 75% 668.500000 1.000000 3.000000 38.000000 1.000000 max 891.000000 1.000000 3.000000 80.000000 8.000000 Parch Fare count 891.000000 891.000000 mean 0.381594 32.204208 std 0.806057 49.693429 min 0.000000 0.000000 25% 0.000000 7.910400 50% 0.000000 14.454200 75% 0.000000 31.000000 max 6.000000 512.329200
2.缺失值的处理。
查看每一个变量的是否有缺失:
titanic.count()
得到结果:
PassengerId 891 Survived 891 Pclass 891 Name 891 Sex 891 Age 714 SibSp 891 Parch 891 Ticket 891 Fare 891 Cabin 204 Embarked 889 dtype: int64
可知,除了Age、Cabin、Embarked三个变量外,其他变量均有891个记录,所以需要对这三个有缺失值的变量进行处理。
2.1 缺失比例过高的变量处理
Cabin只有204条记录,缺失值占比太高,直接舍弃。
2.2 数值型变量缺失值补全
数值型变量可选择该变量的各统计特征补全,如:平均值、众数、最大值、最小值等。
这里选择用平均值补全Age缺失的部分。
#数据预处理:1.将有缺失值的列补全。
titanic["Age"] = titanic["Age"].fillna(titanic["Age"].median())
#Age列有很多的缺失值,用.fillna(xxx)用xxx填充Age列,并将结果赋给Age列
print(titanic["Age"].count())
得到结果:
891
2.3 字符型变量缺失值补全
变量Embarked的类型为字符型,我们可以用数量最多的值补全缺失值。先看一下Embarked的各个取值的数量。
#看一下,哪个值的数量最多
titanic.Embarked.value_counts()
得到结果:
S 644 C 168 Q 77 Name: Embarked, dtype: int64
可知,'S'的值最多,我们用'S'来补全缺失的部分。
#统计得出S值最多,所以拿S填充缺失的部分
titanic["Embarked"] = titanic["Embarked"].fillna('S')
print(titanic["Age"].count())
得到结果:
891
3.字符型变量转换为数值型变量。
字符型变量不利于计算机处理,我们需要把字符型变量转换成数值型变量。
3.1 处理变量Sex
对于变量Sex,先观察它有哪些可能值。
#打印Sex列一共有几种可能的值
print (titanic["Sex"].unique())
得到结果:
['male' 'female']
将0赋给male,1赋给female。
titanic.loc[titanic["Sex"] == "male", "Sex"] = 0
titanic.loc[titanic["Sex"] == "female", "Sex"] = 1
3.2 处理变量Embarked
观察可能值。
print(titanic["Embarked"].unique())
得到结果:
['S' 'C' 'Q' nan]
令'S'= 0 ,'C'= 1, 'Q'= 2
titanic.loc[titanic["Embarked"] == "S", "Embarked"] = 0
titanic.loc[titanic["Embarked"] == "C", "Embarked"] = 1
titanic.loc[titanic["Embarked"] == "Q", "Embarked"] = 2
4.准备训练数据集
4.1 挑选变量
选择"Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"七个变量作为训练分类器的数据集。predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
X = titanic[predictors]
y = titanic["Survived"]
4.2 数据标准化
from sklearn import preprocessing
X = preprocessing.scale(X)
至此,数据集准备完毕,(X,y)。
三、随机森林求解。
1.引入交叉验证
将数据集分为4份做交叉验证。
from sklearn import cross_validation
kf = cross_validation.KFold(titanic.shape[0], n_folds=4, random_state=3)
2.随机森林
从sklearn中导入随机森林分类器,计算分类得分。
from sklearn.ensemble import RandomForestClassifier
RFC = RandomForestClassifier(n_estimators=80, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
print(RFCscores.mean())
得到结果:
0.8305508423221427
3.随机森林改进
随机森林分类器中的树的个数,最小样本数及最小子叶数都是可以修改的。我们可以通过循环尝试找出最佳参数。这里以树的个数为例。
from sklearn.ensemble import RandomForestClassifier
a = []
b = []
for i in range(1,100):
RFC = RandomForestClassifier(n_estimators=i, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
a.append(RFCscores.mean())
b.append(i)
print(max(a))
print(a.index(max(a))+1)
得到结果:
0.8406455783137398 34
作折线图看一下树的个数与得分之间的关系。
import matplotlib.pyplot as plt
fig = plt.figure()
plt.plot(b,a)
plt.show()
得到结果:
可以看到,当树的数量为34时,得分最高达到84%。
四、特征工程:提炼特征,重构数据集
1.尝试构造可能与生还有关的特征(自由发挥想象力,一个充满玄学的过程)。
1.1 家庭规模大小
原始数据集中给出了兄弟姐妹SibSp和父母小孩Parch的数量,将这俩个数据加总在一起,可以得出每个乘客的家庭规模大小。
titanic["FamilySize"] = titanic["SibSp"] + titanic["Parch"]
1.2 名字的长度
原始数据中给出了每个乘客的名字,如何利用这一变量?可能名字的长度和生还率有关。
titanic["NameLength"] = titanic["Name"].apply(lambda x: len(x))
1.3 名字的称谓
外国人对于称谓比较严格,或许称谓和生还率也有关系。
#用re.search来匹配称谓
import re
def get_title(name):
title_search = re.search(' ([A-Za-z]+)\.', name)
if title_search:
return title_search.group(1)
return ""
#将称谓保存在titles中
titles = titanic["Name"].apply(get_title)
#将字符型的称谓转换为数值型
title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Dr": 5, "Rev": 6, "Major": 7, "Col": 7, "Mlle": 8, "Mme": 8, "Don": 9, "Lady": 10, "Countess": 10, "Jonkheer": 10, "Sir": 9, "Capt": 7, "Ms": 2}
for k,v in title_mapping.items():
titles[titles == k] = v
#将转换后的称谓添加到原始数据集中
titanic["Title"] = titles
1.4 欣赏一下新构建的数据集都有啥
titanic.head()
得到结果:
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked FamilySize NameLength Title
0 1 0 3 Braund, Mr. Owen Harris 0 22.0 1 0 A/5 21171 7.2500 NaN 0 1 23 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... 1 38.0 1 0 PC 17599 71.2833 C85 1 1 51 3
2 3 1 3 Heikkinen, Miss. Laina 1 26.0 0 0 STON/O2.3101282 7.9250 NaN 0 0 22 2
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) 1 35.0 1 0 113803 53.1000 C123 0 1 44 3
4 5 0 3 Allen, Mr. William Henry 0 35.0 0 0 373450 8.0500 NaN 0 0 24 1
2.用添加了3个特征的数据集重新训练随机森林分类器
#创建数据集
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked","FamilySize", "Title", "NameLength"]
X = titanic[predictors]
y = titanic["Survived"]
#数据标准化
from sklearn import preprocessing
import numpy as np
X = preprocessing.minmax_scale(X,feature_range=(0,1))
#引入交叉验证
from sklearn import cross_validation
kf = cross_validation.KFold(titanic.shape[0], n_folds=4, random_state=3)
#随机森林求解
from sklearn.ensemble import RandomForestClassifier
RFC = RandomForestClassifier(n_estimators=56, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
print(RFCscores.mean())
得到结果:
0.8327929947885104
#随机森林改进
from sklearn.ensemble import RandomForestClassifier
a = []
b = []
for i in range(1,100):
RFC = RandomForestClassifier(n_estimators=i, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
a.append(RFCscores.mean())
b.append(i)
print(max(a))
print(a.index(max(a))+1)
#改进效果图
import matplotlib.pyplot as plt
fig = plt.figure()
plt.plot(b,a)
plt.show()
得到结果:
0.8462560093726013 54
可以看出结果比构造特征之前的结果要提升了0.6%。
3.特征重要性
有时候并不是每一个特征都对分类结果产生一个好的影响,可能去掉一些变量反而能得到更好的结果。这就需要筛选比较重要的特征。
from sklearn.feature_selection import SelectKBest, f_classif
#选择特种中最好的k个
selector = SelectKBest(f_classif, k=6)
selector.fit(X, y)
#将结果映射为容易观察的区间
scores = -np.log10(selector.pvalues_)
#画条形图
plt.bar(range(len(predictors)), scores)
plt.xticks(range(len(predictors)), predictors, rotation='vertical')
plt.show()
得到结果:
可以看出Age、SibSp、Parch、FamilySize对生还的影响不大。用前6个变量训练随机森林分类器,看一下会不会有更好的结果。
#创建数据集
predictors = ["Pclass", "Sex", "Fare", "Embarked", "Title", "NameLength"]
X = titanic[predictors]
y = titanic["Survived"]
#数据标准化
from sklearn import preprocessing
import numpy as np
X = preprocessing.minmax_scale(X,feature_range=(0,1))
#引入交叉验证
from sklearn import cross_validation
kf = cross_validation.KFold(titanic.shape[0], n_folds=4, random_state=3)
#随机森林求解
from sklearn.ensemble import RandomForestClassifier
RFC = RandomForestClassifier(n_estimators=56, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
print(RFCscores.mean())
得到结果:
0.8237991354583283
好像结果并没有变好,看一下改进后的。
#随机森林改进
from sklearn.ensemble import RandomForestClassifier
a = []
b = []
for i in range(1,100):
RFC = RandomForestClassifier(n_estimators=i, min_samples_split=6, min_samples_leaf=1)
RFCscores = cross_validation.cross_val_score(RFC, X, y, cv=kf)
a.append(RFCscores.mean())
b.append(i)
print(max(a))
print(a.index(max(a))+1)
#print(RFCscores.mean())
#改进效果图
import matplotlib.pyplot as plt
fig = plt.figure()
plt.plot(b,a)
plt.show()
得到结果:
0.8350250474689936 44
好吧,说明有时候变量太少,结果也不会很好。
五、还想提高准确率怎么办
做到这一步,随机森林的分类器真的已经尽力了。如果还嫌分不高,想要再往上走一走有什么办法呢?
第一,可以改变分类器,随机森林做不到的事儿,其算法可以做到,其他算法做不到,还有集成算法在后面等着,再不行还有深度学习的算法。
第二,重新构造特征,多尝试几种可能性,特种工程做得好,准确率绝对低不了,低 不 了~。