首页 > 技术文章 > 决策树——泰坦尼克号生存者预测

victorywr 2020-05-14 08:43 原文

import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt

data=pd.read_csv('data .csv')  #导入数据
#删除缺失值过多的列,和观察判断来说和预测的y没有关系的列
data.drop(["Name","Ticket","Cabin"],inplace=True,axis=1)
#处理缺失值,对缺失值较多的列进行填补,有一些特征只缺失一两个值,可以采取直接删除记#录的方法
data["Age"]=data["Age"].fillna(data["Age"].mean())
#用年龄的平均值补全缺失的年龄数据
data=data.dropna() #删除数据中还有空缺数据的行


data["Sex"] = (data["Sex"]== "male").astype("int")
#将多分类变量转换为数值型变量
labels = data["Embarked"].unique().tolist()   #获取“Embarked”列数据的类别列表
data["Embarked"] = data["Embarked"].apply(lambda x: labels.index(x))  
#将数据换成类别列表中的索引

x=data.iloc[:,data.columns!="Survived"]
y=data.iloc[:,data.columns=="Survived"]  #单独提取出数据 和 标签

Xtrain,Xtest,Ytrain,Ytest=train_test_split(x,y,test_size=0.3)
#修正测试集和训练集的索引
for i in [Xtrain, Xtest, Ytrain, Ytest]:
#数据、标签虽然要随机打乱,可是它们的索引最好还是保持有序,方便后续的处理
    i.index = range(i.shape[0])
clf=DecisionTreeClassifier(random_state=30)  #分类树创建以及训练
clf.fit(Xtrain,Ytrain)
score=clf.score(Xtest,Ytest)
score

数据导入后可以用data.info()查看数据的信息,可以看到数据里面一共有891行,每一行代表一个乘客的信息,一共有12项个人信息。包括姓名,年龄,生存与否,性别,座位区号,票的编号等等。

 再通过data.head()可以查看详细的数据信息:

 

 

 

   从data.info()信息可以看到有些数据存在缺失,比如年龄项只有714项,总的人数有891人,Cabin(座舱)项只有204项,数据缺失严重;还存在的问题是数据在进入决策树里必须是数字,不能有其他对象类型,比如说名字项,性别项,Cabin项等,那么在进行决策分析前需要对数据进行一系列的预处理。

  可以在程序里看到缺失项较多的Cabin项直接舍去了(前提是该项对决策影响不大,座舱信息影响不是特别关键,所以舍去),而人的Name已经Ticket对决策分析影响也不大,所以也舍去。对于年龄项,这是对生存与否影响比较大的(存在保护年老的和年幼的情况),所以这一项不能舍去,那么只能补全,补全的方式是采用已有的年龄的数据的平均值作为替代。而对于缺失只有两项的Embarked(登船城市)数据项,直接将这两个人的数据从整体中剔除,也就是最后的数据只有889行了。

  前面也提到说对于含有其他数据类型的数据全都转换为数字类型,那么首先就是性别项(只有两种结果,男性或者女性),通过data["Sex"] = (data["Sex"]== "male").astype("int")将其转化为0/1项,若为男性,则表示为1,反之用0表示。而对于Embarked项,它不是两类划分,那么程序中有说明它的替换方式,先求他的类别列表,然后用类别列表的索引值替换该处的类别信息(也就是登船城市替换为0、1、2........)

可以看到经过一系列数处理后

 

 

 

 

 此时将数据、标签分别提取出来,送入决策树中训练。

看到训练结果:score=0.7753

 

 得分并不是很高,那么接下来就是进行优化,修剪

 

import numpy as np
gini_thresholds = np.linspace(0,0.5,20)

parameters = {'splitter':('best','random')
              ,'criterion':("gini","entropy")
              ,"max_depth":[*range(1,10)]
              ,'min_samples_leaf':[*range(1,50,5)]
              ,'min_impurity_decrease':[*np.linspace(0,0.5,20)]
             }

clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10) #网格搜索,将所有参数可能的值做成一个参数表,逐个尝试,得到最佳的参数组合,比较耗时
GS.fit(Xtrain,Ytrain)

GS.best_params_

GS.best_score_

 

   可以看到网格搜索后的结果也只有一点点的提升,所以参数调优不是一个简单的过程,个人感觉仅仅调参是解决不了的,还需要从原始数据集入手,可以考虑对数据进行其他更优化的处理,比如对于年龄可以采用高斯分布对其进行替代,而不是统一采用平均值,由于缺失到达城市信息剔除的数据可以将目的城市替换为所有目的地中出现次数最多的那个城市,可以保证数据的完整性;再还有其实最开始去掉的Cabin信息我觉得对生存也是有影响的(有些位置方便逃生,有些位置不利于人流疏通),所以这些数据都是可以利用到的。

推荐阅读