一、项目背景和分析需求的提出
泰坦尼克号出事后,收集了乘客的各项数据,包括:
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
量可选择该变量的各统计特征补全,如:平均值、众数、最大值、最小值等。
#数据预处理:1.将有缺失值的列补全。 titanic["Age"] = titanic["Age"].fillna(titanic["Age"].median()) #Age列有很多的缺失值,用.fillna(xxx)用xxx填充Age列,并将结果赋给Age列 print(titanic["Age"].count())
得到结果:
变量Embarked的类型为字符型,我们可以用数量最多的值补全缺失值。先看一下Embarked的各个取值的数量。
#看一下,哪个值的数量最多 titanic.Embarked.value_counts()
得到结果:
S 644 C 168 Q 77 Name: Embarked, dtype: int64
#统计得出S值最多,所以拿S填充缺失的部分 titanic["Embarked"] = titanic["Embarked"].fillna('S') print(titanic["Age"].count())得到结果:
3.字符型变量转换为数值型变量。
#打印Sex列一共有几种可能的值 print (titanic["Sex"].unique())
得到结果:
['male' 'female']
titanic.loc[titanic["Sex"] == "male", "Sex"] = 0 titanic.loc[titanic["Sex"] == "female", "Sex"] = 1
print(titanic["Embarked"].unique())得到结果:
['S' 'C' 'Q' nan]
titanic.loc[titanic["Embarked"] == "S", "Embarked"] = 0 titanic.loc[titanic["Embarked"] == "C", "Embarked"] = 1 titanic.loc[titanic["Embarked"] == "Q", "Embarked"] = 2
4.准备训练数据集
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"] X = titanic[predictors] y = titanic["Survived"]
from sklearn import preprocessing X = preprocessing.scale(X)
三、随机森林求解。
1.引入交叉验证
from sklearn import cross_validation kf = cross_validation.KFold(titanic.shape[0], n_folds=4, random_state=3)
2.随机森林
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.尝试构造可能与生还有关的特征(自由发挥想象力,一个充满玄学的过程)。
原始数据集中给出了兄弟姐妹SibSp和父母小孩Parch的数量,将这俩个数据加总在一起,可以得出每个乘客的家庭规模大小。
titanic["FamilySize"] = titanic["SibSp"] + titanic["Parch"]
原始数据中给出了每个乘客的名字,如何利用这一变量?可能名字的长度和生还率有关。
titanic["NameLength"] = titanic["Name"].apply(lambda x: len(x))
外国人对于称谓比较严格,或许称谓和生还率也有关系。
#用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
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
好吧,说明有时候变量太少,结果也不会很好。
五、还想提高准确率怎么办
做到这一步,随机森林的分类器真的已经尽力了。如果还嫌分不高,想要再往上走一走有什么办法呢?
第一,可以改变分类器,随机森林做不到的事儿,其算法可以做到,其他算法做不到,还有集成算法在后面等着,再不行还有深度学习的算法。
第二,重新构造特征,多尝试几种可能性,特种工程做得好,准确率绝对低不了,低 不 了~。
文章来源: Kaggle实战:随机森林预测泰坦尼克生存率