数据预处理|7 分类

image.png
在上一章中,你学会了如何去预测数值,在本章中,我们将把注意力转向预测分类数值。从本质上讲,这就是分类的含义:预测未来的分类值。预测的重点是估计一些数值在未来会是什么,而分类则是预测事件在未来的发生或不发生。例如,在本章中,我们将看到分类是如何预测一个人是否会拖欠贷款的。在本章中,我们还将讨论预测和分类在程序上的异同,并将介绍两种最著名的分类算法。决策树和K-Nearest Neighbors(KNN)。虽然本章提供了对分类算法的基本理解,也展示了如何使用Python进行分类,但不能把本章看成是分类的全面参考。相反,你要把重点放在基本概念上,这样你就能为数据预处理之旅做好准备,你将在第9章,数据清理第一级--清理表格中开始。
1 分类模型
在上一章中,我们介绍了预测性建模。分类是预测建模的一种类型;具体来说,分类是一种回归分析,其中因果属性或目标是分类的而不是数字的。尽管分类是预测性建模的一个子集,但由于其实用性,它是数据挖掘中最受关注的领域。今天现实世界中许多机器学习(ML)解决方案的核心是分类算法。尽管它的应用很普遍,算法很复杂,但分类的基本概念很简单。就像预测一样,对于分类,我们需要指定我们的独立属性(预测器)和依赖属性(目标)。一旦我们清楚了这些,并且我们有一个包括这些属性的数据集,我们就可以采用分类算法了。分类算法,就像预测算法一样,寻求找到独立属性和从属属性之间的关系,所以通过了解新数据对象的独立属性的值,我们可以猜测新数据对象的类别(分类)。现在让我们一起看一个例子,以便这些相当抽象的概念开始对你更有意义。
1.1 设计分类模型的例子
当你现在申请现金贷款时,不要搞错了,分类算法将在决定你是否能获得贷款方面发挥重要作用。在真实案例中使用的分类模型往往非常复杂,有许多独立的属性。然而,这些算法所依赖的两个最重要的信息是你的收入和信用评分。在这里,我们将介绍这些复杂分类的一个简单版本。下图所示的分类设计使用收入和信用分数作为独立属性,对申请人是否会对接受的贷款违约进行分类。默认?二元属性是分类设计的因果属性。

image.png
如果你将上一章的图6.4与前述图表相比较,你可能会断言,预测和分类之间没有区别;你不会完全错。除了一个简单的区别,预测和分类几乎是一样的:分类的依赖属性是分类的,而预测的依赖属性是数字的。这个小小的区别相当于为这两个数据挖掘任务带来了很多算法和分析上的变化。
1.2 分类算法
有许多经过精心研究、设计和开发的分类算法。事实上,分类算法比预测算法更多。仅举几例,我们有KNN、决策树、多层感知器(MLP)、支持向量机(SVM)和随机森林。这些算法中的一些被列为预测和分类的算法。例如,MLP总是被列在这两种算法中;然而,MLP本来就是为预测任务设计的,但它可以被修改,以便它也能成功地解决分类问题。另一方面,我们有决策树算法,它本质上是为分类而设计的,但它也可以被修改以解决预测问题。在本章中,我们将简要介绍其中的两种算法。KNN和决策树。
2 KNN
KNN是最简单的分类算法之一,关于它的机制,几乎所有你需要知道的东西都在它的名字中呈现。简单地说,为了对一个新的数据对象进行分类,KNN从训练数据集中找到与新数据对象最近的K个邻居,并使用这些数据对象的标签来分配新数据对象的可能标签。可能是KNN太简单了,也正因为如此,你并没有完全理解它的机制。让我们继续学习,使用下面的例子。
2.1 使用KNN进行分类的例子
我们要继续研究前面介绍的贷款申请问题。在完成分类设计后,我们指定收入和信用分数为独立属性,默认作为因果属性。下面的截图显示了一个可以支持这种分类设计的数据集。该数据集来自CustomerLoan.csv文件。现在,让我们假设我们想用前面的数据来分类一个年收入为美元(USD)98,487,信用分数为785的客户是否会拖欠贷款。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
applicant_df = pd.read_csv('https://raw.githubusercontent.com/PacktPublishing/Hands-On-Data-Preprocessing-in-Python/main/Chapter07/CustomerLoan.csv')
applicant_df.drop(columns=['Name'],inplace=True)
applicant_df.head()

