一文读懂层次聚类(Python代码)

简介: 本篇想和大家介绍下层次聚类,先通过一个简单的例子介绍它的基本理论,然后再用一个实战案例Python代码实现聚类效果。

大家好,我是东哥。

本篇想和大家介绍下层次聚类,先通过一个简单的例子介绍它的基本理论,然后再用一个实战案例Python代码实现聚类效果。

首先要说,聚类属于机器学习的无监督学习,而且也分很多种方法,比如大家熟知的有K-means。层次聚类也是聚类中的一种,也很常用。下面我先简单回顾一下K-means的基本原理,然后慢慢引出层次聚类的定义和分层步骤,这样更有助于大家理解。


层次聚类和K-means有什么不同?


K-means 工作原理可以简要概述为:

  • 决定簇数(k)
  • 从数据中随机选取 k 个点作为质心
  • 将所有点分配到最近的聚类质心
  • 计算新形成的簇的质心
  • 重复步骤 3 和 4

这是一个迭代过程,直到新形成的簇的质心不变,或者达到最大迭代次数。

但是 K-means 是存在一些缺点的,我们必须在算法开始前就决定簇数 K 的数量,但实际我们并不知道应该有多少个簇,所以一般都是根据自己的理解先设定一个值,这就可能导致我们的理解和实际情况存在一些偏差。

层次聚类完全不同,它不需要我们开始的时候指定簇数,而是先完整的形成整个层次聚类后,通过决定合适的距离,自动就可以找到对应的簇数和聚类。


什么是层次聚类?


下面我们由浅及深的介绍什么是层次聚类,先来一个简单的例子。

假设我们有以下几点,我们想将它们分组:

23.png


我们可以将这些点中的每一个分配给一个单独的簇,就是4个簇(4种颜色):

image.gif

然后基于这些簇的相似性(距离),将最相似的(距离最近的)点组合在一起并重复这个过程,直到只剩下一个集群:image.gif

上面本质上就是在构建一个层次结构。先了解到这里,后面我们详细介绍它的分层步骤。


层次聚类的类型


主要有两种类型的层次聚类:

  • 凝聚层次聚类
  • 分裂层次聚类


凝聚层次聚类


先让所有点分别成为一个单独的簇,然后通过相似性不断组合,直到最后只有一个簇为止,这就是凝聚层次聚类的过程,和我们上面刚刚说的一致。


分裂层次聚类


分裂层次聚类正好反过来,它是从单个集群开始逐步分裂,直到无法分裂,即每个点都是一个簇。

所以无论是 10、100、1000 个数据点都不重要,这些点在开始的时候都属于同一个簇:

24.png


现在,在每次迭代中拆分簇中相隔最远的两点,并重复这个过程,直到每个簇只包含一个点:

25.png


上面的过程就是分裂层次聚类


执行层次聚类的步骤


上面已经说了层次聚类的大概过程,那关键的来了,如何确定点和点的相似性呢?

这是聚类中最重要的问题之一了,一般计算相似度的方法是:计算这些簇的质心之间的距离。距离最小的点称为相似点,我们可以合并它们,也可以将其称为基于距离的算法

另外在层次聚类中,还有一个称为邻近矩阵的概念,它存储了每个点之间的距离。下面我们通过一个例子来理解如何计算相似度、邻近矩阵、以及层次聚类的具体步骤。


案例介绍


假设一位老师想要将学生分成不同的组。现在有每个学生在作业中的分数,想根据这些分数将他们分成几组。关于拥有多少组,这里没有固定的目标。由于老师不知道应该将哪种类型的学生分配到哪个组,因此不能作为监督学习问题来解决。下面,我们将尝试应用层次聚类将学生分成不同的组。

下面是个5名学生的成绩:

26.png


创建邻近矩阵


首先,我们要创建一个邻近矩阵,它储存了每个点两两之间的距离,因此可以得到一个形状为 n X n 的方阵。

这个案例中,可以得到以下 5 x 5 的邻近矩阵:

image.gif27.png

矩阵里有两点需要注意下:

  • 矩阵的对角元素始终为 0,因为点与其自身的距离始终为 0
  • 使用欧几里得距离公式来计算非对角元素的距离

比如,我们要计算点 1 和 2 之间的距离,计算公式为:

同理,按此计算方法完成后填充邻近矩阵其余元素。


执行层次聚类


这里使用凝聚层次聚类来实现。

步骤 1:首先,我们将所有点分配成单个簇:image.gif

这里不同的颜色代表不同的簇,我们数据中的 5 个点,即有 5 个不同的簇。

步骤2:接下来,我们需要查找邻近矩阵中的最小距离并合并距离最小的点。然后我们更新邻近矩阵:最小距离是 3,因此我们将合并点 1 和 2:

