R 应用无监督学习(一)
原文:
annas-archive.org/md5/4c0eaccb71c3cc25e04cea28e06cad11译者:飞龙
第一章:前言
关于
本节简要介绍了作者、本书涵盖的内容、您开始所需的技术技能以及完成所有包含的活动和练习所需的硬件和软件要求。
关于本书
从基础知识开始,使用 R 进行应用无监督学习解释了聚类方法、分布分析、数据编码器和 R 的所有特性,这些特性使您能更好地理解数据并回答您最紧迫的业务问题。
本书从无监督学习最重要的和最常用的方法——聚类——开始,并解释了三种主要的聚类算法:k-means、划分和层次聚类。在此之后,您将学习市场篮子分析、核密度估计、主成分分析和异常检测。您将使用 R 编写的代码来介绍这些方法,并进一步说明如何使用、编辑和改进 R 代码。为了帮助您获得实际的理解,本书还提供了将这些方法应用于实际业务问题的有用提示,包括市场细分和欺诈检测。通过完成有趣的活动,您将探索数据编码器和潜在变量模型。
在本书结束时,您将更好地理解不同的异常检测方法,例如异常值检测、马氏距离以及上下文和集体异常检测。
关于作者
阿洛克·马利克是一位印度数据科学家。他之前在金融、加密货币交易、物流和自然语言处理等领域创建和部署无监督学习解决方案。他拥有印度信息技术、设计和制造学院贾巴尔普尔的科技学士学位,在那里他学习电子和通信工程。
布拉德福德·塔克菲尔德为各种行业的企业设计和实施数据科学解决方案。他获得了数学学士学位和经济学博士学位。他为学术期刊和大众媒体撰写文章,主题包括线性代数、心理学和公共政策。
摘要
设计聪明的算法,从非结构化和未标记的数据中发现隐藏的模式和业务相关的见解。
关键特性
-
构建能够解决您企业问题的最先进算法
-
学习如何在您的数据中找到隐藏的模式
-
通过使用真实世界的数据集进行动手练习来实施关键概念
描述
从基础知识开始,使用 R 进行应用无监督学习解释了聚类方法、分布分析、数据编码器和 R 的所有特性,这些特性使您能更好地理解数据并回答您所有的业务问题。
学习目标
-
实现聚类方法,如 k-means、层次聚类和划分聚类
-
使用 R 编写代码来分析市场细分和消费者行为
-
估计不同结果的分布和概率
-
使用主成分分析实现降维
-
应用异常检测方法以识别欺诈
-
使用 R 设计算法并学习如何编辑或改进代码
目标受众
《使用 R 进行无监督学习应用》是为希望了解更好地理解数据的方法的商业专业人士和有兴趣进行无监督学习的开发者设计的。尽管这本书是为初学者准备的,但了解 R 的基本、入门级知识将有所帮助。这包括了解如何打开 R 控制台、如何读取数据以及如何创建循环。为了轻松理解本书的概念,您还应了解基本数学概念,包括指数、平方根、平均值和中位数。
方法
《使用 R 进行无监督学习应用》采用实践方法,通过 R 揭示您非结构化数据中的隐藏模式。它包含多个活动,使用现实生活中的商业场景让您练习并应用您的新技能,在高度相关的环境中。
硬件要求
为了获得最佳的学生体验,我们推荐以下硬件配置:
-
处理器:Intel Core i5 或同等性能
-
内存:4 GB RAM
-
存储:5 GB 可用空间
-
一个互联网连接
软件要求
我们还建议您提前安装以下软件:
-
操作系统:Windows 7 SP1 64 位,Windows 8.1 64 位或 Windows 10 64 位,Linux(Ubuntu、Debian、Red Hat 或 Suse),或最新版本的 OS X
-
R(3.0.0 或更高版本,可在
cran.r-project.org/免费获取)
规范
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称将如下所示:“我们导入一个factoextra库来可视化我们刚刚创建的聚类。”
代码块设置如下:
plot(iris_data$Sepal.Length,iris_data$Sepal.Width,col=iris_data$t_color)
points(k1[1],k1[2],pch=4)
points(k2[1],k2[2],pch=5)
points(k3[1],k3[2],pch=6)
新术语和重要词汇将以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,将以如下方式显示:“执行 k-medoids 聚类的算法有很多种。其中最简单、最有效的是围绕类中心的划分,或简称PAM。”
安装和设置
每一段伟大的旅程都始于一个谦卑的步伐。我们即将在数据整理领域的冒险也不例外。在我们能够用数据做些令人惊叹的事情之前,我们需要准备好最富有成效的环境。在这篇简短的笔记中,我们将了解如何做到这一点。
在 Windows 上安装 R
要在 Windows 上安装 R,请按照以下步骤操作:
-
打开最新版 R 的 Windows 页面:
cran.r-project.org/bin/windows/base/。 -
点击名为“下载 R X.X.X for Windows”的链接,其中每个 X 都是一个自然数,例如 R 3.5.3。这将启动文件下载。
-
您在步骤 1中下载的文件是一个
exe文件。双击此文件,将运行一个程序,在您的计算机上安装 R。 -
在步骤 3中运行的安装程序将提示您有关如何安装 R 的一些问题。为每个提示选择默认选项。
-
您可以通过在 Windows 计算机上步骤 4中安装位置的双击 R 的图标来打开 R。
在 macOS X 上安装 R
要在 macOS X 上安装 R,请执行以下操作:
-
打开以下链接,该链接专门提供适用于 macOS X 的最新版 R:
cran.r-project.org/bin/macosx/. -
在最新发布标题下,点击名为R-X.X.X.pkg的文件,其中每个 X 都代表一个自然数,例如,R-3.5.2.pkg。
-
打开您在步骤 2中下载的文件。这将是一个
.pkg文件。当您打开此文件时,OS X 将打开安装向导,引导您完成剩余过程。 -
当安装向导要求您做出关于安装的决定时,请选择默认选项。
-
安装向导完成工作后,您可以在安装位置的双击 R 的图标。这将打开一个 R 控制台。
在 Linux 上安装 R
-
确定您的 Linux 版本使用的是哪种包管理器。两个常见的包管理器示例是
yum和apt。 -
打开终端,使用您的包管理器输入以下两个命令:
sudo apt update sudo apt install r-base在这个例子中,我们使用了
apt作为包管理器,但如果您的 Linux 版本使用yum或其他包管理器,您应该将这些两行中的每个apt替换为yum或您的包管理器名称。 -
如果您遇到问题,可能是您的 Linux 版本没有访问正确的仓库。为了告诉 Linux 正确的仓库位置,您应该打开您版本 Linux 的
sources.list文件。对于 Ubuntu,此文件默认存储在/etc/apt/sources.list。 -
在
sources.list文件中,如果您使用的是 Debian 或 Ubuntu,则需要添加以下两条中的一条。对于 Ubuntu 版本,使用以下链接:cran.rstudio.com/bin/linux/u… Debian 版本,使用以下链接:debhttp://cran.rstudio.com/bin/linux/debian/。如果您使用的是 Red Hat,您可以在终端中运行以下命令来告诉 Linux 下载 R 所使用的仓库:sudo yum install epel-release。
第二章:第一章
聚类方法简介
学习目标
到本章结束时,你将能够:
-
描述聚类的用途
-
使用内置的 R 库执行 k-means 算法
-
使用内置的 R 库执行 k-medoids 算法
-
确定最佳聚类数量
在本章中,我们将探讨聚类的概念和一些基本的聚类算法。
简介
21 世纪是数字世纪,在这个世纪里,经济阶梯上的每个人都在以前所未有的速度使用数字设备并以数字格式产生数据。在过去 10 年中产生的 90%的数据是在过去两年中产生的。这是一个指数增长的趋势,数据量每两年增加 10 倍。预计这种趋势将在可预见的未来继续:
![图 1.1:年度数据增长
图 1.1:年度数字数据增长
但这些数据不仅仅存储在硬盘上;它们正在被用来让生活变得更好。例如,谷歌使用它拥有的数据为你提供更好的搜索结果,Netflix 使用它拥有的数据为你提供更好的电影推荐。事实上,他们制作热门剧集《纸牌屋》的决定是基于数据分析的。IBM 正在使用它拥有的医疗数据创建一个人工智能医生,并从 X 光图像中检测癌细胞。
为了使用计算机处理如此大量的数据并得出相关结果,使用特定类别的算法。这些算法统称为机器学习算法。机器学习根据所使用的数据类型分为两部分:一种称为监督学习,另一种称为无监督学习。
当我们获得标记数据时,进行监督学习。例如,假设我们从一家医院获得了 1,000 张标记为正常或骨折的 X 光片。我们可以使用这些数据来训练一个机器学习模型,以预测 X 光片是否显示骨折的骨头。
无监督学习是在我们只有原始数据并期望在没有标签的情况下得出见解时进行的。我们有能力理解数据并识别其中的模式,而无需明确告知要识别哪些模式。到本书结束时,你将了解所有主要类型的无监督学习算法。在本书中,我们将使用 R 编程语言进行演示,但算法对所有语言都是相同的。
在本章中,我们将研究最基本的无监督学习类型,聚类。首先,我们将研究聚类是什么,它的类型,以及如何使用任何类型的数据集创建聚类。然后我们将研究每种聚类类型的工作原理,查看它们的优缺点。最后,我们将学习何时使用哪种类型的聚类。
聚类简介
聚类是一组用于根据数据集中变量的预定义属性找到自然分组的方法或算法。Merriam-Webster 词典将聚类定义为“一起发生的一组相似事物。”无监督学习中的聚类在传统意义上正是这个意思。例如,你如何从远处识别一串葡萄?你不需要仔细看这串葡萄就能直观地感觉到葡萄是否相互连接。聚类就是这样。下面提供了一个聚类的例子:
图 1.2:数据集中两个聚类的表示
在前面的图表中,数据点有两个属性:胆固醇和血压。根据它们之间的欧几里得距离,数据点被分类为两个聚类,或两个串。一个聚类包含明显有心脏病高风险的人,另一个聚类包含心脏病风险低的人。也可以有超过两个聚类,如下面的例子所示:
图 1.3:数据集中三个聚类的表示
在前面的图表中,有三个聚类。一个额外的人群有高血压但胆固醇低。这个群体可能或可能没有心脏病风险。在接下来的章节中,将在实际数据集上展示聚类,其中 x 和 y 坐标表示实际数量。
聚类的应用
与所有无监督学习方法一样,聚类通常在我们没有标记数据——即具有预定义类别的数据——用于训练模型时使用。聚类使用各种属性,如欧几里得距离和曼哈顿****距离,来寻找数据中的模式并将它们根据其属性的相似性进行分类,而不需要任何用于训练的标签。因此,聚类在标记数据不可用或我们想要找到由标签未定义的模式的应用领域中有许多用例。
以下是一些聚类的应用:
-
探索性数据分析:当我们有未标记的数据时,我们通常会进行聚类以探索数据集的潜在结构和类别。例如,一家零售店可能想要根据购买历史来探索他们有多少不同的客户细分市场。
-
生成训练数据:有时,在用聚类方法处理未标记数据后,它可以被标记以进一步使用监督学习算法进行训练。例如,两个未标记的不同类别可能形成两个完全不同的聚类,我们可以使用它们的聚类来为更有效的实时分类的监督学习算法标记数据,这些算法比我们的无监督学习算法更有效。
-
推荐系统:借助聚类,我们可以找到相似物品的特性,并利用这些特性进行推荐。例如,一个电子商务网站,在找到同一集群的客户后,可以根据该集群中其他客户购买的物品向该集群中的客户推荐商品。
-
自然语言处理:聚类可以用于对相似词语、文本、文章或推文的分组,无需标签数据。例如,你可能希望自动将同一主题的文章分组。
-
异常检测:你可以使用聚类来找到异常值。我们将在第六章 异常检测 中学习这一点。异常检测也可以用于数据中存在不平衡类别的情况,例如在欺诈信用卡交易检测中。
爱丽丝数据集简介
在本章中,我们将使用爱丽丝花朵数据集进行练习,学习如何在不使用标签的情况下对三种爱丽丝花朵(Versicolor、Setosa 和 Virginica)进行分类。这个数据集是 R 内置的,非常适合学习聚类技术的实现。
注意,在我们的练习数据集中,我们为花朵有最终标签。我们将比较聚类结果与这些标签。我们选择这个数据集只是为了展示聚类结果是有意义的。在数据集如批发客户数据集(本书后面将介绍)的情况下,我们没有最终标签,聚类结果无法客观验证,因此可能会导致错误的结论。这就是在实际生活中没有数据集的最终标签时使用聚类的用例。一旦你完成了这两个练习和活动,这一点将更加清晰。
练习 1:探索爱丽丝数据集
在这个练习中,我们将学习如何在 R 中使用爱丽丝数据集。假设你已经在系统中安装了 R,让我们继续:
-
按如下方式将爱丽丝数据集加载到变量中:
iris_data<-iris -
现在,我们的爱丽丝数据已经存储在
iris_data变量中,我们可以使用 R 中的head函数查看其前几行:head(iris_data)输出如下:
![图 1.4:爱丽丝数据集的前六行
图 1.4:爱丽丝数据集的前六行
我们可以看到我们的数据集有五个列。我们将主要使用两个列来简化二维图的可视化。
聚类类型
如前所述,聚类算法可以在数据中找到自然分组。我们可以以多种方式在数据中找到自然分组。以下是我们将在本章中研究的方法:
-
k-means 聚类
-
k-medoids 聚类
一旦基本聚类类型的概念清晰,我们将探讨其他类型的聚类,如下所示:
-
k-modes
-
基于密度的聚类
-
聚类层次聚类
-
分裂聚类
k-means 聚类简介
K-means 聚类是最基本的无监督学习算法之一。这个算法根据预定义的相似度或距离度量找到自然分组。距离度量可以是以下任何一种:
-
欧几里得距离
-
曼哈顿距离
-
余弦距离
-
汉明距离
要了解距离度量做什么,以一束笔为例。你有 12 支笔。其中 6 支是蓝色的,6 支是红色的。其中 6 支是圆珠笔,6 支是墨水笔。如果你要用墨水颜色作为相似度度量,那么 6 支蓝色笔和 6 支红色笔将位于不同的簇中。这里的 6 支蓝色笔可以是墨水笔或圆珠笔,没有限制。但如果你要用笔的类型作为相似度度量,那么 6 支墨水笔和 6 支圆珠笔将位于不同的簇中。现在,每个簇中的笔是否颜色相同并不重要。
欧几里得距离
欧几里得距离是任意两点之间的直线距离。在二维空间中计算这个距离可以被视为你在学校可能学过的勾股定理的扩展。但欧几里得距离可以在任何 n 维空间中计算,而不仅仅是二维空间。任意两点之间的欧几里得距离是它们坐标差的平方和的平方根。这里展示了欧几里得距离计算的例子:
![图 1.5:欧几里得距离计算的表示
![img/C12628_01_05.jpg]
图 1.5:欧几里得距离计算的表示
在 k-means 聚类中,使用欧几里得距离。使用欧几里得距离的一个缺点是,当数据的维度非常高时,它就失去了意义。这与一个被称为维度诅咒的现象有关。当数据集具有许多维度时,它们可能更难处理,因为所有点之间的距离都可能变得极高,而这些距离难以解释和可视化。
因此,当数据的维度非常高时,我们要么通过主成分分析来降低其维度,我们将在第四章,降维中学习到这一方法,要么使用余弦相似度。
曼哈顿距离
根据定义,曼哈顿距离是沿着与坐标轴成直角测量的两点之间的距离:
![图 1.6:曼哈顿距离的表示
![img/C12628_01_06.jpg]
图 1.6:曼哈顿距离的表示
对角线的长度是两点之间的欧几里得距离。曼哈顿距离简单地说就是两个坐标之间差的绝对值的总和。因此,欧几里得距离和曼哈顿距离的主要区别在于,在欧几里得距离中,我们平方坐标之间的距离,然后取和的根,但在曼哈顿距离中,我们直接取坐标之间差的绝对值的总和。
余弦距离
任意两点之间的余弦相似度定义为以原点为顶点的任意两点之间的角度的余弦值。它可以通过将任意两个向量的点积除以向量的模的乘积来计算:
图 1.7:余弦相似度和余弦距离的表示
余弦距离定义为(1-余弦相似度)。
余弦距离的范围是 0 到 2,而余弦相似度的范围在-1 到 1 之间。始终记住,余弦相似度是余弦距离的值的倒数。
汉明距离
汉明距离是一种特殊的距离,用于分类变量。给定两个维度相等的点,汉明距离定义为彼此不同的坐标的数量。例如,让我们取两个点(0,1,1)和(0,1,0)。这两个变量之间只有一个值不同,即最后一个值。因此,它们之间的汉明距离是 1:
图 1.8:汉明距离的表示
k-means 聚类算法
K-means 聚类用于在未标记的数据集的相似点数据集中找到簇。在本章中,我们将使用鸢尾花数据集。这个数据集包含了不同物种的花瓣长度和宽度的信息。借助无监督学习,我们将学习如何在不了解哪些属性属于哪个物种的情况下区分它们。以下是我们数据集的散点图:
图 1.9:鸢尾花数据集的散点图
这是鸢尾花数据集两个变量的散点图:花瓣长度和花瓣宽度。
如果我们要根据点之间的距离来识别前一个数据集中的簇,我们会选择看起来像挂在树上的葡萄串的簇。你可以看到有两个主要的大串(一个在左上角,另一个是剩余的点)。k-means 算法识别这些“葡萄串”。
下图显示了相同的散点图,但用不同颜色显示了三种不同的鸢尾花品种。这些品种来自原始数据集的“species”列,具体如下:鸢尾花 setosa(用绿色表示),鸢尾花 versicolor(用红色表示),和鸢尾花 virginica(用蓝色表示)。我们将通过形成自己的分类来尝试确定这些品种,使用聚类:
图 1.10:展示不同品种的鸢尾花数据集的散点图
这里是鸢尾花 setosa 的照片,在先前的散点图中用绿色表示:
图 1.11:鸢尾花
下面的照片是鸢尾花 versicolor,在先前的散点图中用红色表示:
图 1.12:鸢尾花 versicolor
这里是鸢尾花 virginica 的照片,在先前的散点图中用蓝色表示:
图 1.13:鸢尾花 virginica
实现 k-means 聚类的步骤
正如我们在图 1.9 中的散点图中看到的,每个数据点代表一朵花。我们将找到可以识别这些品种的簇。为了进行这种聚类,我们将使用 k-means 聚类,其中 k 是我们想要的簇数。以下执行 k-means 聚类的步骤,为了理解简单,我们将用两个簇来演示。我们将在稍后使用三个簇,以尝试匹配实际的物种分组:
-
在散点图上选择任意两个随机坐标,k1 和 k2,作为初始簇中心。
-
计算散点图中每个数据点与坐标 k1 和 k2 的距离。
-
根据数据点与 k1 或 k2 的接近程度,将每个数据点分配到簇中。
-
找到每个簇中所有点的平均坐标,并将 k1 和 k2 的值分别更新到这些坐标。
-
从步骤 2重新开始,直到 k1 和 k2 的坐标停止显著移动,或者经过一定预定的迭代次数后。
我们将使用图表和代码演示先前的算法。
练习 2:在鸢尾花数据集上实现 k-means 聚类
在这个练习中,我们将逐步实现 k-means 聚类:
-
在
iris_data变量中加载内置的鸢尾花数据集:iris_data<-iris -
设置不同物种在散点图上的颜色,以便表示。这将帮助我们看到三种不同的物种是如何在我们最初的两组分类之间分割的:
iris_data$t_color='red' iris_data$t_color[which(iris_data$Species=='setosa')]<-'green' iris_data$t_color[which(iris_data$Species=='virginica')]<-'blue' -
选择任意两个随机簇的中心开始:
k1<-c(7,3) k2<-c(5,3)注意
你可以尝试改变点,看看它如何影响最终的簇。
-
绘制散点图,并包含您在上一步骤中选择的中心。在第一行中,将鸢尾花花瓣的长度和宽度以及颜色传递给
plot函数,然后将中心和点的 x 和 y 坐标传递给points()函数。在这里,pch用于选择聚类中心的表示类型——在这种情况下,4 代表一个十字,5 代表一个菱形:plot(iris_data$Sepal.Length,iris_data$Sepal.Width,col=iris_data$t_color) points(k1[1],k1[2],pch=4) points(k2[1],k2[2],pch=5)输出如下:
图 1.14:所选聚类中心点的散点图
-
选择您想要的迭代次数。迭代次数应使得每次迭代后中心的变化不再显著。在我们的例子中,六次迭代就足够了:
number_of_steps<-6 -
初始化将跟踪循环中迭代次数的变量:
n<-1 -
开始
while循环以找到最终的聚类中心:while(n<number_of_steps) { -
计算每个点到当前聚类中心的距离,这是算法中的第二步。我们在这里使用
sqrt函数计算欧几里得距离:iris_data$distance_to_clust1 <- sqrt((iris_data$Sepal.Length-k1[1])²+(iris_data$Sepal.Width-k1[2])²) iris_data$distance_to_clust2 <- sqrt((iris_data$Sepal.Length-k2[1])²+(iris_data$Sepal.Width-k2[2])²) -
将每个点分配到其最近的中心所在的聚类,这是算法的第三步:
iris_data$clust_1 <- 1*(iris_data$distance_to_clust1<=iris_data$distance_to_clust2) iris_data$clust_2 <- 1*(iris_data$distance_to_clust1>iris_data$distance_to_clust2) -
通过计算每个聚类中点的平均
x和y坐标来计算新的聚类中心(算法中的第四步),使用 R 中的mean()函数:k1[1]<-mean(iris_data$Sepal.Length[which(iris_data$clust_1==1)]) k1[2]<-mean(iris_data$Sepal.Width[which(iris_data$clust_1==1)]) k2[1]<-mean(iris_data$Sepal.Length[which(iris_data$clust_2==1)]) k2[2]<-mean(iris_data$Sepal.Width[which(iris_data$clust_2==1)]) -
更新变量以记录迭代的次数,以便有效地执行算法的第五步:
n=n+1 } -
现在,我们将用新的颜色覆盖物种颜色以展示两个聚类。因此,我们的下一个散点图上只有两种颜色——一种颜色代表聚类 1,另一种颜色代表聚类 2:
iris_data$color='red' iris_data$color[which(iris_data$clust_2==1)]<-'blue' -
绘制包含聚类及其中心的新的散点图:
plot(iris_data$Sepal.Length,iris_data$Sepal.Width,col=iris_data$color) points(k1[1],k1[2],pch=4) points(k2[1],k2[2],pch=5)输出如下:
图 1.15:用不同颜色表示每个聚类的散点图
注意到 setosa(以前是绿色)已被分组在左侧聚类中,而大多数 virginica 花朵(以前是蓝色)已被分组在右侧聚类中。versicolor 花朵(以前是红色)被分在两个新的聚类之间。
您已成功实现 k-means 聚类算法,根据花瓣大小识别两组花朵。注意算法运行后中心位置的变化。
在以下活动中,我们将增加聚类的数量到三个,以查看我们是否可以将花朵正确地分组到三种不同的物种中。
活动 1:使用三个聚类的 k-means 聚类
编写一个 R 程序,使用三个聚类对鸢尾花数据集进行 k-means 聚类。在这个活动中,我们将执行以下步骤:
-
在图上选择任意三个随机坐标,k1、k2 和 k3,作为中心。
-
计算每个数据点到 k1、k2 和 k3 的距离。
-
通过找到最近的聚类中心来对每个点进行分类。
-
找到各自聚类中所有点的平均坐标,并将 k1、k2 和 k3 的值更新到这些值。
-
从 步骤 2 重新开始,直到 k1、k2 和 k3 的坐标停止显著移动,或者经过 10 次迭代过程后。
本活动的结果将是一个包含三个聚类的图表,如下所示:
图 1.16:给定聚类中心的预期散点图
您可以将您的图表与图 1.10 进行比较,以查看聚类与实际物种分类的匹配程度。
注意
本活动的解决方案可在第 198 页找到。
使用内置函数的 k-means 聚类介绍
在本节中,我们将使用 R 的某些内置库来执行 k-means 聚类,而不是编写冗长且容易出错的自定义代码。使用预构建库而不是编写自己的代码也有其他优点:
-
库函数在计算上效率很高,因为这些函数的开发投入了数千人的工时。
-
库函数几乎无错误,因为它们在几乎所有实际可用的场景中都被数千人测试过。
-
使用库可以节省时间,因为您不必花费时间编写自己的代码。
使用三个聚类的 k-means 聚类
在上一个活动中,我们通过编写自己的代码执行了具有三个聚类的 k-means 聚类。在本节中,我们将借助预构建的 R 库实现类似的结果。
首先,我们将在数据集中以三种类型的花的分布开始,如下所示:
图 1.17:用三种颜色表示三种鸢尾花物种的图
在前面的图中,setosa 用蓝色表示,virginica 用灰色表示,versicolor 用粉色表示。
使用这个数据集,我们将执行 k-means 聚类,看看内置算法是否能够自己找到模式来根据花瓣大小对这些三种鸢尾花物种进行分类。这次,我们只需要四行代码。
练习 3:使用 R 库进行 k-means 聚类
在这个练习中,我们将学习使用 R 的预构建库以更简单的方式执行 k-means 聚类。通过完成这个练习,您将能够将三种 Iris 物种划分为三个单独的聚类:
-
我们将鸢尾花数据集的前两列,即花瓣长度和花瓣宽度,放入
iris_data变量中:iris_data<-iris[,1:2] -
我们找到 k-means 聚类中心以及每个点所属的聚类,并将所有这些存储在
km.res变量中。在这里,在kmeans函数中,我们将数据集作为第一个参数输入,我们想要的聚类数量作为第二个参数:km.res<-kmeans(iris_data,3)注意
k-means 函数有许多输入变量,可以调整以获得不同的最终输出。你可以在
www.rdocumentation.org/packages/stats/versions/3.5.1/topics/kmeans的文档中了解更多信息。 -
按以下步骤安装
factoextra库:install.packages('factoextra') -
我们导入
factoextra库来可视化我们刚刚创建的簇。Factoextra是一个 R 包,用于绘制多元数据:library("factoextra") -
生成簇的图。在这里,我们需要将 k-means 的结果作为第一个参数输入。在
data中,我们需要输入聚类所用的数据。在pallete中,我们选择点的几何形状类型,在ggtheme中,我们选择输出图的样式:fviz_cluster(km.res, data = iris_data,palette = "jco",ggtheme = theme_minimal())输出将如下所示:
图 1.18:三种鸢尾花已经被聚成三个簇
在这里,如果你将图 1.18 与图 1.17 进行比较,你会看到我们几乎正确地分类了所有三种物种。我们生成的簇与图 1.18 中显示的物种不完全匹配,但考虑到仅使用花瓣长度和宽度进行分类的限制,我们已经非常接近了。
从这个例子中,你可以看到,如果我们不知道它们的物种,聚类将是一个非常有用的对鸢尾花进行分类的方法。你将遇到许多数据集的例子,在这些数据集中,你没有标记的类别,但能够使用聚类来形成自己的分组。
市场细分简介
市场细分是根据共同特征将客户划分为不同的细分市场。以下是一些客户细分的用途:
-
提高客户转化率和留存率
-
通过识别特定细分市场和其需求来开发新产品
-
通过特定细分市场改善品牌沟通
-
识别营销策略中的差距并制定新的营销策略以增加销售额
练习 4:探索批发客户数据集
在这个练习中,我们将查看批发客户数据集中的数据。
注意
对于所有需要导入外部 CSV 或图像文件的外部练习和活动,请转到RStudio-> 会话-> 设置工作目录-> 到源文件位置。你可以在控制台中看到路径已自动设置。
-
要下载 CSV 文件,请访问
github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Exercise04/wholesale_customers_data.csv。点击wholesale_customers_data.csv。注意
此数据集来自 UCI 机器学习仓库。您可以在以下网址找到数据集:
archive.ics.uci.edu/ml/machine-learning-databases/00292/。我们已经下载了文件并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Exercise04/wholesale_customers_data.csv. -
将其保存到您安装 R 的文件夹中。现在,要在 R 中加载它,请使用以下函数:
ws<-read.csv("wholesale_customers_data.csv") -
现在,我们可以通过使用以下 R 函数来查看这个数据集的不同列和行:
head(ws)输出如下:
![Figure 1.19:批发客户数据集的列]
![img/C12628_01_19.jpg]
图 1.19:批发客户数据集的列
这六行显示了按产品类别划分的年度货币消费的前六行。
活动二:使用 k-means 进行客户细分
对于这个活动,我们将使用来自 UCI 机器学习仓库的批发客户数据集。它可在以下网址找到:github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Activity02/wholesale_customers_data.csv。我们将通过聚类来识别属于不同市场细分、喜欢购买不同类型商品的客户。尝试使用 k-means 聚类,k 的值为 2 到 6。
注意
此数据集来自 UCI 机器学习仓库。您可以在以下网址找到数据集:archive.ics.uci.edu/ml/machine-learning-databases/00292/。我们已经下载了文件并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Activity02/wholesale_customers_data.csv.
这些步骤将帮助您完成活动:
-
将从 UCI 机器学习仓库下载的数据读入一个变量。数据可在以下网址找到:
github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Activity02/wholesale_customers_data.csv. -
只选择两列,即杂货和冷冻食品,以便于可视化聚类。
-
如同第 4 步中练习 4,探索批发客户数据集的第 2 步,将聚类数量设置为 2 并生成聚类中心。
-
按照第 4 步中的练习 4,探索批发客户数据集绘制图表。
-
保存您生成的图表。
-
通过改变聚类数量的值,重复步骤 3、步骤 4和步骤 5,将聚类数量设置为 3、4、5 和 6。
-
决定哪个聚类数量的值最适合对数据进行分类。
输出将是一个包含六个聚类的图表,如下所示:
图 1.20:预期六个聚类的图表
注意
本活动的解决方案可以在第 201 页找到。
k-medoids 聚类简介
k-medoids 是另一种聚类算法,可用于在数据集中找到自然分组。k-medoids 聚类与 k-means 聚类非常相似,除了几个不同之处。k-medoids 聚类算法的优化函数与 k-means 略有不同。在本节中,我们将研究 k-medoids 聚类。
k-medoids 聚类算法
有许多不同类型的算法可以执行 k-medoids 聚类,其中最简单且效率最高的算法是基于聚类中心的划分,简称 PAM。在 PAM 中,我们执行以下步骤来找到聚类中心:
-
从散点图中选择 k 个数据点作为聚类中心的起始点。
-
计算它们与散点图中所有点的距离。
-
将每个点分类到其最近的中心所在的聚类中。
-
在每个聚类中选择一个新点,该点使该聚类中所有点与该点的距离之和最小。
-
重复步骤 2,直到中心不再改变。
您可以看到,PAM 算法与 k-means 聚类算法相同,除了步骤 1和步骤 4。对于大多数实际应用,k-medoids 聚类几乎给出了与 k-means 聚类相同的结果。但在某些特殊情况下,如果数据集中有异常值,k-medoids 聚类更受欢迎,因为它对异常值更稳健。关于何时使用哪种类型的聚类及其差异将在后面的章节中研究。
k-medoids 聚类代码
在本节中,我们将使用与上一节相同的鸢尾花数据集,并将其与上次的结果进行比较,以查看结果是否与上次的结果有明显的不同。我们将直接使用 R 的库来执行 PAM 聚类,而不是编写代码来执行 k-medoids 算法的每个步骤。
练习 5:实现 k-medoid 聚类
在这个练习中,我们将使用 R 的预建库来执行 k-medoids 聚类:
-
将鸢尾花数据集的前两列存储在
iris_data变量中:iris_data<-iris[,1:2] -
安装
cluster包:install.packages("cluster") -
导入
cluster包:library("cluster") -
将 PAM 聚类结果存储在
km.res变量中:km<-pam(iris_data,3) -
导入
factoextra库:library("factoextra") -
在图中绘制 PAM 聚类结果:
fviz_cluster(km, data = iris_data,palette = "jco",ggtheme = theme_minimal())输出如下所示:
图 1.21:k-medoids 聚类结果
k-medoids 聚类的结果与我们在上一节中执行的 k-means 聚类结果没有很大差异。
因此,我们可以看到,前面的 PAM 算法将我们的数据集划分为三个与 k-means 聚类得到的簇相似的簇。如果我们将这两种聚类的结果并排绘制,我们可以清楚地看到它们是多么相似:
图 1.22:k-medoids 聚类与 k-means 聚类的结果
在前面的图表中,观察 k-means 和 k-medoids 聚类的中心是如何如此接近的,但 k-medoids 聚类的中心直接重叠在数据中的点上,而 k-means 聚类的中心则不是。
k-means 聚类与 k-medoids 聚类
现在我们已经研究了 k-means 和 k-medoids 聚类,它们几乎相同,我们将研究它们之间的差异以及何时使用哪种类型的聚类:
-
计算复杂度:在这两种方法中,k-medoids 聚类的计算成本更高。当我们的数据集太大(>10,000 个点)且我们想要节省计算时间时,我们将更倾向于选择 k-means 聚类而不是 k-medoids 聚类。
注意
数据集的大小完全取决于可用的计算能力。随着时间的推移,计算成本越来越低,因此被认为是大数据集的标准将在未来发生变化。
-
异常值的存在:与 k-medoids 聚类相比,k-means 聚类对异常值更敏感。由于数据集中存在异常值,簇中心的定位可能会发生显著变化,因此当我们需要构建对异常值有弹性的簇时,我们使用 k-medoids 聚类。
-
簇中心:k-means 和 k-medoids 算法以不同的方式找到簇中心。k-medoids 簇的中心始终是数据集中的数据点。k-means 簇的中心不需要是数据集中的数据点。
活动三:使用 k-medoids 聚类进行客户细分
使用批发客户数据集进行 k-means 和 k-medoids 聚类,然后比较结果。将从 UCI 机器学习仓库下载的数据读入一个变量。数据可以在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Data/wholesale_customers_data.csv找到。
注意
此数据集来自 UCI 机器学习仓库。您可以在archive.ics.uci.edu/ml/machine-learning-databases/00292/找到数据集。我们已经下载了文件并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Activity03/wholesale_customers_data.csv.
这些步骤将帮助您完成活动:
-
仅选择两列,杂货和冷冻,以便于进行簇的二维可视化。
-
使用 k-medoids 聚类来绘制显示四个簇的图表。
-
使用 k-means 聚类来绘制四簇图表。
-
比较两个图表,以评论两种方法的结果差异。
结果将是一个 k-means 簇的图表,如下所示:
图 1.23:簇的预期 k-means 图
注意
本活动的解决方案可以在第 206 页找到。
决定簇的最佳数量
到目前为止,我们一直在处理鸢尾花数据集,其中我们知道有多少种类的花,并且我们根据这个知识将我们的数据集划分为三个簇。但在无监督学习中,我们的主要任务是处理关于我们没有任何信息的数据,例如数据集中有多少自然簇或类别。此外,聚类也可以是一种探索性数据分析的形式,在这种情况下,你不会对数据有太多了解。有时,当数据有超过两个维度时,可视化变得困难,手动找出簇的数量也变得困难。那么,在这些情况下,我们如何找到簇的最佳数量呢?在本节中,我们将学习获取簇数量最佳值的技术。
聚类度量类型
在无监督学习中,确定簇的最佳数量有多种方法。以下是我们将在本章中研究的方法:
-
轮廓分数
-
肘部方法 / WSS
-
Gap 统计量
轮廓分数
轮廓分数或平均轮廓分数的计算用于量化聚类算法获得的簇的质量。让我们以簇 x 中的一个点 a 为例:
-
计算点 a 与簇 x 中所有点的平均距离(用 dxa 表示):
图 1.24:计算点 a 与簇 x 中所有点的平均距离
-
计算点 a 与另一个簇中离 a 最近的簇中所有点的平均距离(dya)
图 1.25:计算点 a 与簇 x 中所有点的平均距离
-
通过将 步骤 1 的结果与 步骤 2 的结果之差除以 步骤 1 和 步骤 2 的最大值来计算该点的轮廓分数((dya-dxa)/max(dxa,dya))。
-
对簇中的所有点重复前三个步骤。
-
在得到聚类中每个点的轮廓得分后,所有这些得分的平均值是该聚类的轮廓得分:![图 1.26:计算轮廓得分
图 1.26:计算轮廓得分
-
对数据集中的所有聚类重复前面的步骤。
-
在得到数据集中所有聚类的轮廓得分后,所有这些得分的平均值是该数据集的轮廓得分:
![图 1.27:计算平均轮廓得分
图 1.27:计算平均轮廓得分
轮廓得分介于 1 和 -1 之间。如果一个聚类的轮廓得分低(介于 0 和 -1 之间),这意味着该聚类分布较广或该聚类中点的距离较高。如果一个聚类的轮廓得分高(接近 1),这意味着聚类定义良好,聚类中点的距离较低,而与其他聚类中点的距离较高。因此,理想的轮廓得分接近 1。
理解前面的算法对于形成对轮廓得分的理解很重要,但它对于学习如何实现它并不重要。因此,我们将学习如何使用一些预构建的库在 R 中进行轮廓分析。
练习 6:计算轮廓得分
在这个练习中,我们将学习如何计算具有固定聚类数量的数据集的轮廓得分:
-
将 Iris 数据集的前两列,即花瓣长度和花瓣宽度,放入
iris_data变量中:iris_data<-iris[,1:2] -
导入
cluster库以执行 k-means 聚类:library(cluster) -
将 k-means 聚类存储在
km.res变量中:km.res<-kmeans(iris_data,3) -
将所有数据点的成对距离矩阵存储在
pair_dis变量中:pair_dis<-daisy(iris_data) -
计算数据集中每个点的轮廓得分:
sc<-silhouette(km.res$cluster, pair_dis) -
绘制轮廓得分图:
plot(sc,col=1:8,border=NA)输出如下:
![图 1.28:每个聚类中每个点的轮廓得分用一个单独的条形表示
图 1.28:每个聚类中每个点的轮廓得分用一个单独的条形表示
前面的图显示了数据集的平均轮廓得分为 0.45。它还显示了按聚类和按点计算的平均轮廓得分。
在前面的练习中,我们计算了三个聚类的轮廓得分。但为了决定有多少个聚类,我们必须计算数据集中多个聚类的轮廓得分。在下一个练习中,我们将学习如何使用 R 中的 factoextra 库来完成这项工作。
练习 7:确定最佳聚类数量
在这个练习中,我们将通过在一行代码中使用 R 库来计算 k 的各种值,以确定最佳聚类数量:
-
将 Iris 数据集的前两列,即花瓣长度和花瓣宽度,放入
iris_data变量中:iris_data<-iris[,1:2] -
导入
factoextra库:library("factoextra") -
绘制轮廓分数与簇数量(最多 20 个)的图表:
fviz_nbclust(iris_data, kmeans, method = "silhouette",k.max=20)注意
在第二个参数中,你可以将 k-means 改为 k-medoids 或任何其他类型的聚类。
k.max变量是要计算的簇的最大数量。在函数的方法参数中,你可以输入要包含的三种聚类度量类型。所有这三种度量在本章中都有讨论。输出如下:
![图 1.29:簇数量与平均轮廓分数的关系]
图 1.29:簇数量与平均轮廓分数的关系
从前面的图表中,你选择一个具有最高分数的 k 值;即 2。根据轮廓分数,2 是最佳簇数量。
WSS/肘部方法
为了在数据集中识别簇,我们尝试最小化簇内点之间的距离,而内部平方和(WSS)方法正是衡量这一点。WSS 分数是簇内所有点距离平方的总和。在此方法中,我们执行以下步骤:
-
使用不同的 k 值计算簇。
-
对于每个 k 值,使用以下公式计算 WSS:
图 1.30:计算 WSS 的公式,其中 p 是数据的总维度数
此公式在此处展示:
图 1.31:WSS 分数的说明
注意
图 1.31 说明了相对于两个点的 WSS,但现实中 WSS 衡量的是相对于每个簇内所有点的所有距离的总和。
-
绘制簇数量 k 与 WSS 分数的关系图。
-
确定 WSS 分数不再显著下降的 k 值,并选择这个 k 作为理想的簇数量。这个点也被称为图表的“肘部”,因此得名“肘部方法”。
在接下来的练习中,我们将学习如何借助factoextra库来确定理想簇的数量。
练习 8:使用 WSS 确定簇的数量
在这个练习中,我们将看到如何使用 WSS 来确定簇的数量。执行以下步骤。
-
将 Iris 数据集的前两列,即花瓣长度和花瓣宽度,放入
iris_data变量中:iris_data<-iris[,1:2] -
导入
factoextra库:library("factoextra") -
绘制 WSS 与簇数量(最多 20 个)的图表:
fviz_nbclust(iris_data, kmeans, method = "wss", k.max=20)输出如下:
![图 1.32:WSS 与簇数量的关系]
图 1.32:WSS 与簇数量的关系
在前面的图表中,我们可以选择图表的肘部作为 k=3,因为当 k=3 后,WSS 的值开始下降得更慢。选择图表的肘部始终是一个主观的选择,有时你可能选择 k=4 或 k=2 而不是 k=3,但在这个图表中,很明显 k>5 的值不合适,因为它们不是图表的肘部,图表的斜率在这里急剧变化。
间隙统计
间隙统计是寻找数据集中最佳聚类数量的最有效方法之一。它适用于任何类型的聚类方法。间隙统计是通过比较在观测数据集上生成的聚类与在参考数据集上生成的聚类(其中没有明显的聚类)的 WSS 值来计算的。参考数据集是在我们想要计算间隙统计的观测数据集的最小值和最大值之间均匀分布的数据点。
因此,简而言之,间隙统计测量观测和随机数据集的 WSS 值,并找出观测数据集与随机数据集的偏差。为了找到理想的聚类数量,我们选择一个使间隙统计值最大的 k 值。这些偏差如何测量的数学细节超出了本书的范围。在下一项练习中,我们将学习如何使用 factoviz 库计算间隙统计。
这里是一个参考数据集:
![图 1.33:参考数据集
图 1.33:参考数据集
以下为观测数据集:
![图 1.34:观测数据集
图 1.34:观测数据集
练习 9:使用间隙统计计算理想的聚类数量
在这个练习中,我们将使用间隙统计计算理想的聚类数量:
-
将 Iris 数据集的前两列,即花瓣长度和花瓣宽度,放入
iris_data变量中,如下所示:iris_data<-iris[,1:2] -
按以下方式导入
factoextra库:library("factoextra") -
绘制间隙统计与聚类数量(最多 20)的图表:
fviz_nbclust(iris_data, kmeans, method = "gap_stat",k.max=20)
![图 1.35:间隙统计与聚类数量
图 1.35:间隙统计与聚类数量
如前图所示,间隙统计的最高值对应于 k=3。因此,iris 数据集中的理想聚类数量是三个。三个也是数据集中的物种数量,这表明间隙统计使我们得出了正确的结论。
活动 4:寻找理想的市场细分数量
使用前述三种方法在批发客户数据集中找到最佳聚类数量:
注意
此数据集来自 UCI 机器学习仓库。您可以在archive.ics.uci.edu/ml/machine-learning-databases/00292/找到数据集。我们已经下载了文件并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson01/Activity04/wholesale_customers_data.csv.
-
在一个变量中加载批发客户数据集的第 5 到 6 列。
-
使用轮廓分数计算 k-means 聚类的最佳聚类数量。
-
使用 WSS 分数计算 k-means 聚类的最佳聚类数量。
-
使用 Gap 统计量计算 k-means 聚类的最佳聚类数量。
结果将是三个图表,分别表示最佳聚类数量与轮廓分数、WSS 分数和 Gap 统计量。
注意
本活动的解决方案可以在第 208 页找到。
正如我们所看到的,每种方法都会给出最佳聚类数量的不同值。有时,结果可能没有意义,就像你在 Gap 统计量的例子中看到的那样,它给出的最佳聚类数量为一,这意味着不应该在这个数据集上进行聚类,所有数据点都应该在一个单独的聚类中。
给定聚类中的所有点将具有相似的性质。对这些性质的解读留给领域专家。在无监督学习中,几乎永远没有一个正确的答案来确定正确的聚类数量。
摘要
恭喜!你已经完成了这本书的第一章。如果你理解了我们至今所学的所有内容,你现在对无监督学习的了解比大多数声称了解数据科学的人都要多。k-means 聚类算法对无监督学习来说如此基础,以至于许多人把 k-means 聚类和无监督学习等同起来。
在本章中,你不仅学习了 k-means 聚类及其应用,还学习了 k-medoids 聚类,以及各种聚类指标及其应用。因此,现在你对 k-means 和 k-medoid 聚类算法有了顶级理解。
在下一章,我们将探讨一些不太为人所知的聚类算法及其应用。
第三章:第二章
高级聚类方法
学习目标
到本章结束时,您将能够:
-
执行 k-modes 聚类
-
实现 DBSCAN 聚类
-
执行层次聚类并在树状图中记录聚类
-
执行分裂和聚合聚类
在本章中,我们将探讨一些高级聚类方法以及如何在树状图中记录聚类。
简介
到目前为止,我们已经学习了无监督学习的一些最基本算法:k-means 聚类和 k-medoids 聚类。这些算法不仅对实际应用很重要,而且对于理解聚类本身也很重要。
在本章中,我们将研究一些其他高级聚类算法。我们不称它们为高级是因为它们难以理解,而是因为在使用它们之前,数据科学家应该了解为什么他们选择这些算法而不是我们在上一章中研究的一般聚类算法。k-means 是一种通用聚类算法,对于大多数情况都足够用,但在某些特殊情况下,根据数据类型,高级聚类算法可以产生更好的结果。
k-modes 聚类的简介
我们迄今为止研究过的所有聚类类型都是基于距离度量的。但如果我们得到一个数据集,其中无法以传统方式测量变量之间的距离,例如分类变量的情况,怎么办?在这种情况下,我们使用 k-modes 聚类。
k-modes 聚类是 k-means 聚类的扩展,处理的是众数而不是均值。k-modes 聚类的主要应用之一是分析调查结果等分类数据。
k-Modes 聚类的步骤
在统计学中,众数定义为最频繁出现的值。因此,对于 k-modes 聚类,我们将计算分类值的众数来选择中心。因此,执行 k-modes 聚类的步骤如下:
-
选择任意 k 个随机点作为聚类中心。
-
计算每个点到中心的汉明距离(在第一章,聚类方法简介中讨论)。
-
根据汉明距离将每个点分配到最近的中心所在的聚类。
-
通过找到该聚类中所有数据点的众数来在每个聚类中选择新的聚类中心。
-
从 步骤 2 重复此操作,直到聚类中心不再变化。
您可能已经注意到,这些步骤与 k-means 聚类的步骤非常相似。这里只是改变了距离度量的类型。所以,如果您理解了 k-means 聚类,那么理解 k-modes 聚类也会很容易。
练习 10:实现 k-modes 聚类
注意
对于所有需要导入外部 CSV 或图像的练习和活动,请转到 RStudio-> 会话-> 设置工作目录-> 到源文件位置。您可以在控制台中看到路径已自动设置。
此练习的数据可以从这里下载:github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson02/Exercise10/breast_cancer.csv。这是一个包含九个变量的分类数据集,其中一些是分类的,一些是名义的,描述了不同的乳腺癌病例。在将数据保存到名为breast_cancer.csv的文件中后,我们将执行以下操作:
注意
此数据集来自 UCI 机器学习仓库。您可以在archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer/breast-cancer.data找到数据集。这个乳腺癌领域的数据来自南斯拉夫卢布尔雅那大学医学院,肿瘤研究所。感谢 M. Zwitter 和 M. Soklic 提供数据。我们已经下载了文件,并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson02/Exercise10/breast_cancer.csv。
-
读取数据集并将其存储到一个变量中:
bc_data<-read.csv('breast_cancer.csv',header = FALSE) -
将从第二列到最后一列的所有列存储到一个新变量中:
k_bc_data<-bc_data[,2:10] -
查看变量
k_bc_data的前六行:head(k_bc_data)输出包含六个行,其中包含描述患者、症状和治疗的各个属性值:
V2 V3 V4 V5 V6 V7 V8 V9 V10 1 30-39 premeno 30-34 0-2 no 3 left left_low no 2 40-49 premeno 20-24 0-2 no 2 right right_up no 3 40-49 premeno 20-24 0-2 no 2 left left_low no 4 60-69 ge40 15-19 0-2 no 2 right left_up no 5 40-49 premeno 0-4 0-2 no 2 right right_low no 6 60-69 ge40 15-19 0-2 no 2 left left_low no -
导入
klaR库,它包含kmodes函数。klaR是一个 R 库,用于分类和可视化:install.packages("klaR") library(klaR) -
预测并存储最终的集群中心到一个变量中。在这一步,我们输入数据集和集群数量(即
k和找到集群数量的最大迭代次数):k.centers<-kmodes(k_bc_data,2,iter.max = 100) -
查看集群中心:
k.centers输出如下:
图 2.1:集群中心的截图
聚类算法将所有乳腺癌病例分为两个集群,每个集群包含彼此相似的病例。在输出中,有两个主要部分:集群模式和聚类向量。集群模式部分告诉我们集群 1 和集群 2 的中心或坐标。下面,聚类向量包含索引序列中每个数据点的集群编号。
注意
由于中心的随机起始位置,每次运行算法都可能得到不同的结果。
由于这是一个多维分类数据集,除了将数据打印到 R 控制台之外,没有简单的方法来可视化结果。
活动 5:在蘑菇数据集上实现 k-modes 聚类
注意
此数据集来自 UCI 机器学习仓库。您可以在 archive.ics.uci.edu/ml/datasets/Mushroom 找到数据集。我们已经下载了文件,清理了数据,并将其保存在 github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson02/Activity05。
在本活动中,我们将对蘑菇数据集执行 k-modes 聚类。此数据集列出了 23 种不同蘑菇的属性。每种蘑菇被分类为可食用(e)或有毒(p)。我们将看到无监督学习如何通过将数据分组到两个簇中来对有毒和可食用蘑菇进行分类。以下步骤将帮助您完成活动:
-
从
github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson02/Activity05下载mushrooms.csv。 -
将
mushrooms.csv文件读入一个变量中。 -
导入
klaR库。 -
根据 k-modes 聚类计算簇。
-
通过形成数据标签与分配的簇之间的矩阵来检查聚类的结果。
注意
本活动的解决方案可以在第 212 页找到。
输出将是一个真实标签和簇标签的表格,如下所示:
1 2
e 80 4128
p 3052 864
密度聚类(DBSCAN)简介
基于密度的聚类或 DBSCAN 是聚类中最直观的形式之一。使用这种类型的聚类很容易找到数据中的自然簇和异常值。此外,它不需要你定义簇的数量。例如,考虑以下图示:
图 2.2:一个示例散点图
此数据集中有四个自然簇和一些异常值。因此,DBSCAN 将分离簇和异常值,如图所示,而无需您指定数据集中要识别的簇的数量:
图 2.3:由 DBSCAN 分类出的簇和异常值
因此,DBSCAN 可以在散点图中找到由低密度区域分隔的高密度区域。
DBSCAN 步骤
如前所述,DBSCAN 不需要您选择簇的数量,但您必须选择其他两个参数来执行 DBSCAN。第一个参数通常用 ε(epsilon)表示,表示同一簇中两点之间的最大距离。另一个参数是簇中的最小点数,通常用 minPts 表示。现在我们将逐步查看 DBSCAN 聚类算法:
-
在数据集中选择任何点,
R。 -
在距离点
R的 epsilon 范围内找到所有点。 -
如果从点
R出发,距离 epsilon 内的点的总数大于minPts,那么它是一个簇,R是核心点。 -
如果从点 p 出发,距离 epsilon 内的点的总数小于
minPts,那么距离 epsilon 内的所有点将被归类为噪声。然后,我们选择一个新的点,R,它既没有被归类为噪声,也没有被归类为簇的一部分,从步骤 2 重新开始这个过程。 -
对簇中的其他点重复此过程,以找到距离 epsilon 内不在簇中的点。这些新点也将被归类到同一个簇中。
-
当对簇中的所有点执行了这些步骤后,通过选择一个新的随机点,
R,它既没有被归类到簇中,也没有被归类为噪声,重复相同的过程。
因此,为了说明前面的算法,让我们以 epsilon 为x和簇中的最小点数为 4 为例。看看下面的图:
图 2.4:只有 2 个点位于点 R1 的 x 距离内
只有三个点位于点R1的x距离范围内,而我们对于x半径内最小点数的阈值是四个。因此,这四个点将被归类为异常值或噪声。但如果再有一个点,R5,位于R1和R4之间,所有这四个点将属于一个簇,如下面的图所示:
图 2.5:这四个点都属于一个簇
在前面的图中,点R1和R5是核心点,因为它们各自有四个点在x距离内。而点R4、R2和R3不是核心点,如下面的图所示:
图 2.6:核心点与非核心点
任何位于这些圆圈之外的点将被归类为噪声点,如下面的图中的R6所示:
图 2.7:噪声点 R6
练习 11:实现 DBSCAN
在这个练习中,我们将查看使用 Iris 数据集实现 DBSCAN 的过程。为了执行 DBSCAN,我们将执行以下步骤:
-
将 Iris 数据集的前两列存储在
iris_data中:iris_data<-iris[,1:2] -
导入
dbscan库,它包含各种 DBSCAN 算法的实现:install.packages("dbscan") library(dbscan) -
计算并存储簇在
clus变量中。在这个步骤中,你还需要选择 epsilon 和minPts的值:clus<-dbscan(iris_data,eps=.3,minPts = 5)注意
你可以尝试调整 epsilon 的值。为了得到期望的输出,我们将其设置为 0.3。
-
导入
factoextra库以可视化簇:install.packages("factoextra") #use it only the first time if library is not installed already library(factoextra) -
绘制簇中心。您需要将存储 DBSCAN 结果的变量作为
plot函数的第一个参数输入。作为第二个参数,您需要输入一个数据集。在我们的例子中,它是iris_data。函数中的geom变量用于定义图形的几何形状。我们只会使用point。现在,ggtheme用于选择图形的主题。palette用于选择点的几何形状。将ellipse值设置为 false,这样函数就不会绘制簇的轮廓。fviz_cluster(clus,data=iris_data,geom = "point",palette="set2",ggtheme=theme_minimal(),ellipse=FALSE)这是输出结果:
图 2.8:不同颜色的 DBSCAN 簇
在图 2.8 中,有三个簇,分别是橙色、蓝色和绿色。黑色点是噪声或异常值。这里的橙色点属于一个物种,而绿色点属于两个不同的物种。
注意
您可以快速比较这张散点图与第一章,聚类方法简介中的图 1.18。
DBSCAN 与我们迄今为止研究过的所有聚类方法都不同,并且在科学文献中是最常见和最常引用的聚类方法之一。DBSCAN 相对于其他聚类方法有几个优点:
-
您最初不需要选择簇的数量。
-
它每次运行时不会产生不同的结果,这与 k-means 或其他“k 型”聚类方法不同,其中起始点可能会影响最终结果。因此,结果是可重复的。
-
它可以在数据中发现任何形状的簇。
但 DBSCAN 也有一些缺点:
-
确定正确的参数集,即 epsilon 和
minPts,对于数据的适当聚类来说很难确定。 -
DBSCAN 无法根据密度区分簇。如果一个数据集的密度变化很大,它可能表现不佳。
DBSCAN 的应用
作为最常引用的聚类方法之一,DBSCAN 有许多实际应用。DBSCAN 聚类的一些实际应用包括以下内容:
-
DBSCAN 可以在城市规划中以多种方式使用。例如,给定关于犯罪事件位置的数据,DBSCAN 可以用来识别城市中的犯罪多发区。这些数据可以用来规划警察力量的部署,甚至调查潜在的帮派活动。
-
DBSCAN 可以用来在板球和篮球等游戏中制定策略。给定板球场地上的投球数据,您可以识别击球手和投球手的优缺点。或者如果我们有关于击球手击球的数据,从 DBSCAN 中获得的数据可以用来相应地调整防守。
-
在自然灾害或病毒疾病的传播期间,可以使用 DBSCAN 识别受影响严重的地区。
活动 6:实现 DBSCAN 并可视化结果
在这个活动中,我们将执行 DBSCAN 并与 k-means 聚类进行比较。为此,我们将使用包含表示不同形状的模拟数据的multishapes数据集。
这些步骤将帮助您完成活动:
-
在
factoextra库中生成并存储multishapes数据集。 -
绘制
multishapes数据集的前两列。 -
在数据集上执行 k-means 聚类并可视化。
-
在数据集上执行 DBSCAN 并可视化数据,以比较与 k-means 聚类的结果。
注意
这个活动的解决方案可以在第 213 页找到。
DBSCAN 聚类的绘图将如下所示:
图 2.9:多形状数据集上 DBCAN 的预期绘图
在这里,所有黑色的点都是异常值,并且没有被归类到任何聚类中,而 DBSCAN 中形成的聚类无法用其他任何类型的聚类方法获得。这些聚类具有多种形状和大小,而 k-means 中所有聚类都是近似球形的。
层次聚类简介
我们将要研究的最后一种聚类类型是层次聚类。层次被定义为“一个将人或事物放置在一系列不同重要性或地位的层级系统。”层次聚类是按顺序合并聚类。这个合并聚类的序列被称为层次。我们可以通过一个名为树状图的层次聚类算法的输出更清楚地看到这一点。
层次聚类有两种类型:
-
聚类
-
分裂
由于两种类型的层次聚类都很相似,因此一起研究它们是有意义的。
聚类是层次聚类的自下而上的方法。在这个方法中,每个数据点最初被假定为单个聚类。从那里开始,我们根据相似度或距离度量开始合并最相似的聚类,直到所有数据点合并成一个单一的聚类。
在分裂聚类中,我们做的是完全相反的事情。它是层次聚类的自上而下方法。在这个方法中,所有数据点最初被假定为在一个单一的聚类中。从那里开始,我们开始将聚类分割成多个聚类,直到每个数据点都是一个单独的聚类。两种聚类类型之间的差异和相似性将在后续章节中变得清晰。但首先,我们应该尝试理解为什么我们需要这种其他类型的聚类以及它所服务的特殊目的,这是其他类型的聚类所不具备的。层次聚类主要用于以下原因:
-
就像 DBSCAN 一样,我们最初不需要选择聚类的数量。
-
层次聚类的最终输出,树状图,可以帮助我们以可视化的方式理解聚类结果,这意味着我们不需要重新运行算法来查看结果中不同数量的簇。
-
与 k-means 不同,层次聚类可以使用任何类型的距离度量。
-
它可以找到复杂形状的簇,与像 k-means 这样的其他聚类算法不同,后者只能找到近似球形的簇。
所有这些先前因素的结合使层次聚类成为无监督学习中的一个重要聚类方法。
相似性度量的类型
如前所述,层次聚类是一种自下而上的层次聚类方法。我们根据相似性度量逐个合并最相似的簇。这个相似性度量可以从几种不同类型中选择:
- 单链:在单链相似度中,我们测量两个簇中两个最相似点之间的距离或相似度:
图 2.10:单链度量的演示
- 完全连接:在这种度量中,我们测量簇中两个最远点之间的距离或相似度:
图 2.11:完全连接度量的演示
- 组平均:在这个度量中,我们测量一个簇中所有成员与第二个簇中任何成员之间的平均距离:
图 2.12:组平均度量的演示
注意
在这些相似性度量中,不测量同一簇成员之间的距离。
- 质心相似度:在这种相似度中,两个簇之间的相似度定义为两个簇质心之间的相似度:
图 2.13:质心相似度的演示
执行层次聚类的方法步骤
通过了解这些相似性度量,我们现在可以理解执行层次聚类算法的方法:
-
将每个点初始化为一个单独的簇。
-
计算每对簇之间的相似性度量。相似性度量可以是之前提到的四种度量中的任何一种。
-
根据第 2 步中选择的相似性度量,合并两个最相似的簇。
-
从第 2 步开始重复这个过程,直到只剩下一个簇为止。
整个过程将产生一个称为树状图的图。这个图记录了每个步骤形成的簇。一个简单的、元素非常少的树状图看起来如下:
图 2.14:一个示例树状图
在前面的树状图中,假设点 A 和点 B 是我们在使用的相似性度量中所有点上最接近的两个点。它们的接近度被用来确定连接线的长度,在点 A 和点 B 的情况下,这个长度是 L1。因此,点 A 和点 B 首先被聚类。之后,在 L2,点 D 和点 E 被聚类,然后,在 L3,点 A、B 和 C 被聚类。此时,我们有两个集群在 L4 处连接形成一个集群。
现在,为了从这个树状图中得到集群,我们进行水平切割。例如,如果我们将在 L4 和 L3 之间进行切割,我们将得到两个集群:
-
集群 1 – A、B 和 C
-
集群 2 – D 和 E
集群将看起来如下:
图 2.15:树状图中表示的集群
同样地,如果我们将在树状图中的 L3 和 L2 之间进行水平切割,我们将得到三个集群:
-
集群 1 – A 和 B
-
集群 2 – C
-
集群 3 – D 和 E
集群将看起来如下:
图 2.16:树状图中集群的表示
因此,为了得到不同数量的集群,我们不需要重新运行整个过程。使用这种方法,我们可以得到数据中可能出现的任何数量的集群,而无需再次执行整个过程。
练习 12:使用不同相似性度量的聚合聚类
在这个练习中,我们将使用不同的相似性度量进行聚合层次聚类,并比较结果:
-
让我们将
iris_flowers数据集的最后三列,即花瓣长度、花瓣宽度和物种,输入到iris_data变量中,如下所示:iris_data<-iris[,3:5] install.packages('cluster') library(cluster) -
在这一步,我们使用
hclust函数来获取层次聚类。在hclust函数中,我们需要输入所有点之间的成对距离,这可以通过dist()函数实现。第二个参数method用于定义层次聚类的相似性度量:h.clus<-hclust(dist(iris_data),method="complete") -
现在,让我们将层次聚类的结果绘制成树状图,如下所示:
plot(h.clus)输出如下:
图 2.17:由完整相似性度量得到的树状图
-
要从前面的树状图中选择多个集群,我们可以使用一个名为
cutree的 R 函数。我们将hclust的结果以及集群数量输入到这个函数中:clusterCut <- cutree(h.clus, 3) table(clusterCut, iris_data$Species)输出表格如下:
![img/C12628_02_18.jpg]
图 2.18:显示集群分布的表格
使用这种聚类方法,setosa 和 virginica 物种被准确分类。
-
使用
single作为相似性度量,如下所示:h.clus<-hclust(dist(iris_data),method = "single") plot(h.clus)输出如下:
图 2.19:基于单相似度指标生成的树状图
注意这个树状图与使用
完全相似度指标创建的树状图的不同之处。 -
将此数据集划分为三个簇:
clusterCut <- cutree(h.clus, 3) table(clusterCut, iris_data$Species)输出如下:
图 2.20:显示簇分布的表格
在这里,我们的聚类方法成功地将一个类别从其他两个类别中分离出来。
-
现在让我们使用
平均相似度指标执行层次聚类:h.clus<-hclust(dist(iris_data),method = "average") plot(h.clus)输出如下:
图 2.21:基于平均相似度指标生成的树状图
-
让我们将前面的树状图再次划分为三个簇,并查看结果:
clusterCut <- cutree(h.clus, 3) table(clusterCut, iris_data$Species)输出如下:
图 2.22:显示簇分布的表格
在这里,使用
平均相似度指标,我们得到了几乎完全正确的分类结果。 -
现在,让我们尝试使用最后一个相似度指标,
质心,创建一个树状图:h.clus<-hclust(dist(iris_data),method = "centroid") plot(h.clus)输出如下:
图 2.23:基于质心相似度指标生成的树状图
-
现在,让我们将前面的树状图划分为三个簇,并查看结果:
clusterCut <- cutree(h.clus, 3) table(clusterCut, iris_data$Species)输出如下:
图 2.24:显示簇分布的表格
虽然基于平均和质心相似度指标的簇的树状图看起来不同,但当我们把树状图切割成三个簇时,每个簇中的元素数量是相同的。
划分聚类
划分聚类与聚合聚类相反。在聚合聚类中,我们以每个点作为其自己的簇开始,而在划分聚类中,我们以整个数据集作为一个簇开始,然后开始将其划分为更多的簇:
图 2.25:聚合聚类和划分聚类的表示
因此,划分聚类过程可以总结如下一个图示:
图 2.26:划分聚类过程的表示
执行划分聚类的步骤
从步骤 1 到步骤 6,划分聚类过程不断将点划分为更小的簇,直到每个点都是一个单独的簇。图 2.26 显示了划分聚类过程的前 6 步,但要正确运行完整过程,我们需要执行与数据集中点数相同数量的步骤。
因此,对于划分聚类,我们将使用 DIANA 算法——DIANA 代表划分分析。为此,我们需要执行以下步骤:
-
从数据集中所有点都在一个单独的簇中开始。
-
根据您喜欢的任何距离度量,从数据集中所有可能的簇中选择两个最不相似的簇。
-
重复步骤 2,直到数据集中的所有点都各自聚类。
我们将使用 R 中的 cluster 库来执行 DIANA 聚类。
练习 13:执行 DIANA 聚类
在这个练习中,我们将执行 DIANA 聚类:
-
将花瓣长度、花瓣宽度和物种名称放入
iris_data变量中:iris_data<-iris[,3:5] -
导入
cluster库:install.packages("cluster") library("cluster") -
将
iris_data数据集和用于度量差异度的度量传递给diana()函数:h.clus<-diana(iris_data, metric="euclidean") -
使用
pltree()函数绘制树状图。要绘制树状图,需要将diana()函数的结果和图表标题传递给pltree()函数:pltree(h.clus, cex = 0.6, main = "Dendrogram of divisive clustering")划分聚类的树状图如下所示:
图 2.27:划分聚类的树状图
-
如果我们将前面的树状图划分为三个簇,我们会看到这种聚类方法也能够独立地识别不同的花卉种类:
clusterCut <- cutree(h.clus, 3) table(clusterCut, iris_data$Species)输出如下:
clusterCut setosa versicolor virginica 1 50 1 0 2 0 49 0 3 0 0 50
在前面的输出中,只有一朵花被错误地分类到另一个类别。这是我们在本书中遇到的所有聚类算法在不知道任何关于花卉信息的情况下对花卉物种进行分类的最佳性能。
活动 7:对种子数据集执行层次聚类分析
在这个活动中,我们将对种子数据集执行层次聚类分析。我们将看到当对三种种子进行分类时,聚类的结果是什么。
注意
此数据集来自 UCI 机器学习仓库。您可以在 archive.ics.uci.edu/ml/machine-learning-databases/00236/ 找到数据集。我们已经下载了文件,清理了它,并将其保存在 github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson02/Activity07/seeds_data.txt。
这些步骤将帮助您完成活动:
-
对数据集执行层次聚类并绘制树状图。
-
在
k=3处进行切割,并通过创建一个包含原始标签的表格来检查聚类的结果。 -
对数据集进行划分聚类并绘制树状图。
-
在
k=3处进行切割,并通过形成一个带有原始标签的表格来检查聚类的结果。注意
本活动的解决方案可以在第 215 页找到。
本活动的输出将是一个表格,显示聚类结果在分类三种类型种子方面的表现。它将如下所示:
图 2.28:预期表格,用于分类三种类型的种子
摘要
恭喜您完成了关于聚类技术的第二章!至此,我们已经涵盖了所有主要的聚类技术,包括 k-modes、DBSCAN 以及两种类型的层次聚类,我们还探讨了它们之间的联系。我们可以将这些技术应用到我们可能遇到的任何类型的数据集中。这些新方法有时在第一章中使用的数据集上也产生了更好的结果。在下一章中,我们将研究概率分布及其在探索性数据分析中的应用。
第四章:第三章
概率分布
学习目标
到本章结束时,你将能够:
-
在 R 中生成不同的分布
-
在 R 中估计新数据集的概率分布函数
-
比较同一分布的两个不同样本或不同分布的接近程度
在本章中,我们将学习如何使用概率分布作为无监督学习的一种形式。
简介
在本章中,我们将研究无监督学习的另一个方面,称为概率分布。概率分布是许多数学教科书和课程中涵盖的经典统计学的一部分。随着大数据的出现,我们已经开始在探索性数据分析和其他建模应用中使用概率分布。因此,在本章中,我们将研究如何在无监督学习中使用概率分布。
概率分布的基本术语
统计学中有两种方法家族:参数方法和非参数方法。非参数方法旨在处理可能具有任何形状的数据。相比之下,参数方法对数据可能采取的特定形状做出假设。这些假设通常编码为参数。以下是你应该注意的两个主要参数:
-
均值:这是分布中所有值的平均值。
-
标准差:这是衡量分布均值周围值分布的度量。
统计学中的大多数参数方法以某种方式依赖于这两个参数。在本章中我们将研究的参数分布是这些:
-
均匀分布
-
正态分布
-
对数正态分布。
-
二项分布
-
泊松分布
-
帕累托分布
均匀分布
在均匀分布中,一个区间内的所有值,比如说 [a,b],都是等可能的。数学上,它被定义为如下:
图 3.1:均匀分布的数学公式
它可以如下绘制在图上:
图 3.2:均匀分布图
均匀分布是参数分布中最简单的。现实世界中许多过程遵循均匀抽样:
-
如果在一个非常大的区域内下雨,雨滴落下的分布可以假设在较小区域内是均匀的。
-
社会保障号码的最后一位应该对任何人群子集,例如加利福尼亚州所有初创公司首席执行官,都遵循均匀分布。
-
均匀随机抽样对于生成实验数据和进行控制试验非常重要。
练习 14:在 R 中生成和绘制均匀样本
在这个练习中,我们将生成均匀样本并绘制它们。为此,执行以下步骤:
-
使用内置的
runifR 函数生成均匀样本。首先,输入你想要生成的样本数量。这里我们生成 1,000 个样本。然后,输入min值和max值:uni<-runif(1000, min=0, max=100) -
在将生成的随机数存储在
uni变量之后,我们将绘制它们的值与它们的索引的关系:plot(uni)输出如下:
图 3.3:均匀分布
如您所见,点几乎均匀地散布在各个地方。我们也可以绘制这个直方图,以获得分布的更清晰图像。
-
我们将使用 R 的
hist()函数绘制生成的样本的直方图:hist(uni)输出如下:
图 3.4:分布的直方图
如您所见,它并不完全平坦,正如我们之前所设想的那样。它或多或少是均匀的,但并不完全均匀,因为它是由随机生成的。每次我们生成一个新的样本,它都会类似于这个直方图,而且很可能会不完全平坦,因为所有随机抽样方法都会伴随噪声。
正态分布
正态分布是一种受两个参数控制的参数分布:均值和均值的方差。它关于均值对称,大多数值都接近均值。其曲线也被称为钟形曲线:
图 3.5:正态分布数据的典型近似表示
正态分布由以下方程定义:
图 3.6:正态分布的方程
这里,是均值,
是标准差。
正态分布是现实世界中非常常见的一种分布。以下是一些正态分布的例子:
-
篮球运动员的身高大约呈正态分布。
-
一个班级学生的分数可能有一个非常接近正态分布的分布。
-
尼罗河的年流量是正态分布的。
现在,我们将使用 R 生成并绘制一个正态分布。
练习 15:在 R 中生成和绘制正态分布
在这个练习中,我们将生成一个正态分布来模拟 1,000 名学生的(满分 100 分)考试成绩,并将它们绘制出来。为此,请执行以下步骤:
-
通过在 R 的
rnorm函数中输入样本数量、均值和标准差来生成正态分布:nor<-rnorm(1000,mean=50, sd= 15) -
将生成的数字与它们的索引进行对比:
plot(nor)输出如下:
图 3.7:正态分布
如您所见,大多数值都围绕着 50 的平均值,当我们远离 50 时,点的数量开始减少。这个分布将在下一步通过直方图来阐明。
-
使用
hist()函数绘制正态分布的直方图:hist(nor)输出如下:
图 3.8:正态分布直方图
您可以看到这个形状非常类似于正态分布的钟形曲线。
偏度和峰度
正如我们所见,您在实践中所看到的许多分布都被假定为正态分布。但并非每个分布都是正态分布。为了衡量一个分布偏离标准正态分布的程度,我们使用两个参数:
-
偏度
-
峰度
分布的偏度是衡量其相对于标准正态分布的不对称程度的指标。在具有高偏度的数据集中,均值和众数将彼此不同。偏度有两种类型:正偏度和负偏度:
图 3.9:负偏度和正偏度
负偏度是指平均值左侧存在长尾值,而正偏度是指平均值右侧存在长尾值。偏度也可以用以下公式表示:
图 3.10:偏度的数学公式
这里,是
的期望值或平均值,其中
和
分别是分布的均值和标准差。
峰度是衡量分布尾部的胖瘦程度与正态分布相比的指标。与偏度不同,峰度不会在分布中引入不对称性。这里提供了一个说明:
图 3.11:峰度演示
峰度也可以用以下公式表示:
图 3.12:峰度的数学公式
这里,是
的期望值或平均值,其中
和
分别是分布的均值和标准差。标准正态分布的偏度为 0,峰度测量值为 3。因为正态分布非常常见,所以我们有时只测量超峰度,如下所示:
Kexcess = K - 3
因此,正态分布的超峰度为 0。
对数正态分布
对数正态分布是值的对数呈正态分布的分布。如果我们在对数尺度上展示对数正态分布,它与正态分布完全相同,但如果我们在标准分布尺度上展示它,它将具有非常高的正偏斜:
图 3.13:对数正态分布
为了说明对数正态分布是按对数尺度分布的正态分布,我们将进行一个练习。
对数正态分布在金融风险管理领域用于模拟股价。由于假设增长因子是正态分布的,股价可以按对数正态分布建模。这种分布也用于与期权定价相关的计算,包括风险价值(VaR)。
练习 16:从正态分布生成对数正态分布
在这个练习中,我们将从正态分布生成对数正态分布。为此,执行以下步骤:
-
生成正态分布并将值存储在一个变量中:
nor<-rnorm(1000,mean=5, sd= 1) -
绘制具有 100 个不同区间的正态分布直方图:
hist(nor,breaks = 100)输出如下:
图 3.14:均值为 5,标准差为 1 的正态分布
-
创建一个将存储 1,000 个值的对数正态分布向量:
lnor <- vector("list", 1000) -
将指数值输入到
lnor向量中。指数函数是对数函数的逆函数:for (x in 1:1000){ lnor[x]=exp(nor[x]) } -
绘制
lnor的直方图:hist(as.integer(lnor), breaks = 200)输出如下:
图 3.15:对数正态分布
注意前一个图看起来像对数正态分布图,并且这个图是从正态分布生成的。如果我们对前一个图中的值取对数后绘制新的图形,那么它将再次是正态分布。
二项分布
二项分布是一种离散分布,与连续的正态分布或均匀分布不同。二项分布用于模拟每个事件有两种可能性的多个事件同时发生的概率。一个二项分布可以应用的例子是在同时掷三个硬币时,找出所有三个硬币都出现正面的概率。
二项分布的均值和方差分别是 np 和 np(1-p),其中 p 是成功的概率,n 是试验次数。当 p=0.5 时,二项分布是对称的。当 p 小于 0.5 时,它更偏向右侧,当 p 大于 0.5 时,它更偏向左侧。
二项分布的公式如下:
P(x) = n!/((n-x)!x!)*(p^x)*((1-p)^x)
在这里,n 是总试验次数,x 是焦点试验次数,p 是成功的概率。
练习 17:生成二项分布
在这个练习中,我们将生成一个二项分布来模拟抛掷硬币 50 次时出现正面的次数。为此,执行以下步骤:
-
要生成二项分布,我们首先需要一个由 50 个数字组成的序列作为索引,这将作为我们想要模拟的成功次数。这将是前一小节公式中的 x:
s <- seq(0,50,by = 1) -
现在我们将把
s作为参数传递给 R 中的dbinom()函数,该函数将计算s变量中每个值的概率并将它们存储在新的probs变量中。首先,在函数中,我们输入将编码成功次数范围的序列。然后,我们输入序列的长度,然后输入成功的概率:probs <- dbinom(s,50,0.5) -
在最后一步,我们将
s和probs一起绘制:plot(s,probs)输出如下:
图 3.16:二项分布
在这里,x 轴显示我们感兴趣的正面数量,y 轴显示在 50 次试验中恰好得到该数量正面的概率。当我们抛掷硬币 50 次时,最可能的结果是我们将得到 25 个正面和 25 个反面,但得到所有 50 个正面或反面的概率非常低。这在前面的图表中解释得很好。
泊松分布
泊松分布是另一种离散分布,用于在特定时间段内根据该事件在该时间段内平均发生次数来模拟事件的发生。
它由以下方程式表示:
图 3.17:泊松分布公式
在这里,lambda 是给定时间段的平均发生次数,e是欧拉常数,x是你想要找到概率的事件数量。根据到目前为止观察到的每分钟到达事件的新人数,泊松分布可以用来计算在下一分钟到达该事件的人数概率。
泊松分布图看起来是这样的:
图 3.18:泊松分布图
在这里,我们可以看到x的不同值的概率与 lambda 有关。
帕累托分布
帕累托分布是一种基于指数的概率分布。这种分布是为了模拟在许多现实世界情况中观察到的 80:20 规则而发明的。遵循 80:20 规则的一些有趣的情况如下列所示:
-
大约 80%的世界财富掌握在 20%的人手中。
-
在商业管理中,发现对于大多数公司来说,80%的收入是由 20%的客户产生的。
-
据说 20%的司机造成了 80%的所有事故。
有许多其他现实世界的观察可以通过帕累托分布来建模。帕累托分布的数学公式如下:
图 3.19:帕累托分布的数学公式
核密度估计简介
到目前为止,我们本章已经研究了参数分布,但在现实生活中,所有分布要么是参数分布的近似,要么根本不类似于任何参数分布。在这种情况下,我们使用一种称为核密度估计(KDE)的技术来估计它们的概率分布。
核密度估计(KDE)使用核来估计具有给定有限点的分布或随机变量的概率密度函数。在继续本章内容后,这对你来说会更加清晰。
核密度估计算法
尽管名字听起来很复杂,但核密度估计(KDE)是一个非常简单的两步过程:
-
选择核
-
将核放在数据点上并取核的总和
核是一个非负对称函数,用于建模分布。例如,在核密度估计(KDE)中,正态分布函数是最常用的核函数。核函数可以是不同类型的。它们与我们本章 earlier 研究的分布有很大关系。这里总结了其中一些类型:
- 在均匀核中,一个范围内的所有值都被赋予相同的权重。这表示如下:
图 3.20:均匀核函数的表示
- 在三角形核中,随着值向范围中间移动,权重线性增加。这表示如下:
图 3.21:三角形核函数的表示
- 在高斯核中,权重按正态分布。这表示如下:
图 3.22:高斯核函数的表示
除了核之外,在第一步中,我们还需要选择另一个参数,称为核的带宽。带宽是影响核平滑度的参数。选择正确的带宽非常重要,甚至比选择正确的核更重要。这里我们将看一个例子。
练习 18:可视化和理解核密度估计
假设我们有一个包含五个不同点(1,2,3,4 和 5)的分布。让我们使用这个例子来可视化和理解核密度估计(KDE):
-
将五个点的向量存储在一个变量中:
x<- c(1,2,3,4,5) -
绘制点:
y<-c(0,0,0,0,0) plot(x,y)输出如下:
图 3.23:五个点的图
-
如果您还没有安装
kdensity包,请安装它,并导入它:install.packages("kdensity") library('kdensity') -
使用
kdensity()函数计算核密度。输入分布x和带宽参数为.35。默认情况下核是高斯核:dist <- kdensity(x, bw=.35) -
按以下方式绘制核密度估计图(KDE):
plot(dist)输出如下:
图 3.24:高斯核的绘制图
这是核密度估计图的最终输出。在下一步中,假设每个点(1, 2, 3, 4, 和 5)都有一个以高斯核为中心的点,并将它们相加得到这个图。以下图将使它更清晰:
注意
此图用于说明目的,而不是在 R 中生成。
图 3.25:每个点上的高斯核绘制图
如前图所示,每个点上都绘制了一个高斯核,然后所有核相加得到最终的曲线。
现在,如果我们把带宽改为 0.5 而不是 0.35 会怎样呢?
-
在
kdensity()函数中将带宽改为 0.5,并再次绘制kdensity图:dist <- kdensity(x, bw=.5) plot(dist)输出如下:
图 3.26:带宽为 0.5 的高斯核绘制图
你可以看到核现在要平滑得多。以下使用了以下核:
图 3.27:每个点上的高斯核绘制图
注意
此图用于说明目的,而不是在 R 中生成。
这次,核的宽度要宽得多。
如果我们被给予足够多的点来进行估计,核的选择不会像带宽参数的选择那样显著改变最终核密度估计图的形状。因此,选择理想的带宽参数是一个重要的步骤。有许多技术被用来选择理想的带宽参数。研究这些技术超出了本书的范围,但 R 库可以自动选择理想的参数。我们将在下一个练习中学习这一点。
练习 19:研究改变核对分布的影响
在这个练习中,我们将生成两个具有不同标准差和均值的正态分布,并将它们合并以生成它们的合并核密度估计图:
-
生成两个不同的正态分布并将它们存储在两个变量中:
y1 <- rnorm(100,mean = 0, sd = 1) y2<-rnorm(100, mean = 3, sd=.2) -
将生成的分布合并并绘制:
y3<-c(y1,y2) plot(y3)输出如下:
图 3.28:合并分布的绘制图
你可以看到有两个不同的分布,它们有不同的均值和分散度(标准差)。
-
绘制
y3的直方图以供参考:hist(y3)输出如下:
图 3.29:结果分布的直方图
-
使用
gaussian核生成并绘制y3的核密度估计图:dist<-kdensity(y3,kernel = "gaussian") plot(dist)输出如下:
图 3.30:高斯核密度图
在前面的图中,我们使用了高斯核,带宽是由函数自动选择的。在这个分布中,我们有 200 个点,这应该足以生成一个稳健的 KDE 图,这样改变核类型不会在最终的 KDE 图中产生显著差异。在下一步,让我们尝试改变核并查看最终的图。
-
使用
triangular核生成并绘制 KDE:dist<-kdensity(y3,kernel = "triangular") plot(dist)输出如下:
图 3.31:具有三角形核的 KDE
使用不同核的两种图看起来几乎相同。因此,带宽的选择比核的选择更重要。在这个练习中,带宽是由 R 的kdensity库自动选择的。
活动 8:寻找与 Iris 数据集变量分布最接近的标准分布
在这个活动中,我们将找到与 Iris 数据集的 setosa 物种变量分布最接近的标准分布。以下步骤将帮助你完成活动:
-
加载 Iris 数据集。
-
选择仅对应 setosa 物种的行。
-
绘制由
kdensity函数生成的花瓣长度和花瓣宽度的分布图。注意
这个活动的解决方案可以在第 218 页找到。
这个活动的最终结果将是花瓣宽度 KDE 图,如下所示:
图 3.32:预期花瓣宽度 KDE 图
Kolmogorov-Smirnov 测试简介
现在我们已经学会了如何生成不接近标准分布的数据集的概率密度函数,我们将学习如何执行一些测试来区分这些非标准分布。
有时候,我们被给出多个观测数据样本,我们想知道这些样本是否属于同一分布。在标准分布的情况下,我们有多个测试,例如 Student's t-test 和 z-test,来确定这一点。对于非标准分布,或者当我们不知道分布类型时,我们使用 Kolmogorov-Smirnov 测试。为了理解 Kolmogorov-Smirnov 测试,你首先需要理解一些术语:
-
累积分布函数(CDF):这是一个函数,其值给出了随机变量小于或等于函数参数的概率。
-
零假设:在假设检验中,零假设意味着观测样本之间没有显著差异。在假设检验中,我们的目标是证伪零假设。
Kolmogorov-Smirnov 测试算法
在 Kolmogorov-Smirnov 测试中,我们执行以下步骤:
-
为两个函数生成 CDF。
-
指定一个分布作为父分布。
-
在同一张图上绘制两个函数的 CDF。
-
找到两个 CDF 中点之间的最大垂直差异。
-
从上一步测量的距离计算测试统计量。
-
在 Kolmogorov-Smirnov 表中找到临界值。
在 R 中,这些步骤是自动化的,因此我们不需要逐个执行它们。
练习 20:对两个样本执行 Kolmogorov-Smirnov 测试
要对两个样本执行 Kolmogorov-Smirnov 测试,请执行以下步骤:
-
生成两个独立的分布进行比较:
x_norm<-rnorm(100, mean = 100, sd=5) y_unif<-runif(100,min=75,max=125) -
按如下方式绘制
x_norm的 CDF:plot(ecdf(x_norm))输出如下:
要绘制
ecdf(y_unif),请执行以下操作:plot(ecdf(y_unif),add=TRUE)输出如下:
如您所见,函数的累积分布函数(CDF)看起来完全不同,因此 Kolmogorov-Smirnov 测试将返回非常小的 p 值。
-
在 R 中使用
ks.test()函数运行 Kolmogorov-Smirnov 测试:ks.test(x_norm,y_unif)输出如下:
Two-sample Kolmogorov-Smirnov test data: x_norm and y_unif D = 0.29, p-value = 0.0004453 alternative hypothesis: two-sided注意
这个练习依赖于随机生成数据。因此,当您运行此代码时,一些数字可能会有所不同。在假设检验中,有两个假设:零假设和检验假设。假设检验的目的是确定我们是否有足够的证据来拒绝零假设。在这种情况下,零假设是两个样本是由同一分布生成的,检验假设是两个样本不是由同一分布生成的。p 值表示在零假设为真的情况下,观察到与观察到的差异一样极端或更极端的概率。当 p 值非常接近零时,我们将其视为零假设为假的证据,反之亦然。
如您所见,
ks.test()返回两个值,D和 p 值。D值是两个分布的 CDF 中两点之间的绝对最大距离。它越接近零,两个样本属于同一分布的可能性就越大。p 值的解释与任何其他情况相同。在我们这个例子中,
D的值为0.29,p 值非常低,接近零。因此,我们拒绝两个样本属于同一分布的零假设。接下来,我们将生成一个新的正态分布,并观察它对 p 值和D的影响。 -
生成一个与
xnorm具有相同mean和sd的新正态分布:x_norm2<-rnorm(100,mean=100,sd=5) -
绘制
x_norm和x_norm2的合并 CDF:plot(ecdf(x_norm)) plot(ecdf(x_norm2),add=TRUE)输出如下:
图 3.35:合并 CDF 的绘图
-
在
x_norm和x_norm2上运行ks.test():ks.test(x_norm,x_norm2)输出如下:
Two-sample Kolmogorov-Smirnov test data: x_norm and x_norm2 D = 0.15, p-value = 0.2106 alternative hypothesis: two-sided如您所见,这次 p 值要高得多,而
D要低得多。因此,根据 p 值,我们不太有理由拒绝两个样本属于同一分布的零假设。
活动九:使用正态分布计算累积分布函数(CDF)和执行 Kolmogorov-Smirnov 测试
在随机生成的分布的帮助下,计算样本的花瓣长度和宽度最接近的标准分布:
-
将 Iris 数据集加载到一个变量中。
-
仅保留具有 setosa 物种行的记录。
-
计算花瓣长度的平均值和标准差。
-
使用花瓣长度列的平均值和标准差生成一个新的正态分布。
-
绘制两个函数的累积分布函数(CDF)。
-
生成 Kolmogorov-Smirnov 测试的结果并检查分布是否为正态分布。
-
对花瓣宽度列重复步骤 3、4、5 和 6。
注意
本活动的解决方案可以在第 219 页找到。
本活动的最终结果如下:
Two-sample Kolmogorov-Smirnov test
data: xnorm and df$Sepal.Width
D = 0.12, p-value = 0.7232
alternative hypothesis: two-sided
摘要
恭喜您完成了本书的第三章!在本章中,我们学习了标准概率分布的类型,以及如何在 R 中生成它们。我们还学习了如何使用核密度估计(KDE)找到未知分布的 PDF 和 CDF。在最后一节中,我们学习了如何在 R 中比较两个样本并确定它们是否属于同一分布。在接下来的章节中,我们将学习其他类型的无监督学习技术,这些技术不仅有助于探索性数据分析,还能为我们提供对数据的其他有用见解。
第五章:第四章
降维
学习目标
到本章结束时,你将能够:
-
应用不同的降维技术
-
使用 Apriori 算法执行市场篮子分析
-
对数据集执行主成分分析
在本章中,我们将探讨不同的降维技术。
简介
本章介绍了无监督学习技术,这些技术实现了所谓的降维。首先,我们将讨论什么是维度,为什么我们想要避免拥有太多的维度,以及降维的基本思想。然后,本章将详细介绍两种降维技术:市场篮子分析和主成分分析(PCA)。市场篮子分析是一种在数据集中生成关联规则的技术。本章将包含一个详细的 R 代码示例,展示如何实现这一目标。PCA 是一种非常常见的降维技术,源自理论线性代数。本章还将详细展示如何使用 R 实现 PCA。
降维的概念
数据集的维度不过是描述其中观测所需的不同数字的集合。例如,考虑以 Pac-Man 命名的游戏中的 Pac-Man 位置。Pac-Man 是一款在 20 世纪美国流行的游戏。这是一款极其简单的游戏:Pac-Man 是一个屏幕上的小圆形生物,喜欢吃小点和水果。他生活在一个迷宫中,只能用两组方向移动:上/下和左/右。有一些怪物试图追赶 Pac-Man 并杀死他。你可以在下面的插图看到 Pac-Man 游戏的样子,以及他必须在其中移动的世界:
图 4.1:Pac-Man 风格游戏的插图
如你所见,Pac-Man 的位置可以用两个数字完全描述:他距离屏幕左侧有多远,以及他距离屏幕顶部的距离有多远。如果我们知道这两个数值测量值,那么屏幕上就只有一个唯一的位置他可能在那里。所以,如果我们想要收集关于 Pac-Man 随时间位置的数据,我们就能收集一个包含这两个数字的二维数据集,这些数字被反复测量。我们会完全确信,每个由两个数字组成的观测值,完全描述了在观测时刻关于 Pac-Man 位置所能知道的一切。
不只是位置数据或几何数据可以被描述为二维。任何包含两种不同测量的数据集都可以被描述为二维。例如,如果我们测量了个人身高和体重,我们可以创建一个包含他们的身高和体重测量的二维数据集。如果我们记录了身高、体重和鞋码,那么我们就会有一个三维数据集。数据集中可以包含的维度数量没有限制。
降维是找到一个低维数据集来近似高维数据集的过程。考虑一个与 Pac-Man 相关的例子。想象我们有一个描述 Pac-Man 位置的三个维度的数据集。假设这个数据集的维度是(1)Pac-Man 距离屏幕左侧有多远,(2)Pac-Man 距离屏幕顶部有多远,以及(3)Pac-Man 距离追逐他的蓝色怪物有多远。这是一个三维数据集;然而,我们只需要前两个维度的信息就可以完全了解 Pac-Man 的位置。我们进行有效降维的最简单方法就是丢弃第三个维度,因为它不会比只有前两个维度帮助我们更好地定位 Pac-Man。因此,由数据集的前两个维度组成的二维数据集将是我们最初开始的三维数据集的良好近似。
在大多数实际场景中,降维并不像丢弃维度那样简单。通常,我们将尝试使用所有维度的数据来创建一个全新的数据集,其维度与原始数据集的维度具有不同的含义。本章剩余的练习将说明这个过程。
在接下来的练习中,我们将查看一个包含多个维度的数据集。我们将创建图表来说明降维以及它如何帮助我们。
练习 21:检查包含不同葡萄酒化学属性的数据集
前提条件:
注意
此数据集来自 UCI 机器学习仓库。您可以在archive.ics.uci.edu/ml/datasets/Wine找到数据集。我们已经下载了文件,并将其保存于github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson04/Exercise21/wine.csv。
下载此数据,并将其存储在名为wine.csv的文件中。
注意
对于所有需要导入外部 csv 或图像的练习和活动,请转到 R Studio-> 会话-> 设置工作目录-> 到源文件位置。你可以在控制台中看到路径已被自动设置。
这份数据包含了关于 178 个不同葡萄酒样本 13 个不同化学测量值的信息。总共有 13 维数据集。如果我们考虑只包含 13 个属性中的 2 个属性的数据子集,我们将得到一个 2 维数据集,这与我们的假设 Pac-Man 数据相似。对于 2 维数据,我们总是可以在 2 维散点图上绘制它。
在这个数据集中,第一列记录了葡萄酒的类别,换句话说,就是它的类型。其他每一列都记录了与葡萄酒化学成分相关的测量值。机器学习的一个美妙之处在于,即使我们对葡萄酒的化学知识一无所知,我们也可以使用纯粹的数据分析工具来发现模式并得出可能连化学专家都未曾注意到的结论。
完成步骤如下:
-
打开 R 控制台,确保你已将数据文件(
wine.csv)保存在 R 可以访问的位置。你可以使用setwd()命令来确保你的文件是可访问的。例如,如果你的wine.csv文件位于C:/Users/me/datasets文件夹中,那么你可以在 R 控制台中运行setwd('C:/Users/me/datasets')命令。然后,你将能够在 R 中打开葡萄酒数据文件,如下所示:wine<-read.csv('wine.csv') -
考虑以下由
flavanoids和total phenols属性创建的二维数据散点图:plot(wine$flavanoid,wine$phenol)输出如下:
图 4.2:黄酮和酚类二维数据的散点图
-
在绘制数据后,我们观察到黄酮和酚类测量值之间似乎存在强烈的关联。我们可以在图上画一条线来表示这种相关性。现在,你不必担心我们如何在下面的命令中找到标记为
a和b的系数:plot(wine$flavanoid,wine$phenol) abline(a=1.1954,b=.54171,col='red',lwd=5)
图 4.3:表示黄酮和酚类之间相关性的散点图,其中有一条线表示这种相关性
如你所见,红线非常接近我们数据的几何形状。数据中的大多数点都非常接近红线。如果我们想要简洁地描述这些点,我们可以说它们最接近红线的哪个点。这不会是数据的完美描述,因为即使它们的黄酮和酚类水平不同,一些点也会映射到红线上相同的点。然而,仅使用红线来描述这些数据是对实际数据的合理近似。
如果我们用每个观察结果最接近的红线上的点来描述它,那么我们所完成的就是降维。我们从一个需要两个测量值来描述每个观察结果的数据库开始,并找到了只用一个点来描述每个观察结果的方法。这是所有降维策略的基本思想,本章包含了一些实现它的实用策略。
降维的重要性
为什么降维是我们感兴趣做的事情?以下是一些原因:
-
一个原因可能是为了压缩数据。如果一个数据集特别大,而且如果你的笔记本电脑上运行的 R 实例在对其进行简单计算时花费时间过长,那么降低数据的维度可能是有用的,这样它就可以更容易地适应你的计算机内存。
-
降维更有趣的原因是,它为我们提供了对数据潜在结构和不同属性之间相互关系的洞察。在前面的练习中,即使我们没有在化学方面的先进培训,我们也可以使用我们从简单的降维练习中学到的知识来更好地理解葡萄酒化学。
注意
如果我们阅读更多关于酚类和黄烷醇(例如,在这个网站上:
www.researchgate.net/post/What_is_the_relation_between_total_Phenol_total_Flavonoids),我们可以了解到酚类和黄烷醇都具备抗氧化活性。因此,图表上的红线可能代表了特定葡萄酒的抗氧化活性水平,而黄酮醇和酚类的测量只是捕捉了这一事物的噪声测量。因此,降维使我们能够对葡萄酒的化学成分提出假设,即使没有高级领域的知识。
市场篮子分析
市场篮子分析是一种方法,它允许我们将高维数据降低到简单且易于管理的程度,同时不会在过程中丢失太多信息。在市场篮子分析中,我们的目标是生成控制数据的规则。
市场篮子分析也称为亲和分析。它是以一家杂货店试图对其顾客的交易进行分析的例子命名的——分析每个顾客放入其篮子的产品。任何给定时间,大型杂货店可能有大约 5,000 种商品出售。他们每天可能有数千名顾客。对于每位顾客,杂货店可以记录这些顾客的交易记录。一种方法就是使用二进制编码,如下面的例子所示:
客户 1 在第一天交易:
花生酱:否
果冻:是
面包:否
牛奶:否
…
客户 2 在第一天交易:
花生酱:是
果冻:是
面包:否
牛奶:否
…
这些交易可以存储在一个有 5,000 列的表中——每列代表商店中出售的每个项目——以及每行代表每条记录的交易。而不是为每个项目存储“是”和“否”的值,它们可以在一个看起来像以下表格的表中存储 1s 和 0s,其中 1 表示“是”,0 表示“否”:
图 4.4:展示客户交易的表格
上述表格只显示了四列和五行,但在实践中,表格会大得多。
市场篮子分析的最简单用例是回答一个简单的问题:通常一起购买哪些商品?杂货店老板可能纯粹出于好奇对此感兴趣。但事实上,有一些令人信服的商业理由使他们想要了解客户最常见的篮子。
到目前为止,这个问题似乎相当简单。我们的二进制数据是最简单的,只由 0s 和 1s 组成。我们的问题仅仅是找到哪些商品倾向于一起购买。复杂性不在于这些简单想法,而在于它们的实际实施。
考虑寻找倾向于一起购买的商品的暴力方法。如果我们考虑每个可能的项目篮子,即前述数据中 0s 和 1s 的每个可能组合,我们发现存在 2⁵⁰⁰⁰ 个可能的篮子。这比已知宇宙中的粒子数还要多,在合理的时间内检查每个可能的篮子或存储关于每个可能的篮子的发现都是计算上不可行的。
如果我们不能检查每个可能的篮子,我们如何才能找到任何信心地认为我们进行了全面检查的与任何篮子一起购买的篮子?答案是应用算法解决方案。Apriori算法是在时间和空间限制下进行彻底市场篮子分析的最流行方法。它是由 Agrawal 和 Srikant 发明的,他们在 1994 年发表了关于它的论文。它按顺序通过不断增加的市场篮子大小进行。
Apriori 算法由几个步骤组成。在前几个步骤中,我们将遍历我们的数据集以找到最常见的篮子。在我们的第一次遍历中,我们将找到包含恰好一个项目的最常见的篮子。在我们的第二次遍历中,我们将找到包含恰好两个项目的最常见的篮子。我们将继续进行这些遍历,直到我们找到我们感兴趣的每个尺寸的最常见篮子。在杂货店的例子中,可能最常见的两个项目篮子是“花生酱,果酱”,而最常见的三个项目篮子是“花生酱,果酱,面包”。
在找到最常见的篮子后,我们将为这些篮子生成关联规则。这些规则将表达最常见的篮子中项目之间的关系。例如,一个杂货店的关联规则可能如下所示:“如果花生酱和果酱都在篮子里,那么面包很可能也在篮子里。”这类规则使我们能够找到不同单个项目之间的关联,这可能对我们有用。例如,在知道花生酱和果酱经常与面包一起出现后,杂货店老板可能会对重新排列这些商品的陈列感兴趣,以便它们在商店中更靠近,让购物者更容易且不费力地将它们放入篮子中。
注意
规则“如果花生酱和果酱[在篮子里]存在,那么面包很可能[在那个篮子里]存在”是一个简单的关联规则。关联规则有时会画一个箭头从 X 指向 Y,表示 X“意味着”Y,尽管关联规则不一定具有因果关系。
许多美国人从小吃着花生酱和果酱三明治长大,因此对他们来说,花生酱、果酱和面包可能很可能会一起购买。市场篮子分析可能会生成一些看似明显的关联规则,例如这些。然而,在实践中,市场篮子分析可能会生成一些令人惊讶和意外的关联规则。这是另一个例子,即使不是购物或零售方面的专家,我们也可以使用机器学习来发现即使是专家也会感到惊讶的模式和见解。
在下一个练习中,我们将应用市场篮子分析到人口普查调查数据中。数据集的数据如下所示:
图 4.5:数据集截图
注意
此数据集来自 UCI 机器学习仓库。您可以在archive.ics.uci.edu/ml/datasets/Adult找到数据集。我们已经下载了文件并将其保存在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson04/Exercise22-Exercise25/。
这份数据与我们之前描述的假设购物篮数据不同,因为它的列不是 0-1 的二进制编码,而是可以接受多个值。由于 Apriori 算法是为 0-1 数据设计的,我们将对数据进行重新编码。在这里,重新编码意味着我们将创建新的变量,这些变量比原始变量更简单、更容易处理,但仍然传达相同的信息。我们将在这里执行的重编码将使数据由 0-1 编码组成。我们在这里所做的事情的另一个术语是创建虚拟变量。虚拟变量是一个只取 0 和 1 值的变量。对于数据集中的每一列,我们可以参考archive.ics.uci.edu/ml/datasets/Adult上的数据,以找到有关该列的信息,然后使用这些信息进行我们的重新编码。我们可以对所有的变量执行类似的转换。
对于像就业状态这样的分类变量,我们为每个可能的响应创建新的 0-1 变量。对于像年龄这样的有序变量,我们创建两个新的变量,表示值是高还是低。
我们将得出关于哪些调查答案倾向于以相同方式回答的结论。除了购物数据之外,市场篮子分析可以用于各种数据集。无论使用什么数据集,市场篮子分析都会生成关联规则,并告诉我们哪些数据属性倾向于具有相同的值。
练习 22:为 Apriori 算法准备数据
注意
练习 22-25 应一起执行。
在这个练习中,我们将使用在github.com/TrainingByPackt/Applied-Unsupervised-Learning-with-R/tree/master/Lesson04/Exercise22-Exercise25/census.csv上免费提供的数据库。这是调查数据。要使用此数据,您应首先将其下载到您的计算机上 - 保存为名为census.csv的文件。您不需要加载任何特殊包来运行此数据或完成任何先决条件:
-
使用 R 中的
setwd()函数读取数据。在设置工作目录后,你可以按以下方式将其读入 R:filepath='census.csv' mkt<-read.csv(filepath,stringsAsFactors=FALSE,header=FALSE,sep=',') -
检查数据:
head(mkt)图 4.6:数据截图
你会注意到,R 已经自动为数据分配了列名,因为原始数据文件没有包含列名。默认情况下,R 从
V开始分配编号列名,因为每一列都可以被视为一个向量。 -
创建虚拟变量。
我们可以从数据网站上看到第一个变量,R 将其称为
V1,是年龄的测量值。对于这个变量,我们根据其值是否高于或低于中位数年龄值将其重新编码为 0-1 二进制变量。我们可以用"median(mkt$V1)"来计算中位数年龄值:mkt$old<-1*(mkt$V1>median(mkt$V1)) mkt$young<-1*(mkt$V1<=median(mkt$V1)) -
同样,我们可以在网站上看到第二列,R 将其标记为
V2,指的是就业状况。对于就业,我们可以创建几个新变量,每个就业类别一个:mkt$government_employee<-1*(mkt$V2 %in% c(" State-gov"," Local-gov"," Federal-gov")) mkt$self_employed<-1*(mkt$V2 %in% c(" Self-emp-not-inc"," Self-emp-inc")) mkt$never_worked<-1*(mkt$V2 %in% c(" Never-worked")) mkt$private_employment<-1*(mkt$V2 %in% c(" Private")) mkt$other_employment<-1*(mkt$V2 %in% c(" ?"," Without-pay" )) -
在这里,我们为受访者的教育水平编码 0-1 变量:
mkt$high_school_incomplete<-1*(mkt$V4 %in% c(" 1st-4th"," Preschool"," 5th-6th"," 7th-8th"," 9th"," 10th"," 11th"," 12th")) mkt$high_school_complete<-1*(mkt$V4 %in% c(" HS-grad"," Some-college"," Assoc-acdm"," Assoc-voc")) mkt$bachelors<-1*(mkt$V4 %in% c(" Bachelors")) mkt$post_bachelors<-1*(mkt$V4 %in% c(" Masters"," Prof-school"," Doctorate" ))我们使用
V4列来编码教育水平,因为标记为V3的列对我们来说没有用。我们不会使用V5列,因为它包含的是以不同方式表达相同数据。 -
在这里,我们为一个人的婚姻状况编码 0-1 变量:
mkt$married<-1*(mkt$V6 %in% c(" Married-civ-spouse"," Married-AF-spouse"," Married-spouse-absent")) mkt$never_married<-1*(mkt$V6 %in% c(" Never-married")) mkt$divorced_separated<-1*(mkt$V6 %in% c(" Divorced"," Separated")) mkt$widowed<-1*(mkt$V6 %in% c( " Widowed")) -
在这里,我们为受访者的职业编码 0-1 变量:
mkt$clerical<-1*(mkt$V7 %in% c(" Adm-clerical")) mkt$managerial<-1*(mkt$V7 %in% c(" Exec-managerial")) mkt$moving<-1*(mkt$V7 %in% c(" Transport-moving")) mkt$farming_fishing<-1*(mkt$V7 %in% c(" Farming-fishing")) mkt$craft_repair<-1*(mkt$V7 %in% c(" Craft-repair" )) mkt$sales<-1*(mkt$V7 %in% c(" Sales")) mkt$tech_support<-1*(mkt$V7 %in% c(" Tech-support")) mkt$service<-1*(mkt$V7 %in% c(" Protective-serv"," Priv-house-serv", " Other-service")) mkt$armed_forces<-1*(mkt$V7 %in% c(" Armed-Forces")) mkt$other_occupation<-1*(mkt$V7 %in% c(" Handlers-cleaners"," ?"," Machine-op-inspct"," Prof-specialty"))我们不会使用
V8列,因为它是为了普查目的而记录的,对我们分析没有用。 -
在这里,我们为受访者的自报性别编码 0-1 变量:
mkt$male<-1*(mkt$V9 %in% c(" Male")) mkt$female<-1*(mkt$V9 %in% c(" Female"))V10和V11列不太具有信息量,所以我们不会在分析中使用它们。 -
在这里,我们为每个受访者的自报工作时间编码 0-1 变量:
mkt$high_hours<-1*(mkt$V12 > median(mkt$V12)) mkt$low_hours<-1*(mkt$V12 <= median(mkt$V12)) -
在这里,我们为受访者报告的国籍是否为美国编码 0-1 变量:
mkt$usa<-1*(mkt$V13==" United-States") mkt$not_usa<-1*(mkt$V13!=" United-States") -
在这里,我们为受访者报告的收入是否高于或低于$50,000 编码 0-1 变量:
mkt$low_income<-1*(mkt$V14==" <=50K") mkt$high_income<-1*(mkt$V14==" >50K") -
现在,我们已经添加了 33 个新的变量,它们是 0-1 编码。由于我们只会在 0-1 编码上执行市场篮子分析,我们可以删除最初用来创建只包含虚拟变量的数据集的 14 个初始变量,如下所示:
mktdummies<-mkt[,15:ncol(mkt)] mktdummies -
我们可以通过运行以下代码来查看我们每个变量的平均值:
print(colMeans(mktdummies,na.rm=TRUE))虚拟变量的平均值等于它等于 1 的时间百分比。所以,当我们看到
已婚变量的平均值是 0.473 时,我们知道大约 47.3%的受访者已婚。完成这个练习后,你的数据将会有 33 列,每一列都是一个只取 0 和 1 值的
虚拟变量实例。如果你在控制台中运行print(head(mktdummies))来打印前 6 行,那么你可以看到生成的数据集如下所示:
图 4.7:虚拟变量结果数据集的一部分
现在我们已经完成了练习,我们有一个只包含 0-1 变量的虚拟变量数据集,这些变量提供了关于数据集中每个原始变量的真/假信息。
最后,我们准备实际执行 Apriori 算法。在接下来的练习中,我们将开始“遍历”我们的数据。在每次遍历中,我们将找到具有特定大小的最常见的篮子。
在我们开始遍历数据之前,我们需要指定一个称为支持率的东西。支持率是 Apriori 算法参数之一的名字。在这里,支持率指的是包含特定项目组合的篮子百分比。如果我们发现市场数据中 40%的受访者既是高收入又是女性,那么我们将说在我们的数据中,高收入、女性的“篮子”有 40%的支持率。
我们需要决定我们感兴趣的最低支持率。如果我们设定的最低支持率阈值过高,我们将找不到任何满足阈值的篮子。如果我们设定的最低支持率阈值过低,我们将找到太多的篮子,这将很难查看所有篮子以找到有趣的一个。此外,因为我们希望找到实际有用的规则,所以我们希望找到相对常见的篮子,因为更常见的篮子更有可能对我们有实际用途。
练习 23:通过数据遍历以找到最常见的篮子
现在数据已经准备好进行市场篮子分析的主要步骤。在继续之前,我们必须决定我们将在算法中使用哪些参数:
-
我们将要处理的第一参数是支持率,如前所述。在这种情况下,我们可以从将最低支持率阈值设定为 10%开始。
support_thresh<-0.1 -
首先,我们将找到所有符合我们支持阈值的单项篮子,如下所示:
firstpass<-unname(which(colMeans(mktdummies,na.rm=TRUE)>support_thresh))这显示了至少有 10%的受访者以相同方式回答的所有调查项目。
-
为了对数据进行第二次遍历,我们将定义所有可能的两项篮子候选者,这些篮子可能支持率超过 10%,如下所示:
secondcand<-t(combn(firstpass,2)) secondpass<-NULL注意
如果少于 10%的篮子包含某个特定项目,那么超过 10%的篮子同时包含该项目和另一个项目的可能性是不存在的。因此,支持率超过 10%的两项篮子候选者将是那些在第一次数据遍历中幸存的项目组合。
我们已经定义了
secondcand,这是我们第二次遍历的候选者集合,以及secondpass,我们将用它来存储第二次遍历的结果。secondpass变量初始值为NULL,因为我们还没有开始第二次遍历。如果我们查看
secondcand,我们可以看到它由一对数字组成。每个数字都指代mktdummies数据中的一个列。例如,secondcand的第四行指代一个潜在篮子,其中包含那些表示他们年龄大于中位数且是私企雇员的受访者。在第二次数据遍历中,我们将检查secondcand中的每个两项候选者,如果其支持率超过 10%,它将成功通过第二次数据遍历。 -
为了检查我们的候选者
secondcand中第四行的支持率,我们可以进行以下计算:k<-4 support<-mean(mktdummies[,secondcand[k,1]]*mktdummies[,secondcand[k,2]],na.rm=TRUE) print(support)输出如下:
0.05515801 -
我们需要为每个候选篮子进行相同的计算,我们可以通过将这个计算放入循环中来实现。这个循环将把达到支持阈值的最终两个项篮子保存在
secondpass变量中:k<-1 while(k<=nrow(secondcand)){ support<-mean(mktdummies[,secondcand[k,1]]*mktdummies[,secondcand[k,2]],na.rm=TRUE) if(support>support_thresh){ secondpass<-rbind(secondpass,secondcand[k,]) } k<-k+1 } -
这个练习的重要结果变量是名为
secondpass的变量。这个变量包含所有达到我们指定的支持阈值(10%)的两个项篮子。通过在控制台中运行以下命令,查看这个变量的前六行:print(head(secondpass))输出如下:
[,1] [,2] [1,] 1 6 [2,] 1 9 [3,] 1 12 [4,] 1 14 [5,] 1 25 [6,] 1 26在这里,每一行包含两个数字,每个数字都指代原始数据集中的列号。例如,第一行表示
mktdummies数据集的第一列和第六列共同构成一个支持度超过 10%的两个项篮子。由于我们数据集的第一列被称为old,而数据集的第六列被称为private_employment,因此我们得出结论,既是老年人又是私营部门雇员的调查受访者占所有调查受访者的 10%以上。
在此之后,我们已经完成了第二次数据遍历。通过完成第二次遍历,我们现在有一个包含所有最常见的两个项篮子的列表。
Apriori 算法的要点在于,我们可以利用两项篮子和单项篮子来缩小我们关注的三个项候选篮子,这使得我们的搜索速度大大加快。
要全面了解 Apriori 算法的工作原理,我们应该至少再遍历一次数据,这将在下面的练习中介绍。
练习 24:多次遍历数据
在下面的练习中,我们将多次遍历数据。回想一下,每次我们遍历数据时,我们都在寻找符合我们支持阈值的篮子。在每次遍历中,我们寻求比之前遍历中更多的篮子。因此,在第一次遍历中,我们寻找符合我们支持阈值的单项篮子。在第二次遍历中,我们寻找符合我们支持阈值的两项篮子。在下面的练习中,我们将说明如何进行多次数据遍历,包括第三次遍历,我们将寻找符合我们支持阈值的三个项篮子,以及第四次遍历,我们将寻找符合我们支持阈值的四个项篮子。
如果我们对许多项目所遵循的复杂规则感兴趣,能够多次遍历数据对我们来说将非常重要:
-
在第三次遍历数据时,我们将寻找至少有 10%支持度的三个项篮子。第三次遍历数据将从
product变量等于 1 开始。这个product变量将给我们数据的不同列的乘积,而product变量的平均值将给我们不同篮子的支持度,如下所示:product<-1 n<-1 -
这个
product变量将与在第二次遍历中幸存下来的两项篮子相关的观测值相乘:thirdpass<-NULL k<-1 while(k<=nrow(secondpass)){ j<-1 while(j<=length(firstpass)){ n<-1 product<-1 while(n<=ncol(secondpass)){ product<-product*mktdummies[,secondpass[k,n]] n<-n+1 } -
最后,每个
product变量将乘以第一轮中幸存下来的单个项目篮子的观测值:if(!(firstpass[j] %in% secondpass[k,])){ product<-product*mktdummies[,firstpass[j]] -
我们取产品的平均值以找到我们指定的篮子的支持度:
support<-mean(product,na.rm=TRUE) -
如果结果三项篮子的支持度高于我们指定的支持度阈值,则将其保存到我们的最终
thirdpass变量中:if(support>support_thresh){ thirdpass<-rbind(thirdpass,c(secondpass[k,],firstpass[j])) } } j<-j+1 } k<-k+1 }注意
步骤 2-5 应一起执行。
现在我们有一个包含数据中所有大小为三的常见篮子的列表。
-
经过几轮数据遍历后,我们可以开始看到 Apriori 算法所采取步骤的一般形式。一般来说,为了找到在
n轮中幸存下来的篮子,我们需要取在n-1轮中幸存下来的篮子,向其中添加一个在第一轮中幸存下来的项目,并查看结果组合的支持度是否大于我们选择的阈值:fourthpass<-NULL k<-1 while(k<=nrow(thirdpass)){ j<-1 while(j<=length(firstpass)){ n<-1 product<-1 while(n<=ncol(thirdpass)){ product<-product*mktdummies[,thirdpass[k,n]] n<-n+1 } if(!(firstpass[j] %in% thirdpass[k,])){ product<-product*mktdummies[,firstpass[j]] support<-mean(product,na.rm=TRUE) if(support>support_thresh){ fourthpass<-rbind(fourthpass,c(thirdpass[k,],firstpass[j])) } } j<-j+1 } k<-k+1 }我们可以无限期地继续这样做,创建任何大小且符合我们支持度阈值的篮子。在这里,我们的目的是在数据中遍历四次后停止,并检查我们第三遍的结果。
-
本练习的最终重要结果是
thirdpass和fourthpass变量。这些变量包含关于符合我们支持度阈值的三项和四项篮子的信息。您可以像解释secondpass的每一行一样解释这些变量的每一行。每一行代表一个符合我们支持度阈值的篮子,每一行中的每个数字都指的是我们的数据集中的一列编号。您可以通过执行以下操作来验证
thirdpass的前六行:print(head(thirdpass))输出如下:
[,1] [,2] [,3] [1,] 1 6 9 [2,] 1 6 12 [3,] 1 6 26 [4,] 1 6 29 [5,] 1 6 30 [6,] 1 6 32我们可以将第二行解释为表示包含项目 1、项目 6 和项目 12 的篮子达到了我们的支持度阈值。
-
您可以通过以下方式验证
fourthpass的前六行:print(head(fourthpass))输出如下:
[,1] [,2] [,3] [,4] [1,] 1 6 9 26 [2,] 1 6 9 29 [3,] 1 6 9 30 [4,] 1 6 9 32 [5,] 1 6 12 26 [6,] 1 6 12 29我们可以将第五行解释为告诉我们包含项目 1、项目 6、项目 12 和项目 26 的篮子达到了我们的支持度阈值。
在之前的练习中,我们已经找到了我们感兴趣的篮子。在这个练习中,我们将获得市场篮子分析的最后产品。我们感兴趣的最后产品将是“旧”、“私人雇佣”和“低小时数”的统一。我们还感兴趣于生成一个关联这三个项目的规则。这样一个规则可能就是“年龄超过中位数调查受访者且为私人雇佣的人,很可能工作时间少于中位数受访者”。因此,市场篮子分析比其他仅发现数据中组的分布分析和聚类方法更进一步。市场篮子分析不仅找到组,而且将它们按照有意义的规则进行分组。
为了生成这些规则,我们需要指定更多的参数,类似于我们之前指定的支持度阈值。
这些参数中的一个被称为置信度。置信度仅仅是一个条件概率。假设一个人既是女性又是低收入,她离婚的可能性有多大?我们迄今为止确定的是支持度,这可能告诉我们由女性、低收入和离婚这三个项目组成的篮子占所有调查者的 10%以上。置信度告诉我们更多——它告诉我们“离婚”是否只是一个常见的篮子项目,或者是否在“女性”和“低收入”存在的情况下特别常见。
我们最后必须指定的参数被称为提升度。提升度是规则预测的项目总体普遍性的置信度。在这种情况下,假设如果一个人是女性且低收入,她有 90%的可能性也是离婚的。那么 90%是这个规则的置信度,这似乎相当高。然而,如果 89%的人无论如何都是离婚的,那么这个置信度就不会显得那么令人印象深刻。如果是这样,那么知道篮子中存在“女性”和“低收入”只会略微提高我们的预测能力,大约 1%。在这种情况下,提升度的值将是 90%/89%,或大约 1.011。这只是一个假设——我们得检查实际数据来看到提升度的实际值是多少。
一起,置信度和提升度提供了帮助我们决定一个关联规则是否有用的测量指标。在一个复杂的情况,比如我们在这里看到的许多问题调查中,我们指定置信度和提升度的最小阈值,以过滤掉不够有用的关联规则,这样我们就可以用少量非常有用的规则完成 Apriori 算法。
练习 25:作为 Apriori 算法最后一步生成关联规则
在这个练习中,我们将完成 Apriori 算法的最后一步。到目前为止,任何经过我们数据处理而幸存下来的篮子都可以被认为是候选规则。在市场篮子分析的最终步骤中,我们将根据我们的最终标准——置信度和提升度进一步减少候选规则。
-
检查以下经过多次数据处理的篮子:
head(thirdpass)输出如下:
[,1] [,2] [,3] [1,] 1 6 9 [2,] 1 6 12 [3,] 1 6 26 [4,] 1 6 29 [5,] 1 6 30 [6,] 1 6 32你可以这样看到经过第三次处理幸存下来的三项篮子的数量:
nrow(thirdpass)输出如下:
[1] 549我们可以看到有 549 个三项篮子,即 549 个至少在我们的数据中有 10%支持度的候选规则。这些篮子不是市场篮子分析的最终产品——我们正在寻找的最终产品是关联规则。
-
我们三项篮子的置信度公式如下:由所有三个项目组成的篮子的支持度,除以只包含前两个项目的篮子的支持度。我们可以这样计算我们的
thirdpass三项篮子的第五行的置信度:k<-5 confidence<-mean(mktdummies[,thirdpass[k,1]]*mktdummies[,thirdpass[k,2]]*mktdummies[,thirdpass[k,3]],na.rm=TRUE)/mean(mktdummies[,thirdpass[k,1]]*mktdummies[,thirdpass[k,2]],na.rm=TRUE)注意
这只是包含三个项目的完整购物篮的支持度,除以不包含第三个项目的两个项目的支持度。
-
提升度是置信度除以规则预测的项目总体流行度。对于我们的第三遍候选人的第五行,提升度可以很容易地按以下方式计算:
k<-5 lift<-confidence/mean(mktdummies[,thirdpass[k,3]],na.rm=TRUE) -
为了将候选规则缩小到一组可接受的关联规则,我们将指定最小置信度和提升度阈值,就像我们对支持度所做的那样。在这里,我们指定了提升度阈值为 1.8 和置信度阈值为 0.8:
注意
lift_thresh<-1.8 conf_thresh<-.8 -
我们可以通过以下循环为我们的每个候选规则计算
提升度和置信度:thirdpass_conf<-NULL k<-1 while(k<=nrow(thirdpass)){ support<-mean(mktdummies[,thirdpass[k,1]]*mktdummies[,thirdpass[k,2]]*mktdummies[,thirdpass[k,3]],na.rm=TRUE) confidence<-mean(mktdummies[,thirdpass[k,1]]*mktdummies[, thirdpass[k,2]]*mktdummies[,thirdpass[k,3]],na.rm=TRUE)/ mean(mktdummies[,thirdpass[k,1]]*mktdummies[,thirdpass[k,2]],na.rm=TRUE) lift<-confidence/mean(mktdummies[,thirdpass[k,3]],na.rm=TRUE) thirdpass_conf<-rbind(thirdpass_conf,unname(c(thirdpass[k,],support,confidence,lift))) k<-k+1 }这生成了一个名为
thirdpass_conf的新变量,它是一个包含每个候选规则的支持度、置信度和提升度列的 DataFrame。在这里,conf被用作置信度的简称,这是我们添加到thirdpass数据中的。 -
最后,我们可以消除所有不符合指定置信度和提升度阈值的候选规则,如下所示:
thirdpass_high<-thirdpass_conf[which(thirdpass_conf[,5]>conf_thresh & thirdpass_conf[,6]>lift_thresh),] -
现在我们有了
thirdpass_high,这是我们数据中具有高置信度和高提升度的关联三项目规则的集合。我们可以通过以下方式将其中一些打印到控制台来浏览它们:head(thirdpass_high)
图 4.8:thirdpass_high 的输出
总的来说,我们在市场篮子分析中遵循的步骤可以总结如下:
注意
记住,这些是指我们在练习 22,为 Apriori 算法准备数据中创建的虚拟变量,其中我们创建了一个名为old的虚拟变量,对于年龄较高的个体,其值为 1,否则为 0。我们还创建了一个表示高收入的虚拟变量,其中 1 表示年收入超过 50,000 美元,否则为 0。
thirdpass_high的第一行规则的解释是:年龄超过中位数且收入较高的人,很可能(具有高置信度和高提升度)已婚。这在直觉上是有道理的:婚姻和高收入都需要很多年才能实现,所以没有很多年轻、已婚、高收入的人是有道理的。我们发现这个规则的置信度约为 87%,提升度约为 1.84。
在这种情况下,进行调查的公司可以使用这些数据来创建广告活动——要么创建针对已婚老年人的住房广告活动,因为这是一个经过证明的高收入人口群体,要么针对年轻的已婚人士的住房广告活动,因为这可能是一个未得到充分服务的群体,将构成商业机会。我们发现的每个七项规则都可以提供对人口模式和商业机会的见解,以及这些规则告诉我们什么以及它们提供的确定性量化测量。
在我们的市场篮子分析过程中,我们可以做出一些不同的选择,这些选择可能会改变我们的结果。如果我们改变指定的阈值,我们可能会得到更多的规则,或者更有用的规则。例如,如果我们将支持阈值设置为 9%而不是 10%,则过滤出的规则会更少,我们可能最终得到一条规则,例如“住在公寓里的年轻学生很可能是亚裔美国人”,这是一条只占调查受访者约 9%的群体的规则。
我们只关注了包含三项的篮子和与这些篮子元素相关的规则。通过允许更多或更少的物品进入我们用于搜索规则的篮子,我们可以找到更有趣的规则,这些规则可能导致坚实的商业洞察。所有这些都是在相对较短的时间内用相对较少的代码行完成的。这表明市场篮子分析在解决数据问题和商业问题方面的有用性和潜力。
市场篮子分析将一个高维问题(在大数据集中寻找模式的问题)转化为一个低维解决方案(六个简单、高置信度的规则),而无需太多的努力、计算能力或时间。
主成分分析
我们将要介绍的下一类降维方法是主成分分析(PCA)。这是一种在广泛领域的学者中非常常见的技巧。
线性代数复习
本节不会对线性代数进行全面回顾,而只是提醒一些主要观点。
备注
joshua.smcvt.edu/linearalgebra/#current_version 覆盖了一些基础知识,包括矩阵、协方差矩阵、特征向量和特征值。如果您已经熟悉这些术语,可以自由跳过线性代数复习。
矩阵
线性代数主要关注矩阵的分析。矩阵可以被视为一个矩形格式的数字集合。我们可以在 R 中创建一个矩阵,如下所示:
matrix1<-matrix(c(1,2,3,4,5,6),nrow=2)
在这里,我们创建了一个两行三列的矩阵,总共有六个条目。我们根据矩阵中条目出现的行和列来描述条目。在我们刚刚创建的"matrix1"中,数字 3 位于"1-2"位置,因为它位于第一行第二列。我们可以在 R 中通过调用matrix1[1,2]来访问该特定位置。
方差
通常,一个变量的方差让我们了解该变量分布的广泛程度。
协方差
协方差是测量两个不同变量一起的方差。它衡量它们的分散程度是否匹配。换句话说,它衡量如果一个变量高,另一个变量也高的程度,以及每个变量预期会多高。
练习 26:检查葡萄酒数据集中的方差和协方差
执行练习 21中要遵循的所有步骤,即检查包含不同葡萄酒化学属性的数据集。然后计算同一数据集的方差和协方差:
-
酒精测量值都在 11.03 和 14.83 之间,你可以通过运行以下代码来看到:
range(wine$alcohol)输出如下:
[1] 11.03 14.83 -
我们可以使用 R 的
var命令来计算方差。对于葡萄酒的酒精测量,我们发现var(wine$alcohol)约为 0.66。相比之下,我们发现通过执行以下代码,我们数据集中的镁测量值分布更广:range(wine$magnesium)输出如下:
[1] 70 162 -
这表明变量范围从 70 到 162。由于它分布更广,我们应该预期方差更高,我们确实通过执行以下代码找到了这一点:
var(wine$magnesium)输出如下:
[1] 203.9893 -
要计算协方差,执行以下代码:
cov(wine$alcohol,wine$magnesium)输出如下:
[1] 3.139878 -
在步骤 4中,我们发现酒精和镁变量的协方差约为 3.14。请注意,协方差是对称的,所以 X 与 Y 的协方差与 Y 与 X 的协方差相同。你可以通过尝试以下代码来检查这一点:
cov(wine$magnesium,wine$alcohol)输出如下:
[1] 3.139878你会注意到它产生了相同的值。
-
一个变量的方差就是该变量与其自身的协方差。你可以通过运行以下代码来看到这一点:
var(wine$magnesium)输出如下:
[1] 203.9893通过执行以下代码,你会得到相同的输出:
cov(wine$magnesium,wine$magnesium)输出如下:
[1] 203.9893
协方差矩阵是一个方阵,其中每个条目都是一个方差或协方差。要构建协方差矩阵,首先我们必须给我们的数据集中的每个变量编号。在葡萄酒数据集中,我们可以根据列列表中的顺序给每个变量一个编号。因此,酒精将是变量 1,苹果酸将是变量 2,依此类推。
注意
记住,你可以在数据源网站archive.ics.uci.edu/ml/datasets/wine看到变量的列表。
在对变量排序后,我们可以创建协方差矩阵。在这个矩阵中,我们说的是,“i-j 条目是变量 i 和变量 j 的协方差。”因此,第一行第二列的项目是 1-2 条目,它将等于第一个变量(酒精)与第二个变量(苹果酸)的协方差。由于协方差是一个对称操作,2-1 条目将与 1-2 条目相同。这意味着矩阵本身将是对称的——每个条目都与主对角线另一侧镜像位置的条目相同。
协方差矩阵主对角线上的条目将是方差而不是协方差。例如,矩阵的 3-3 位置的条目将是变量 3 与变量 3 的协方差——这是变量与其自身的协方差,这也是说它是变量方差的一种方式。
特征向量和特征值
当我们有一个如协方差矩阵这样的方阵时,我们可以计算一些特殊的向量,称为特征向量。每个特征向量都有一个与之相关的值,称为特征值。关于特征向量和特征值的讨论可以轻易填满一本书。对我们来说,关于特征向量最重要的知道是它们表达了数据中最大方差的方向。关于特征值最重要的知道是它们表明哪些特征向量是最重要的。
PCA 的概念
PCA 是一种基于前面复习中描述的线性代数主题的强大降维技术。
为了完成 PCA,我们将取我们数据的协方差矩阵,然后找到它的特征向量。协方差矩阵的特征向量被称为主成分。主成分使我们能够用不同的术语和不同的维度重新表达数据。
我们将使用本章开头探索的与葡萄酒相关的数据集。回想一下,葡萄酒数据集有 13 个维度,这些维度测量了特定葡萄酒的特定化学属性。该数据集中的一项观测值由 13 个数字组成——每个维度一个。
主成分分析(PCA)使数据能够以不同的术语重新表达。葡萄酒数据集的协方差矩阵将包含 13 个特征向量。我们可以将这些特征向量解释为 13 个新的维度——我们将在接下来的练习中看到如何做到这一点。本质上,我们将能够用我们通过 PCA 发现的新维度完全描述每个观测值。
更重要的是,PCA 使我们能够进行降维。我们不必用特征向量定义的 13 个新维度重新表示数据,而只需选择这 13 个新维度中最重要的 12 个,并用这 12 个维度来表示数据,而不是原来的 13 个维度。PCA 使得选择最重要的维度变得容易,因为每个特征向量的重要性是通过其对应的特征值来衡量的。以下练习将更详细地说明如何做到这一点。
在 PCA 过程中,我们将创建一种新的图表类型,称为散点图。散点图是一个简单的线段图,显示了矩阵的特征值,按从高到低的顺序排列,以指示它们相关特征向量的相对重要性。
散点图显示了矩阵的特征值,按从大到小的顺序绘制。我们将使用散点图来决定哪些特征向量(即哪些维度)是最重要的。
PCA 可能听起来很难,它基于一些可能对你来说是新术语和想法,但实际上在 R 中实现相对简单。
练习 27:执行主成分分析(PCA)
如果我们有一个协方差矩阵,我们就准备好执行 PCA 了。在这种情况下,我们将使用本章前面探索过的葡萄酒数据集。我们的目标是进行降维——用比原始数据集更少的维度来表示葡萄酒数据集。这个练习建立在练习 26,“在葡萄酒数据集上检查方差和协方差”的基础上:
-
首先,加载本章前面使用的相同
wine数据集。作为第一步,我们将从葡萄酒数据集中删除class列。我们这样做是因为class不是葡萄酒的化学属性,而是一个标签,我们感兴趣的是研究葡萄酒的化学属性。我们可以按照以下方式删除此列:wine_attributes<-wine[,2:14] -
我们可以按照以下方式获取这个较小矩阵的协方差矩阵:
wine_cov<-cov(wine_attributes) -
接下来,我们将使用 R 中的一个函数
eigen。这个函数计算称为特征向量的特殊向量,以及称为特征值的特殊值。我们可以将其应用于我们的协方差矩阵,如下所示:wine_eigen<-eigen(wine_cov) -
现在,我们可以查看我们找到的特征向量:
print(wine_eigen$vectors)输出如下:
图 4.10:葡萄酒的特征向量
-
R 已经将特征向量编译成一个与我们的原始协方差矩阵大小相同的方阵。这个新矩阵的每一列都是协方差矩阵的一个特征向量。如果我们查看我们找到的特征值,我们可以看到每个特征向量的相对重要性。执行以下命令来查看特征值:
print(wine_eigen$values)输出如下:
图 4.11:葡萄酒的特征值
-
我们实际上已经完成了我们的 PCA。协方差矩阵的特征向量被称为数据的特征值。让我们看看第一个:
print(wine_eigen$vectors[,1])输出如下:
图 4.12:这个第一个特征向量表示了原始维度的线性组合。
我们可以这样理解我们的第一个主成分:
主成分 1 = -0.0016592647 * 酒精 + 0.0006810156 * 苹果酸 -0.0001949057 * 灰分 + 0.0046713006 * 碱度 -0.0178680075 * 镁 - 0.0009898297 * 酚 -0.0015672883 * 黄烷醇 +0.0001230867 * 非酚 -0.0006006078 * 白藜芦醇 -0.0023271432 * 颜色 -0.0001713800 * 色调 -0.0007049316 * OD280 -0.9998229365 * 脯氨酸
因此,特征向量的每个元素都是这个方程中的一个系数,用于生成一个新的主成分。主成分是原始维度的线性组合。我们可以将每个主成分用作新的维度。所以,我们不必通过说“它有 14.23 的酒精测量值,1.71 的苹果酸测量值……”等等来描述一个观察结果,我们可以通过说类似“它有 5.62 的主成分 1 测量值,9.19 的主成分 2 测量值……”等等来描述它。
这个练习最重要的结果是wine_eigen$vectors和wine_eigen$values对象。
任何降维技术都意味着我们必须在数据集中丢失一些编码的信息。这是不可避免的:一个数字永远不能完全表达出 13 个数字所表达的一切。PCA 的好处是它保证了这是降维最有效的方法——通过用主成分来表示数据,我们丢失了尽可能少的信息。
在接下来的练习中,我们将讨论如何转换数据以实现降维。
练习 28:使用 PCA 进行降维
这个练习是前一个练习的延续——它将使用相同的数据和相同的矩阵以及我们之前计算的特征向量:
-
记住,我们协方差矩阵的每个特征向量都告诉我们一个可以用来总结数据的 13 个葡萄酒属性的线性组合。在这种情况下,第一个特征向量告诉我们我们可以这样转换数据:
neigen<-1 transformed<-t(t(as.matrix(wine_eigen$vectors[,1:neigen])) %*% t(as.matrix(wine_attributes)))在这里,我们指定了若干个特征向量(1),并且将我们的原始数据集乘以这个数量的特征向量,创建了一个用这个特征向量或我们的第一个主成分表示的转换后的数据集。
-
我们可以这样查看我们转换后的数据集的一部分:
print(head(transformed))这将给出以下输出:
图 4.13:转换后的数据集
在这里,我们有一个一维数据集,它只使用一个数字来描述每个观测值。因此,我们说第一个观测的葡萄酒在主成分 1 上的得分为-1067.0557。我们已经完成了降维。
-
我们可以通过以下乘法进行数据集的部分恢复:
restored<- t(as.matrix(wine_eigen$vectors[,1:neigen]) %*% t(as.matrix(transformed)))这应该可以恢复我们的原始数据集。
注意
由于降维总是丢失一些数据中编码的原始信息,因此它不会是一个完美的恢复。
-
我们可以通过以下方式测试我们的变换是否导致了数据的准确重建:
print(mean(abs(wine_attributes[,13]-restored[,13])))输出如下:
[1] 1.466919在这种情况下,错误相当小,这表明我们在恢复数据方面相当成功。
-
我们可以使用任意数量的维度进行降维。通常,我们可以通过生成以下所示的碎石图来确定在变换中应使用多少维度:
plot(wine_eigen$values,type='o')在这种情况下,我们的碎石图如下所示:
图 4.14:显示协方差矩阵特征值的碎石图
为了决定使用多少维度进行降维,我们可以查看这个碎石图,并选择一个与相对较高的特征值数量相对应的维度数。
我们可以看到,第一个特征值远远是最高的,因此第一个特征向量也是最重要的一个,它告诉我们第一个主成分是最重要的维度。在这种情况下,将数据集简化为一维数据集是非常合适的。
你刚刚对一个协方差矩阵进行了 PCA。
活动 10:对新的数据集执行 PCA 和市场篮子分析
在接下来的活动中,你将加载一个新的数据集,然后你将对它执行 PCA 和市场篮子分析。该活动将涵盖这两个程序的所有主要步骤,包括所需的数据准备。我们将使用的数据集来自对马萨诸塞州波士顿周边地区的社区所进行的研究,它包含了许多社区的属性,包括税率、房产价值和当地人口的人口统计信息。
对于这个活动,使用"Boston"数据集,可以在 R 中运行以下代码:
library(MASS)
data(Boston)
这些步骤将帮助你完成活动:
-
通过以下方式将所有变量转换为虚拟变量:对于每个变量,创建一个新的变量,如果它等于或高于该变量的中位数,则等于 1,如果它低于该变量的中位数,则等于 0。创建另一个新的变量,它是这个变量的补数:在之前创建的虚拟变量中,每个 0 都是 1,每个 1 都是 0。将所有虚拟变量保存到一个名为
Bostondummy的新数据集中。 -
找到原始数据的所有特征向量和特征值
-
创建该数据的特征值散点图。如何解释这个散点图?
-
尝试仅使用少数几个主成分来近似此数据。你的近似与原始数据有多接近?
-
使用你在步骤 1中创建的虚拟变量,通过找到值在超过 10%的行中为 1 的所有变量来进行市场篮子分析的第一遍。
-
通过找到在数据中有超过 10%支持的所有变量的组合来进行市场篮子分析的第二次遍历。
-
完成市场篮子分析,直到三项篮子。
预期输出:此活动的最重要的输出是数据集的主成分,以及从市场篮子分析中获得的三项规则。主成分在活动的第二步解决方案中获得,当我们创建Boston_eigen时,我们可以运行print(Boston_eigen$vectors)命令来查看主成分,如下所示:
图 4.15:原始数据的主成分
市场篮子分析的三项规则在活动的步骤 14的解决方案中获得,当我们运行控制台中的print(head(thirdpass_conf))时,我们可以看到最终结果:
图 4.16:市场篮子分析的三项规则
注意
此活动的解决方案可以在第 222 页找到。
摘要
在本章中,我们讨论了数据维度的概念。我们探讨了为什么降低数据的维度可能是有用的,并强调了降维过程可以揭示关于数据潜在结构的重要真相。我们介绍了两种重要的降维方法。我们讨论的第一种方法是市场篮子分析。这种方法对于从复杂数据中生成关联规则很有用,并且可以用于其命名的用例(分析购物篮)或广泛的其它应用(例如分析调查响应的聚类)。我们还讨论了 PCA,这是一种用其维度的线性组合来描述数据的方法。PCA 使用一些线性代数工具很容易执行,并提供了一种简单的方法来近似甚至非常复杂的数据。
在下一章中,我们将探讨不同的数据比较方法。