决策树分类的应用场景非常广泛,在各行各业都有应用,比如在金融行业可以用决策树做贷款风险评估,医疗行业可以用决策树生成辅助诊断,电商行业可以用决策树对销售额进行预测等。
案例:泰坦尼克号乘客的生存预测
sklearn中只实现了ID3与CART决策树,
其中有一个参数是criterion,意为标准。它决定了构造的分类树是采用ID3分类树,还是CART分类树,对应的取值分别是entropy或者gini:
•entropy: 基于信息熵,也就是ID3算法,实际结果与C4.5相差不大;
•gini:默认参数,基于基尼系数。CART算法是基于基尼系数做属性划分的,所以criterion=gini时,实际上执行的是CART算法。
举例ID3决策树分类器

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')

机器学习 基尼指数 基尼指数的定义_机器学习 基尼指数 基尼指数的定义


机器学习 基尼指数 基尼指数的定义_决策树_02

1.问题描述

数据集格式为csv,一共有两个文件:

•train.csv是训练数据集,包含特征信息和存活与否的标签;

•test.csv: 测试数据集,只包含特征信息。

现在我们需要用决策树分类对训练集进行训练,针对测试集中的乘客进行生存预测,并告知分类器的准确率。

机器学习 基尼指数 基尼指数的定义_缺失值_03

2.关键流程

机器学习 基尼指数 基尼指数的定义_决策树_04


1.准备阶段:我们首先需要对训练集、测试集的数据进行探索,分析数据质量,并对数据进行清洗,然后通过特征选择对数据进行降维,方便后续分类运算;

2.分类阶段:首先通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集。然后我们对决策树分类器的准确性进行分析,并对决策树模型进行可视化。2.1 数据探索(EDA)

就是充分了解数据集中数据的特点,确定变量之间的相互关系,以及变量预测值之间的关系,从而帮助后期更好的进行特征工程与建立模型。

机器学习 基尼指数 基尼指数的定义_数据_05


所需工具:数据科学库(pandas、numpy、scipy)、可视化库(matplotlib、seabon)

第一步:载入数据,总览数据

#载入数据科学库与可视化库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
#载入数据集
train_data=pd.read_csv('./Titanic_Data/train.csv')
test_data=pd.read_csv('./Titanic_Data/test.csv')
#简略观察数据(head()+taik()+shape)
train_data.head().append(train_data.tail())
train_data.shape
#总览数据概况(describe()+info())
train_data.describe()#包括每列的统计量,个数,平均值,方差,最小值,四分位数,最大值,看这个信息的主要目的是了解数据的大概的范围以及每个值的异常值的判断。
#查看字符串类型(非数字)的整体情况
train_data.describe(include=['O'])
#查看数据类型与数据量,行数,列数,梅列数据类型,数据完整度
train_data.info()
#运行结果:
#头加尾
     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
886          887         0       2    ...     13.0000   NaN         S
887          888         1       1    ...     30.0000   B42         S
888          889         0       3    ...     23.4500   NaN         S
889          890         1       1    ...     30.0000  C148         C
890          891         0       3    ...      7.7500   NaN         Q

[10 rows x 12 columns]
------------------------
#shape
(891, 12)
------------------------
------------------------
#describe
       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]
#字符串类型describe
                      Name   Sex Ticket        Cabin Embarked
count                  891   891    891          204      889
unique                 891     2    681          147        3
top     Willey, Mr. Edward  male   1601  C23 C25 C27        S
freq                     1   577      7            4      644
--------------
#info
<class 'pandas.core.frame.DataFrame'>
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

第二步:判断数据缺失和异常

#由info得知,将数据类型分为类别特征,数值特征
#查看每列存在null值的情况,主要目的是nan存在的个数是否很大,如果很小则选择填充,如果使用模型可以直接空缺,让数自己去优化,但如果nan值存在很多,则考虑直接删除。
train_data.isnull().sum()
#输出结果:
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
test_data.isnull().sum()
#输出结果:
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64

可以看出Age,Fare,Cabin,Embarked字段存在缺失值,而Cabin字段存在过多缺失值,这里联想到缺失是否表示乘客的票本身就没有船舱号,就像我们买的无座票一样,本身就没有座位号,因此这里先填充为0
Age字段为年龄字段,是数值型,可以通过平均值补齐,fare为船票价格,是数值型,平均值补齐。embarked为object类型,需要另外操作