让我们看看更新的集群并相应地更新邻近矩阵:

28.png

更新之后,我们取了1、2 两个点中值 (7, 10) 最大的来替换这个簇的值。当然除了最大值之外,我们还可以取最小值或平均值。然后,我们将再次计算这些簇的邻近矩阵:29.png

第 3 步:重复第 2 步,直到只剩下一个簇。

重复所有的步骤后,我们将得到如下所示的合并的聚类:

30.png


这就是凝聚层次聚类的工作原理。但问题是我们仍然不知道该分几组?是2、3、还是4组呢?

下面开始介绍如何选择聚类数。


如何选择聚类数?


为了获得层次聚类的簇数,我们使用了一个概念,叫作树状图

通过树状图,我们可以更方便的选出聚类的簇数。

回到上面的例子。当我们合并两个簇时,树状图会相应地记录这些簇之间的距离并以图形形式表示。下面这个是树状图的原始状态,横坐标记录了每个点的标记,纵轴记录了点和点之间的距离:

31.png

当合并两个簇时,将会在树状图中连接起来,连接的高度就是点之间的距离。下面是我们刚刚层次聚类的过程。

32.png

然后开始对上面的过程进行树状图的绘制。从合并样本 1 和 2 开始,这两个样本之间的距离为 3。

33.png

可以看到已经合并了 1 和 2。垂直线代表 1 和 2 的距离。同理,按照层次聚类过程绘制合并簇类的所有步骤,最后得到了这样的树状图:

34.png

通过树状图,我们可以清楚地形象化层次聚类的步骤。树状图中垂直线的距离越远代表簇之间的距离越大。

有了这个树状图,我们决定簇类数就方便多了。

现在我们可以设置一个阈值距离,绘制一条水平线。比如我们将阈值设置为 12,并绘制一条水平线,如下:

35.png

从交点中可以看到,聚类的数量就是与阈值水平线与垂直线相交的数量(红线与 2 条垂直线相交,我们将有 2 个簇)。与横坐标相对应的,一个簇将有一个样本集合为 (1,2,4),另一个集群将有一个样本集合 (3,5)。

这样,我们就通过树状图解决了分层聚类中要决定聚类的数量。


Python代码实战案例


上面是理论基础,有点数学基础都能看懂。下面介绍下在如何用代码Python来实现这

一过程。这里拿一个客户细分的数据来展示一下。


数据集和代码在我的GitHub里,欢迎star!


https://github.com/xiaoyusmd/PythonDataScience

这个数据来源于UCI 机器学习库。我们的目的是根据批发分销商的客户在不同产品类别(如牛奶、杂货、地区等)上的年度支出,对他们进行细分。

首先对数据进行一个标准化,为了让所有数据在同一个维度便于计算,然后应用层次聚类来细分客户。


from sklearn.preprocessing import normalize
data_scaled = normalize(data)
data_scaled = pd.DataFrame(data_scaled, columns=data.columns)
import scipy.cluster.hierarchy as shc
plt.figure(figsize=(10, 7))  
plt.title("Dendrograms")  
dend = shc.dendrogram(shc.linkage(data_scaled, method='ward'))

36.png

x 轴包含了所有样本,y 轴代表这些样本之间的距离。距离最大的垂直线是蓝线,假如我们决定要以阈值 6 切割树状图:

plt.figure(figsize=(10, 7))  
plt.title("Dendrograms")  
dend = shc.dendrogram(shc.linkage(data_scaled, method='ward'))
plt.axhline(y=6, color='r', linestyle='--')

37.png

现在我们有两个簇了,我们要对这 2 个簇应用层次聚类:

from sklearn.cluster import AgglomerativeClustering
cluster = AgglomerativeClustering(n_clusters=2, affinity='euclidean', linkage='ward')  
cluster.fit_predict(data_scaled)

38.png


由于我们定义了 2 个簇,因此我们可以在输出中看到 0 和 1 的值。0 代表属于第一个簇的点,1 代表属于第二个簇的点。

plt.figure(figsize=(10, 7))  
plt.scatter(data_scaled['Milk'], data_scaled['Grocery'], c=cluster.labels_)

39.png


到这里我们就成功的完成了聚类。