image.png
newApplicant = applicant_df.iloc[20]
newApplicant

image.png
由于这个例子只包括三个维度,我们可以使用可视化的方法来执行和理解KNN算法。下面的截图显示了我们想解决的分类问题,一目了然。
applicant_df = pd.read_csv('https://raw.githubusercontent.com/PacktPublishing/Hands-On-Data-Preprocessing-in-Python/main/Chapter07/CustomerLoan.csv')
applicant_df.drop(index = [20],inplace=True)
fig, ax = plt.subplots()
subset = applicant_df.loc[applicant_df['default']=='Yes']
ax.scatter(subset.income, subset.score, marker='o', label='Default-YES', color='C1')
subset = applicant_df.loc[applicant_df['default']=='NO']
ax.scatter(subset.income, subset.score, marker='D', label='Default-NO', color='C0')
ax.scatter(newApplicant.income, newApplicant.score, marker='*', label='New Applicant', color='black', s=150)
plt.xlabel('income') # set x-axis label
plt.ylabel('score') # set y-axis label
for _, row in applicant_df.iterrows():
ax.annotate(row.Name, (row.income -700, row.score-10))
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, bbox_to_anchor=(1.05, 1))
plt.show()

image.png
执行KNN的第一步是决定K。基本上,我们需要决定我们想要基于分类的近邻的数量。让我们假设我们想使用K=4。
调整KNN 与其他许多数据挖掘算法类似,要成功地使用KNN进行分类,你需要调整该算法。调整KNN意味着找到最佳的K数,使KNN在每个案例研究中都能达到最佳性能。在本书中,我们将不涉及调整,因为我们正在学习算法,主要是为了帮助我们进行更成功的数据预处理。
因此,当K=4时,我们可以很容易地从前面的截图中看到,新申请人的四个最近的邻居是数据对象1、2、3和14。由于四个最近的数据对象中,有三个的标签是Default-NO,我们将把新申请人归类为Default-NO。就是这样,就这么简单。虽然KNN在机制上是那么简单,但创建一个实现这种算法的计算机程序却比较困难。为什么会这样呢?下面介绍几个原因。- 在这里,我们学习了KNN的机制,使用了一个只有三个维度的例子。因此,使用散点图和颜色,我们能够显示问题并总结出我们需要处理的所有数据。现实世界的问题很可能不止有三个维度。
-
• 虽然我们能够用眼睛观察并检测最近的邻居,但计算机没有能力仅仅 "看到 "哪些是最近的邻居。计算机程序需要计算新的数据对象与数据集中的所有数据对象之间的距离,以便找到K-最近的邻居。
-
• 如果出现了平局,会发生什么?假设我们选择了K=4,而其中两个最近的邻居属于一个类别,另外两个来自另一个类别。对我们来说,好消息是我们不需要担心任何这些挑战,因为我们可以简单地使用一个包含这种算法的稳定模块。让我们从
sklearn.neighbors模块中导入KNeighborsClassifier,并将其应用于我们这里的例子。在我们应用该算法之前,我们需要对以下两个事项采取行动。
-
1. 首先,如果你从未在Anaconda Navigator上使用过sklearn模块,你必须安装它。运行以下代码将安装该模块:
conda install scikit-learn -
2. 接下来,我们将需要对我们的数据进行规范化处理。这是一个数据预处理的概念,当我们谈到它的时候,我们会深入地介绍它。然而,让我们在这里简单地讨论一下它的必要性。在应用KNN之前,我们需要对数据进行归一化处理的原因是,通常情况下,独立属性的尺度是彼此不同的,如果数据没有被归一化,具有较大尺度的属性最终会在KNN算法的远距离计算中更加重要,有效地抵消了其他独立属性的作用。在这个例子中,收入的范围是78,479到119,976,而分数(用于信用评分)的范围是680到815。如果我们要用这些尺度来计算数据对象之间的距离,那么重要的就是收入,而不是信用分数。因此,为了避免让属性的尺度干扰算法的机制,我们将在使用KNN之前对数据进行标准化处理。当一个属性被归一化时,它的值被转换,使更新的属性范围从0到1,而不影响属性在数据对象之间的相对区分。下面的代码将
CustomerLoan.csv文件读取到applicant_df数据框架中,并在applicant_df中创建两个新的列,这些列是原始数据中两列的标准化转换。前面的代码通过使用以下公式创建了两个新列。
applicant_df['income_Normalized'] = (applicant_df.income-applicant_df.income.min())/(applicant_df.income.max()-applicant_df.income.min())
applicant_df['score_Normalized'] = (applicant_df.score-applicant_df.score.min())/(applicant_df.score.max()-applicant_df.score.min())
applicant_df.drop(columns=['Name'],inplace=True)
applicant_df.head()