#Age字段补齐
train_data['Age'].fillna(train_data['Age'].mean(),inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(),inplace=True)
#Cabin字段补齐
train_data['Cabin']=train_data['Cabin'].fillna('0')
test_data['Cabin']=test_data['Cabin'].fillna('0')
#fare字段补齐
test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)
#embarked字段补齐
train_data['Embarked'].value_counts())
S    644
C    168
Q     77
#三个港口,S港最多,将缺失的数值设置为S
train_data['Embarked'].fillna('S', inplace=True)

一些机器学习算法能够处理缺失值,比如神经网络,一些则不能。
对于缺失值,一般有以下几种处理方法:
(1)如果数据集很多,但有很少的缺失值,可以删掉带缺失值的行;
(2)如果该属性相对学习来说不是很重要,可以对缺失值赋均值或者众数。
(3)对于标称属性,可以赋一个代表缺失的值,因为缺失本身也可能代表着一些隐含信息。比如船舱号Cabin这一属性,缺失可能代表并没有船舱。
(4)使用回归 随机森林等模型来预测缺失属性的值。
因为Age在该数据集里是一个相当重要的特征(先对Age进行分析即可得知),所以保证一定的缺失值填充准确率是非常重要的,对结果也会产生较大影响。
一般情况下,会使用数据完整的条目作为模型的训练集,以此来预测缺失值。对于当前的这个数据,可以使用随机森林来预测也可以使用线性回归预测。这里使用随机森林预测模型,选取数据集中的数值属性作为特征(因为sklearn的模型只能处理数值属性,所以这里先仅选取数值特征,但在实际的应用中需要将非数值特征转换为数值特征)

#另一种年龄填充方式(推荐)
from sklearn.ensemble import RandomForestClassifier
age_df=train_data[['Age','Survived','Fare','Parch','SibSp','Pclass']]
age_df_notnull=age_df.loc[(train_data['Age'].notnull())]
age_df_isnull=age_df.loc[(train_data['Age'].isnull())]
X=age_df_notnull.values[:,1:]
Y=age_df_notnull.values[:,0]
rfr=RandomForestClassifier(n_estimators=1000,n_jobs=-1)
rfr.fit(X,Y.astype('int'))
predictAges=rfr.predict(age_df_isnull.values[:,1:])
train_data.loc[train_data['Age'].isnull(),['Age']]=predictAges
#查看缺失值填充的结果
print(train_data.info())
#结果为:
<class 'pandas.core.frame.DataFrame'>
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            891 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          891 non-null object
Embarked       891 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

第三步:分析数据间关系
1>PassengerId乘客编码对分类无作用,可以在特征选择中舍弃
2>Survived是我们要预测的值
3>Pclass船票等级与生存与否的关系

train_data.groupby(['Pclass','Survived'])['Pclass'].count()
train_data[['Pclass','Survived']].groupby(['Pclass']).mean().plot.bar()
plt.show()
#结果:
Pclass  Survived
1       0            80
        1           136
2       0            97
        1            87
3       0           372
        1           119
Name: Pclass, dtype: int64
#性别,船舱等级,生存率关系
train_data.groupby(['Sex', 'Pclass', 'Survived'])['Survived'].count()
#结果为:
Sex     Pclass  Survived
female  1       0             3
                1            91
        2       0             6
                1            70
        3       0            72
                1            72
male    1       0            77
                1            45
        2       0            91
                1            17
        3       0           300
                1            47
Name: Survived, dtype: int64

机器学习 基尼指数 基尼指数的定义_机器学习 基尼指数 基尼指数的定义_06


可以看出:女士优先,但与船舱等级也存在关系

4>年龄与存活存在关系

#年龄分布
train_data['Age'].describe()
count    891.000000
mean      29.668231
std       13.739002
min        0.420000
25%       21.000000
50%       28.000000
75%       37.000000
max       80.000000
Name: Age, dtype: float64
#将年龄划分为四个群体,分别分析生还情况
bins = [0, 12, 18, 65, 100]
train_data['Age_group'] = pd.cut(train_data['Age'], bins)
by_age = train_data.groupby('Age_group')['Survived'].mean()
#
Age_group
(0, 12]      0.579710
(12, 18]     0.428571
(18, 65]     0.386243
(65, 100]    0.125000
Name: Survived, dtype: float64

5>姓名与生存也存在关系(从称呼方面考虑,在此不做分析)
由于称呼反映年龄,性别,社会地位等,过于复杂,不做分析
6>有无SibSp与生存的关系

sns.countplot(x="SibSp", hue="Survived", data=train_data)

机器学习 基尼指数 基尼指数的定义_数据_07


7>有无Parch与生存的关系