相关文章
|
14天前
|
缓存 监控 测试技术
Python中的装饰器:功能扩展与代码复用的利器###
本文深入探讨了Python中装饰器的概念、实现机制及其在实际开发中的应用价值。通过生动的实例和详尽的解释,文章展示了装饰器如何增强函数功能、提升代码可读性和维护性,并鼓励读者在项目中灵活运用这一强大的语言特性。 ###
|
17天前
|
缓存 开发者 Python
探索Python中的装饰器:简化代码,增强功能
【10月更文挑战第35天】装饰器在Python中是一种强大的工具,它允许开发者在不修改原有函数代码的情况下增加额外的功能。本文旨在通过简明的语言和实际的编码示例,带领读者理解装饰器的概念、用法及其在实际编程场景中的应用,从而提升代码的可读性和复用性。
|
14天前
|
Python
探索Python中的装饰器:简化代码,提升效率
【10月更文挑战第39天】在编程的世界中,我们总是在寻找使代码更简洁、更高效的方法。Python的装饰器提供了一种强大的工具,能够让我们做到这一点。本文将深入探讨装饰器的基本概念,展示如何通过它们来增强函数的功能,同时保持代码的整洁性。我们将从基础开始,逐步深入到装饰器的高级用法,让你了解如何利用这一特性来优化你的Python代码。准备好让你的代码变得更加优雅和强大了吗?让我们开始吧!
22 1
|
19天前
|
设计模式 缓存 监控
Python中的装饰器:代码的魔法增强剂
在Python编程中,装饰器是一种强大而灵活的工具,它允许程序员在不修改函数或方法源代码的情况下增加额外的功能。本文将探讨装饰器的定义、工作原理以及如何通过自定义和标准库中的装饰器来优化代码结构和提高开发效率。通过实例演示,我们将深入了解装饰器的应用,包括日志记录、性能测量、事务处理等常见场景。此外,我们还将讨论装饰器的高级用法,如带参数的装饰器和类装饰器,为读者提供全面的装饰器使用指南。
|
14天前
|
存储 缓存 监控
掌握Python装饰器:提升代码复用性与可读性的利器
在本文中,我们将深入探讨Python装饰器的概念、工作原理以及如何有效地应用它们来增强代码的可读性和复用性。不同于传统的函数调用,装饰器提供了一种优雅的方式来修改或扩展函数的行为,而无需直接修改原始函数代码。通过实际示例和应用场景分析,本文旨在帮助读者理解装饰器的实用性,并鼓励在日常编程实践中灵活运用这一强大特性。
|
19天前
|
存储 算法 搜索推荐
Python高手必备!揭秘图(Graph)的N种风骚表示法,让你的代码瞬间高大上
在Python中,图作为重要的数据结构,广泛应用于社交网络分析、路径查找等领域。本文介绍四种图的表示方法:邻接矩阵、邻接表、边列表和邻接集。每种方法都有其特点和适用场景,掌握它们能提升代码效率和可读性,让你在项目中脱颖而出。
28 5
|
17天前
|
机器学习/深度学习 数据采集 人工智能
探索机器学习:从理论到Python代码实践
【10月更文挑战第36天】本文将深入浅出地介绍机器学习的基本概念、主要算法及其在Python中的实现。我们将通过实际案例,展示如何使用scikit-learn库进行数据预处理、模型选择和参数调优。无论你是初学者还是有一定基础的开发者,都能从中获得启发和实践指导。
36 2
|
19天前
|
数据库 Python
异步编程不再难!Python asyncio库实战,让你的代码流畅如丝!
在编程中,随着应用复杂度的提升,对并发和异步处理的需求日益增长。Python的asyncio库通过async和await关键字,简化了异步编程,使其变得流畅高效。本文将通过实战示例,介绍异步编程的基本概念、如何使用asyncio编写异步代码以及处理多个异步任务的方法,帮助你掌握异步编程技巧,提高代码性能。
53 4
|
20天前
|
缓存 开发者 Python
探索Python中的装饰器:简化和增强你的代码
【10月更文挑战第32天】 在编程的世界中,简洁和效率是永恒的追求。Python提供了一种强大工具——装饰器,它允许我们以声明式的方式修改函数的行为。本文将深入探讨装饰器的概念、用法及其在实际应用中的优势。通过实际代码示例,我们不仅理解装饰器的工作方式,还能学会如何自定义装饰器来满足特定需求。无论你是初学者还是有经验的开发者,这篇文章都将为你揭示装饰器的神秘面纱,并展示如何利用它们简化和增强你的代码库。
|
19天前
|
API 数据处理 Python
探秘Python并发新世界:asyncio库,让你的代码并发更优雅!
在Python编程中,随着网络应用和数据处理需求的增长,并发编程变得愈发重要。asyncio库作为Python 3.4及以上版本的标准库,以其简洁的API和强大的异步编程能力,成为提升性能和优化资源利用的关键工具。本文介绍了asyncio的基本概念、异步函数的定义与使用、并发控制和资源管理等核心功能,通过具体示例展示了如何高效地编写并发代码。
27 2