image.png

image.png
前面的代码已经使用公式将收入列转化为收入_正常化,将分数转化为分数_正常化。下面的截图显示了这个数据转换的结果。花点时间研究一下前面的截图;具体来说,看看列和它们的规范化版本之间的关系。你会注意到,原始属性下的数值与归一化版本之间的相关距离和顺序并没有改变。要看到这一点,找到原始属性和其归一化版本下的最小值和最大值,并研究这些。请注意,在前面的截图中,数据的最后一行是我们要分类的新申请人。现在数据已经准备好了,我们可以应用sklearn.neighborsClassifier模块来做这件事。你可以分四步进行,如下所示。
# 1. 首先,KneighborsClassifier模块需要被导入。下面的代码完成了导入工作。
from sklearn.neighbors import KNeighborsClassifier
# 2. 接下来,我们需要指定我们的独立属性和依赖属性。下面的代码将独立属性放在Xs中,将依赖属性放在y中。请注意,我们正在删除数据的最后一行,因为这是我们想要进行预测的数据行。.drop(index=[20])部分将负责这个丢弃。
predictors = ['income_Normalized','score_Normalized']
target = 'default'
Xs = applicant_df[predictors].drop(index=[20])
y = applicant_df[target].drop(index=[20])
# 3.接下来,我们将创建一个KNN模型,然后将数据装入其中。下面的代码显示了这是如何做到的。
knn = KNeighborsClassifier(n_neighbors=4)
knn.fit(Xs,y)
# 4.现在,knn已经准备好对新的数据对象进行分类。下面的代码显示了我们如何分离数据集的最后一行,并使用knn对其进行预测。
newApplicant = pd.DataFrame({
'income_Normalized':applicant_df.iloc[20].income_Normalized,
'score_normalized':applicant_df.iloc[20].score_Normalized
},index=[20])
predict_y = knn.predict(newApplicant)
print(predict_y)
# ['NO']
前面的截图中的输出,也就是newApplicant的类,证实了我们已经决定KNN应该得出的结论。
3 决策树
虽然你可以使用决策树算法进行分类,就像KNN一样,但它的分类任务非常不同。KNN找到最相似的数据对象进行分类,而决策树首先使用树状结构对数据进行总结,然后使用该结构进行分类。让我们通过一个例子来了解决策树。
3.1 使用决策树进行分类的例子
我们将使用sklearn.tree中的DecisionTreeClassifier来对applicant_df应用决策树算法。使用决策树所需的代码与KNN的代码几乎相同。让我们先看看这段代码,然后我将提请你注意它们的异同。这里就是了。
from sklearn.tree import DecisionTreeClassifier
predictors = ['income','score']
target = 'default'
Xs = applicant_df[predictors].drop(index=[20])
y = applicant_df[target].drop(index=[20])
classTree = DecisionTreeClassifier()
classTree.fit(Xs,y)
newApplicant = pd.DataFrame({'income':
applicant_df.iloc[20].income,
'score':
applicant_df.iloc[20].score},
index = [20])
predict_y = classTree.predict(newApplicant)
print(predict_y) # ['Yes']
前面的代码和KNN代码之间有两个区别。在此,我们列出这些差异。
-
• 首先,决策树由于其工作方式,不需要对数据进行归一化处理,所以这就是为什么predictors = ['income', 'score'] 一行代码使用原始属性。我们为KNN使用了规范化的版本。
-
• 第二,很明显,我们使用了
DecisionTreeClassifier()而不是KneighborsClassifier()。我们还在这里将我们的分类模型命名为classTree,而不是knn,我们用的是KNN。
请注意! 你可能已经注意到,在Python中使用任何预测模型(预测和分类)的代码都非常相似。下面是我们对每一个模型所采取的步骤。首先,我们导入拥有我们想要使用的算法的模块。接下来,我们把数据分成独立和依赖属性。之后,我们使用我们导入的模块创建一个模型。然后,我们使用我们创建的模型的.fit()函数,将数据装入模型。最后,我们使用.predict()函数来预测新数据对象的依赖属性。
如果你成功运行前面的代码,你会看到决策树与KNN不同,将newApplicant归类为YES。让我们看看 DecisionTreeClassifier() 创建的树状结构,以得出这个结论。为此,我们将使用 sklearn.tree 模块中的 plot_tree() 函数。尝试运行下面的代码来绘制树状结构。
from sklearn.tree import plot_tree
plot_tree(classTree,
feature_names=predictors,
class_names=y.unique(),
filled=True,
impurity=False
)