sns.countplot(x="Parch", hue="Survived", data=train)

机器学习 基尼指数 基尼指数的定义_数据_08


8>船票编号也暂时不做分析,过于复杂

9>船票价格Fare与生存的关系

plt.figure(figsize=(10,5))
train_data['Fare'].hist(bins = 70)
train_data.boxplot(column='Fare', by='Pclass', showfliers=False)
plt.show()
train_data['Fare'].describe()
count    891.000000
mean      32.204208
std       49.693429
min        0.000000
25%        7.910400
50%       14.454200
75%       31.000000
max      512.329200
Name: Fare, dtype: float64

机器学习 基尼指数 基尼指数的定义_缺失值_09


机器学习 基尼指数 基尼指数的定义_数据_10


票价与是否生存有一定的相关性,生还者票价高于未生还者

10>港口与生存关系

sns.countplot(x="Embarked", hue="Survived", data=train_data)
sns.factorplot('Embarked', 'Survived', data=train_data, size=3, aspect=2)
plt.title('Embarked and Survived rate')
plt.show()

机器学习 基尼指数 基尼指数的定义_决策树_11


机器学习 基尼指数 基尼指数的定义_缺失值_12


不同港口上船生还率不同。

2.2 特征选择

根据上一部数据探索,得到:

需要删除的特征包括:‘PassengerId’,‘Name’和’Ticket’,

其余字段:Pclass、Sex、Age、SibSp、Parch,Fare,Cabin.

变量转换的目的是将数据转换为适用于模型使用的数据,不同模型接受不同类型的数据,Scikit-learn要求数据都是数字型numeric,所以我们要将一些非数字型的原始数据转换为数字型numeric。
所有的数据可以分为两类:
1.定量(Quantitative)变量可以以某种方式排序,Age就是一个很好的列子。
2.定性(Qualitative)变量描述了物体的某一(不能被数学表示的)方面,Embarked就是一个例子。

features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
train_features = train_data[features]
train_labels = train_data['Survived']
test_features = test_data[features]
#使用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'))
print(dvec.feature_names_)
#输出:
['Age', 'Embarked=C', 'Embarked=Q', 'Embarked=S', 'Fare', 'Parch', 'Pclass', 'Sex=female', 'Sex=male', 'SibSp']

train_features特征矩阵就包括10个特征值(列),以及891个样本(行),即891行,10列的特征矩阵
2.3.决策树模型
我们使用ID3算法,即在创建DecisionTreeClassifier时,设置criterion=‘entropy’,然后使用fit进行训练,将特征值矩阵和分类标识结果作为参数传入,得到决策树分类器。

from sklearn.tree import DecisionTreeClassifier
# 构造ID3决策树
clf = DecisionTreeClassifier(criterion='entropy')
# 决策树训练
clf.fit(train_features, train_labels)

2.4.模型预测与评估

test_features=dvec.transform(test_features.to_dict(orient='record'))
# 决策树预测
pred_labels = clf.predict(test_features)
# 得到决策树准确率
acc_decision_tree = round(clf.score(train_features, train_labels), 6)
print(u'score准确率为 %.4lf' % acc_decision_tree)

因为测试集中没有survived的实际结果,无法用测试集的预测结果与实际结果做对比。
所以可以使用K折交叉验证的方式。
交叉验证是一种常用的验证分类准确率的方法,原理是拿出大部分样本进行训练,少量的用于分类器的验证。K折交叉验证,就是做K次交叉验证,每次选取K分之一的数据作为验证,其余作为训练。轮流K次,取平均值。
K折交叉验证的原理是这样的:
1.将数据集平均分割成K个等份;
2.使用1份数据作为测试数据,其余作为训练数据;
3.计算测试准确率;
4.使用不同的测试集,重复2、3步骤。
在sklearn的model_selection模型选择中提供了cross_val_score函数。cross_val_score函数中的参数cv代表对原始数据划分成多少份,也就是我们的K值,一般建议K值取10,因此我们可以设置CV=10。

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

总结:

需要注意一下几点:
1.特征选择是分类模型好坏的关键。选择什么样的特征,以及对应的特征值矩阵,决定了分类模型的好坏。通常情况下,特征值不都是数值类型,要进行相应的数据类型转换;
2.模型准确率需要考虑是否有测试集的实际结果可以做对比,当测试集没有真实结果可以对比时,需要使用K折交叉验证cross_val_score;
3.数据探索非常重要会影响缺失值处理,特征选择,导致模型预测结果。

机器学习 基尼指数 基尼指数的定义_数据分析_13