CategoryResourceRepost/极客时间专栏/数据分析实战45讲/第二模块:数据分析算法篇/19丨决策树(下):泰坦尼克乘客生存预测.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

412 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="19丨决策树泰坦尼克乘客生存预测" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/24/c0/2488ceea1b379251231440e4f03b50c0.mp3"></audio>
在前面的两篇文章中我给你讲了决策树算法。决策树算法是经常使用的数据挖掘算法这是因为决策树就像一个人脑中的决策模型一样呈现出来非常直观。基于决策树还诞生了很多数据挖掘算法比如随机森林Random forest
今天我来带你用决策树进行项目的实战。
决策树分类的应用场景非常广泛,在各行各业都有应用,比如在金融行业可以用决策树做贷款风险评估,医疗行业可以用决策树生成辅助诊断,电商行业可以用决策树对销售额进行预测等。
在了解决策树的原理后今天我们用sklearn工具解决一个实际的问题泰坦尼克号乘客的生存预测。
## sklearn中的决策树模型
首先我们需要掌握sklearn中自带的决策树分类器DecisionTreeClassifier方法如下
```
clf = DecisionTreeClassifier(criterion='entropy')
```
到目前为止sklearn中只实现了ID3与CART决策树所以我们暂时只能使用这两种决策树在构造DecisionTreeClassifier类时其中有一个参数是criterion意为标准。它决定了构造的分类树是采用ID3分类树还是CART分类树对应的取值分别是entropy或者gini
<li>
entropy: 基于信息熵也就是ID3算法实际结果与C4.5相差不大;
</li>
<li>
gini默认参数基于基尼系数。CART算法是基于基尼系数做属性划分的所以criterion=gini时实际上执行的是CART算法。
</li>
我们通过设置criterion='entropy可以创建一个ID3决策树分类器然后打印下clf看下决策树在sklearn中是个什么东西
```
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best')
```
这里我们看到了很多参数除了设置criterion采用不同的决策树算法外一般建议使用默认的参数默认参数不会限制决策树的最大深度不限制叶子节点数认为所有分类的权重都相等等。当然你也可以调整这些参数来创建不同的决策树模型。
我整理了这些参数代表的含义:
<img src="https://static001.geekbang.org/resource/image/ea/0c/ead008e025a039c8731884ce2e29510c.jpg" alt="">
在构造决策树分类器后我们可以使用fit方法让分类器进行拟合使用predict方法对新数据进行预测得到预测的分类结果也可以使用score方法得到分类器的准确率。
下面这个表格是fit方法、predict方法和score方法的作用。
<img src="https://static001.geekbang.org/resource/image/3c/f8/3c7057b582b8078129c8342cde709ef8.png" alt="">
## Titanic乘客生存预测
**问题描述**
泰坦尼克海难是著名的十大灾难之一究竟多少人遇难各方统计的结果不一。现在我们可以得到部分的数据具体数据你可以从GitHub上下载[https://github.com/cystanford/Titanic_Data](https://github.com/cystanford/Titanic_Data)
(完整的项目代码见:[https://github.com/cystanford/Titanic_Data/blob/master/titanic_analysis.py](https://github.com/cystanford/Titanic_Data/blob/master/titanic_analysis.py) 你可以跟着学习后自己练习)
其中数据集格式为csv一共有两个文件
<li>
train.csv是训练数据集包含特征信息和存活与否的标签
</li>
<li>
test.csv: 测试数据集,只包含特征信息。
</li>
现在我们需要用决策树分类对训练集进行训练,针对测试集中的乘客进行生存预测,并告知分类器的准确率。
在训练集中,包括了以下字段,它们具体为:
<img src="https://static001.geekbang.org/resource/image/2c/0a/2c14e00b64a73f40d75451a180c57b0a.png" alt=""><br>
**生存预测的关键流程**
我们要对训练集中乘客的生存进行预测,这个过程可以划分为两个重要的阶段:
<img src="https://static001.geekbang.org/resource/image/6c/7f/6c08ca83a20a969100e5ceddee0ab27f.jpg" alt="">
<li>
**准备阶段**:我们首先需要对训练集、测试集的数据进行探索,分析数据质量,并对数据进行清洗,然后通过特征选择对数据进行降维,方便后续分类运算;
</li>
<li>
**分类阶段**:首先通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集。然后我们对决策树分类器的准确性进行分析,并对决策树模型进行可视化。
</li>
下面,我分别对这些模块进行介绍。
**模块1数据探索**
数据探索这部分虽然对分类器没有实质作用,但是不可忽略。我们只有足够了解这些数据的特性,才能帮助我们做数据清洗、特征选择。
那么如何进行数据探索呢?这里有一些函数你需要了解:
<li>
使用info()了解数据表的基本情况:行数、列数、每列的数据类型、数据完整度;
</li>
<li>
使用describe()了解数据表的统计情况:总数、平均值、标准差、最小值、最大值等;
</li>
<li>
使用describe(include=[O])查看字符串类型(非数字)的整体情况;
</li>
<li>
使用head查看前几行数据默认是前5行
</li>
<li>
使用tail查看后几行数据默认是最后5行
</li>
我们可以使用Pandas便捷地处理这些问题
```
import pandas as pd
# 数据加载
train_data = pd.read_csv('./Titanic_Data/train.csv')
test_data = pd.read_csv('./Titanic_Data/test.csv')
# 数据探索
print(train_data.info())
print('-'*30)
print(train_data.describe())
print('-'*30)
print(train_data.describe(include=['O']))
print('-'*30)
print(train_data.head())
print('-'*30)
print(train_data.tail())
```
运行结果:
```
&lt;class 'pandas.core.frame.DataFrame'&gt;
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId 891 non-null int64
Survived 891 non-null int64
Pclass 891 non-null int64
Name 891 non-null object
Sex 891 non-null object
Age 714 non-null float64
SibSp 891 non-null int64
Parch 891 non-null int64
Ticket 891 non-null object
Fare 891 non-null float64
Cabin 204 non-null object
Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB
None
------------------------------
PassengerId Survived ... Parch Fare
count 891.000000 891.000000 ... 891.000000 891.000000
mean 446.000000 0.383838 ... 0.381594 32.204208
std 257.353842 0.486592 ... 0.806057 49.693429
min 1.000000 0.000000 ... 0.000000 0.000000
25% 223.500000 0.000000 ... 0.000000 7.910400
50% 446.000000 0.000000 ... 0.000000 14.454200
75% 668.500000 1.000000 ... 0.000000 31.000000
max 891.000000 1.000000 ... 6.000000 512.329200
[8 rows x 7 columns]
------------------------------
Name Sex ... Cabin Embarked
count 891 891 ... 204 889
unique 891 2 ... 147 3
top Peter, Mrs. Catherine (Catherine Rizk) male ... B96 B98 S
freq 1 577 ... 4 644
[4 rows x 5 columns]
------------------------------
PassengerId Survived Pclass ... Fare Cabin Embarked
0 1 0 3 ... 7.2500 NaN S
1 2 1 1 ... 71.2833 C85 C
2 3 1 3 ... 7.9250 NaN S
3 4 1 1 ... 53.1000 C123 S
4 5 0 3 ... 8.0500 NaN S
[5 rows x 12 columns]
------------------------------
PassengerId Survived Pclass ... Fare Cabin Embarked
886 887 0 2 ... 13.00 NaN S
887 888 1 1 ... 30.00 B42 S
888 889 0 3 ... 23.45 NaN S
889 890 1 1 ... 30.00 C148 C
890 891 0 3 ... 7.75 NaN Q
[5 rows x 12 columns]
```
**模块2数据清洗**
通过数据探索我们发现Age、Fare和Cabin这三个字段的数据有所缺失。其中Age为年龄字段是数值型我们可以通过平均值进行补齐Fare为船票价格是数值型我们也可以通过其他人购买船票的平均值进行补齐。
具体实现的代码如下:
```
# 使用平均年龄来填充年龄中的nan值
train_data['Age'].fillna(train_data['Age'].mean(), inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(),inplace=True)
# 使用票价的均值填充票价中的nan值
train_data['Fare'].fillna(train_data['Fare'].mean(), inplace=True)
test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)
```
Cabin为船舱有大量的缺失值。在训练集和测试集中的缺失率分别为77%和78%无法补齐Embarked为登陆港口有少量的缺失值我们可以把缺失值补齐。
首先观察下Embarked字段的取值方法如下
```
print(train_data['Embarked'].value_counts())
```
结果如下:
```
S 644
C 168
Q 77
```
我们发现一共就3个登陆港口其中S港口人数最多占到了72%因此我们将其余缺失的Embarked数值均设置为S
```
# 使用登录最多的港口来填充登录港口的nan值
train_data['Embarked'].fillna('S', inplace=True)
test_data['Embarked'].fillna('S',inplace=True)
```
**模块3特征选择**
特征选择是分类器的关键。特征选择不同,得到的分类器也不同。那么我们该选择哪些特征做生存的预测呢?
通过数据探索我们发现PassengerId为乘客编号对分类没有作用可以放弃Name为乘客姓名对分类没有作用可以放弃Cabin字段缺失值太多可以放弃Ticket字段为船票号码杂乱无章且无规律可以放弃。其余的字段包括Pclass、Sex、Age、SibSp、Parch和Fare这些属性分别表示了乘客的船票等级、性别、年龄、亲戚数量以及船票价格可能会和乘客的生存预测分类有关系。具体是什么关系我们可以交给分类器来处理。
因此我们先将Pclass、Sex、Age等这些其余的字段作特征放到特征向量features里。
```
# 特征选择
features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train_data[features]
train_labels = train_data['Survived']
test_features = test_data[features]
```
特征值里有一些是字符串这样不方便后续的运算需要转成数值类型比如Sex字段有male和female两种取值。我们可以把它变成Sex=male和Sex=female两个字段数值用0或1来表示。
同理Embarked有S、C、Q三种可能我们也可以改成Embarked=S、Embarked=C和Embarked=Q三个字段数值用0或1来表示。
那该如何操作呢我们可以使用sklearn特征选择中的DictVectorizer类用它将可以处理符号化的对象将符号转成数字0/1进行表示。具体方法如下
```
from sklearn.feature_extraction import DictVectorizer
dvec=DictVectorizer(sparse=False)
train_features=dvec.fit_transform(train_features.to_dict(orient='record'))
```
你会看到代码中使用了fit_transform这个函数它可以将特征向量转化为特征值矩阵。然后我们看下dvec在转化后的特征属性是怎样的即查看dvec的feature_names_属性值方法如下
```
print(dvec.feature_names_)
```
运行结果:
```
['Age', 'Embarked=C', 'Embarked=Q', 'Embarked=S', 'Fare', 'Parch', 'Pclass', 'Sex=female', 'Sex=male', 'SibSp']
```
你可以看到原本是一列的Embarked变成了“Embarked=C”“Embarked=Q”“Embarked=S”三列。Sex列变成了“Sex=female”“Sex=male”两列。
这样train_features特征矩阵就包括10个特征值以及891个样本即891行10列的特征矩阵。
**模块4决策树模型**
刚才我们已经讲了如何使用sklearn中的决策树模型。现在我们使用ID3算法即在创建DecisionTreeClassifier时设置criterion=entropy然后使用fit进行训练将特征值矩阵和分类标识结果作为参数传入得到决策树分类器。
```
from sklearn.tree import DecisionTreeClassifier
# 构造ID3决策树
clf = DecisionTreeClassifier(criterion='entropy')
# 决策树训练
clf.fit(train_features, train_labels)
```
**模块5模型预测&amp;评估**
在预测中我们首先需要得到测试集的特征值矩阵然后使用训练好的决策树clf进行预测得到预测结果pred_labels
```
test_features=dvec.transform(test_features.to_dict(orient='record'))
# 决策树预测
pred_labels = clf.predict(test_features)
```
在模型评估中决策树提供了score函数可以直接得到准确率但是我们并不知道真实的预测结果所以无法用预测值和真实的预测结果做比较。我们只能使用训练集中的数据进行模型评估可以使用决策树自带的score函数计算下得到的结果
```
# 得到决策树准确率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score准确率为 %.4lf' % acc_decision_tree)
```
运行结果:
```
score准确率为 0.9820
```
你会发现你刚用训练集做训练,再用训练集自身做准确率评估自然会很高。但这样得出的准确率并不能代表决策树分类器的准确率。
这是为什么呢?
因为我们没有测试集的实际结果因此无法用测试集的预测结果与实际结果做对比。如果我们使用score函数对训练集的准确率进行统计正确率会接近于100%如上结果为98.2%),无法对分类器的在实际环境下做准确率的评估。
那么有什么办法,来统计决策树分类器的准确率呢?
这里可以使用K折交叉验证的方式交叉验证是一种常用的验证分类准确率的方法原理是拿出大部分样本进行训练少量的用于分类器的验证。K折交叉验证就是做K次交叉验证每次选取K分之一的数据作为验证其余作为训练。轮流K次取平均值。
K折交叉验证的原理是这样的
<li>
将数据集平均分割成K个等份
</li>
<li>
使用1份数据作为测试数据其余作为训练数据
</li>
<li>
计算测试准确率;
</li>
<li>
使用不同的测试集重复2、3步骤。
</li>
在sklearn的model_selection模型选择中提供了cross_val_score函数。cross_val_score函数中的参数cv代表对原始数据划分成多少份也就是我们的K值一般建议K值取10因此我们可以设置CV=10我们可以对比下score和cross_val_score两种函数的正确率的评估结果
```
import numpy as np
from sklearn.model_selection import cross_val_score
# 使用K折交叉验证 统计决策树准确率
print(u'cross_val_score准确率为 %.4lf' % np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))
```
运行结果:
```
cross_val_score准确率为 0.7835
```
你可以看到score函数的准确率为0.9820cross_val_score准确率为 0.7835。
这里很明显对于不知道测试集实际结果的要使用K折交叉验证才能知道模型的准确率。
**模块6决策树可视化**
sklearn的决策树模型对我们来说还是比较抽象的。我们可以使用Graphviz可视化工具帮我们把决策树呈现出来。
<img src="https://static001.geekbang.org/resource/image/4a/a0/4ae6016c6c7d1371586c5be015247da0.png" alt=""><br>
安装Graphviz库需要下面的几步
<li>
安装graphviz工具这里是它的下载地址[http://www.graphviz.org/download/](http://www.graphviz.org/download/)
</li>
<li>
将Graphviz添加到环境变量PATH中
</li>
<li>
需要Graphviz库如果没有可以使用pip install graphviz进行安装。
</li>
这样你就可以在程序里面使用Graphviz对决策树模型进行呈现最后得到一个决策树可视化的PDF文件可视化结果文件Source.gv.pdf你可以在GitHub上下载[https://github.com/cystanford/Titanic_Data](https://github.com/cystanford/Titanic_Data)
## 决策树模型使用技巧总结
今天我用泰坦尼克乘客生存预测案例把决策树模型的流程跑了一遍。在实战中,你需要注意一下几点:
<li>
特征选择是分类模型好坏的关键。选择什么样的特征以及对应的特征值矩阵决定了分类模型的好坏。通常情况下特征值不都是数值类型可以使用DictVectorizer类进行转化
</li>
<li>
模型准确率需要考虑是否有测试集的实际结果可以做对比当测试集没有真实结果可以对比时需要使用K折交叉验证cross_val_score
</li>
<li>
Graphviz可视化工具可以很方便地将决策模型呈现出来帮助你更好理解决策树的构建。
</li>
<img src="https://static001.geekbang.org/resource/image/f0/ea/f09fd3c8b1ce771624b803978f01c9ea.png" alt=""><br>
我上面讲了泰坦尼克乘客生存预测的六个关键模块请你用sklearn中的决策树模型独立完成这个项目对测试集中的乘客是否生存进行预测并给出模型准确率评估。数据从GitHub上下载即可。
最后给你留一个思考题吧我在构造特征向量时使用了DictVectorizer类使用fit_transform函数将特征向量转化为特征值矩阵。DictVectorizer类同时也提供transform函数那么这两个函数有什么区别
欢迎你在评论区留言与我分享你的答案,也欢迎点击“请朋友读”把这篇文章分享给你的朋友或者同事,一起交流一下。