image.png
前面的截图中的输出将直观地告诉你为什么决策树得出的结论与KNN不同。从顶部节点开始,数据集被分成两组:分数大于789.5的数据对象和分数低于截止值的数据对象。所有分数高于789.5的数据对象都被标记为NO-default;因此,决策树得出的结论是,如果申请人的分数高于789.5,他们应该被归类为NO。由于newApplicant的得分是785分,所以这个规则不适用于这个数据对象。为了根据这个树状结构找到数据对象的类别,我们需要更深入地了解。从树状结构中,我们看到分数低于789.5和收入低于110,122.5的数据对象已经拖欠了贷款。所以,决策树再次达成了这样的规则:当申请人的分数低于789.5和110,122.5时,他们应该被归为YES。由于newApplicant的分数和收入都低于这些临界值,决策树对它的结论是YES。
调整决策树:正如KNN一样,决策树也需要调整以发挥其最大潜力。事实上,决策树需要的调整甚至比KNN更多,因为决策树有更多的超参数可以调整。然而,出于对KNN的同样原因,我们将不在本书中介绍如何调整的问题。
决策树的工作方式也很简单--决策树在不同阶段使用其中一个独立的属性将数据集一次又一次地分割成两个片段,直到所有片段的数据都是纯净的。纯度意味着该段中的所有数据都属于同一类别。在进入本章结尾之前,让我们花点时间来讨论一下为什么这两种算法会得出不同的结论。首先,我们要明白,当两个不同的算法对同一个数据对象得出不同的结论时,这说明对该数据对象的分类是困难的,也就是说,数据中存在着不同的模式,表明该数据对象可能是其中的任何一个类。其次,由于这些算法有不同的模式识别和决策方式,得出不同结论的算法可能以不同的方式对这些模式进行了优先排序。

浙公网安备 33010602011771号