跳至主要内容

层次聚类:概念概览与示例

理解层次聚类的基本概念、工作原理,以及如何用 Python 实现。
· 9分钟 阅读

使用 AI 探索

您刚接到一项任务,要分析一个大型数据集,并提供深入的聚类分析。您遇到了“层次聚类”这个术语,但它究竟是什么,又是如何工作的? 

本文将探讨层次聚类的概念,并给出实际示例,帮助您更好地理解其应用。

什么是层次聚类?

层次聚类是一种无监督学习算法,用于将相似的数据点分组为簇。它通过将较小的簇合并为较大的簇(自底向上,凝聚式)或将较大的簇划分为较小的簇(自顶向下,分裂式)来构建多层次的簇层级结构。这会生成一种树状结构,称为树状图(dendrogram)。

树状图是一种可视化表示,用于展示簇的排列及其相互关系。树状图中分支的高度表示簇合并时的距离或不相似度。高度越低表示在较小距离上合并,因而相似度更高。

层次聚类在机器学习中的重要性

当事先不知道簇的数量时,层次聚类尤其有用。它允许数据分析师和数据科学家通过树状图直观地探索数据结构。 

该技术还能发现嵌套簇,广泛应用于基因组学、客户细分和文档组织等领域。

例如,假设我们有一个客户购买记录的数据集。使用层次聚类,我们可以将购买模式相似的客户分组为簇,并识别潜在的市场细分,以制定有针对性的营销策略。

层次聚类 vs K-Means 聚类

在聚类任务中,多数人会在两种主要方法间犹豫:层次聚类或K-means 聚类。

它们的区别是什么?我们来看下面的对比以获得更清晰的认识。

  • K-Means:
    • 在运行前需要定义簇的数量。
    • 对大型数据集效率高、可扩展性好。
    • 假设簇呈球形且大小相近。
  • 层次聚类:
    • 不需要预先定义簇的数量。
    • 借助树状图更易解释。
    • 在大型数据集上较慢,但更适合探索结构。

对比这两者时,有些方面会让层次聚类更具优势。

以下是层次聚类表现较好的场景:

  • 借助树状图灵活选择簇。
  • 适合小型数据集或需要可视化层级结构的情况。
  • 能够处理复杂的簇形状。

不过,它也存在一些缺点,例如:

  • 计算开销大
  • 对噪声数据和异常值敏感。

您可能会问:什么时候应该使用层次聚类?

层次聚类在以下应用中是绝佳选择:

层次聚类的类型

在层次聚类方法中,您会遇到多种类型,每种都会带来不同的洞察和结果。两种主要类型是凝聚式(自底向上)和分裂式(自顶向下)。

agglomerative vs divisive

来源:Himanshu Sharma(Medium)

1. 凝聚式(自底向上)

凝聚式聚类从将每个数据点视为一个独立的簇开始,反复合并距离最近的一对簇,直到只剩一个簇,或满足某个停止条件(如达到期望的簇数)。

这种方法也称为自底向上,因为它从底部(单个数据点)开始,逐步构建到顶部(最终簇)。

步骤指南:

  1. 将每个数据点视为单元素簇。
  2. 计算所有簇的成对距离。
  3. 合并距离最小的两个簇。
  4. 更新距离矩阵以反映新的簇集合。
  5. 重复步骤 2–4,直到所有点属于同一个簇。

该方法因其简单易实现而被广泛使用。

下面是如何在 Python 中实现的示例:

import numpy as np
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
import matplotlib.pyplot as plt

# Samples data
data = np.array([[1, 2], [2, 3], [5, 8], [8, 8], [1, 0], [0, 1]])

# Applies agglomerative clustering using Ward's method
Z = linkage(data, method='ward')

# Plots dendrogram
plt.figure(figsize=(8, 4))
dendrogram(Z)
plt.title('Agglomerative Clustering Dendrogram')
plt.xlabel('Data Points')
plt.ylabel('Distance')
plt.show()

# Extracts clusters (e.g., form 2 clusters)
clusters = fcluster(Z, t=2, criterion='maxclust')
print("Cluster assignments:", clusters)

2. 分裂式(自顶向下)

分裂式聚类从将所有数据点置于一个簇中开始,并递归地将其划分为更小的簇。该过程持续进行,直到每个数据点都成为单独的簇。

它也被称为自顶向下的方法,因为它从顶部(单一簇)开始,逐步拆分为更小的簇。

步骤指南:

  1. 从包含所有数据点的一个大簇开始。
  2. 将该簇拆分为两个差异尽可能大的簇。
  3. 对每个得到的簇递归地重新应用拆分过程。

分裂式方法通常因其递归特性而计算开销更大,且准确性在很大程度上取决于拆分策略。凝聚式方法因实现简单且软件支持广泛而更为常见。

注意:在标准的 Python 库中,分裂式层次聚类不像凝聚式那样易于使用。一种思路是递归使用 k-means 等聚类算法。

下面是模拟递归 k-means 拆分的代码思路:

from sklearn.cluster import KMeans

def divisive_clustering(data, depth=2):
    if depth == 0 or len(data) <= 1:
        return [data]

    kmeans = KMeans(n_clusters=2, random_state=42).fit(data)
    labels = kmeans.labels_
    cluster1 = data[labels == 0]
    cluster2 = data[labels == 1]

    return divisive_clustering(cluster1, depth - 1) + divisive_clustering(cluster2, depth - 1)

# Run recursive splitting to simulate divisive clustering
split_clusters = divisive_clustering(data, depth=2)
for i, cluster in enumerate(split_clusters):
    print(f"Cluster {i+1} size: {len(cluster)}")

这段简化代码用递归的 K-means 展示了分裂式聚类的概念性做法。但请注意,标准的分裂方法(如 DIANA)采用不同的拆分准则。

关键概念与术语

层次聚类涉及一些重要的概念与术语。为全面理解该过程,我们将在下文更详细地介绍这些概念。

1. 树状图(Dendrogram)

dendogram example

树状图是一种树形结构,用于可视化层次聚类的过程。树的每一层代表一次合并或拆分操作,分支的高度表示簇被合并时的距离(或不相似度)。

其重要性在于:

  • 允许对不同层级的簇进行可视化检查。
  • 通过在某一高度“切割”树状图,您可以选择最适合数据的簇数量。

2. 连接准则(Linkage)

连接准则决定了在合并过程中如何计算簇与簇之间的距离。

常见的连接准则包括:

单链接(最小)

该准则关注两个簇中最近点之间的距离,可能导致细长、链式的簇。

from scipy.spatial.distance import pdist, squareform
data = np.array([[1, 2], [2, 3], [5, 8]])
distance_matrix = squareform(pdist(data, metric='euclidean')) 
single_linkage_min = np.min(distance_matrix[0, 1:])
print("Single Linkage Distance between Cluster A (point 1) and Cluster B (point 2, 3):", single_linkage_min)

完全链接(最大)

该准则关注两个簇中最远点之间的距离,倾向于产生紧凑、大小均匀的簇。

single_linkage_max = np.max(distance_matrix[0, 1:])
print("Complete Linkage Distance between Cluster A and B:", single_linkage_max)

平均链接(均值)

该准则关注一个簇中所有点到另一个簇中所有点的平均距离,介于单链接与完全链接之间,取得平衡。

average_linkage = np.mean(distance_matrix[0, 1:])
print("Average Linkage Distance between Cluster A and B:", average_linkage)

这些简化示例展示了最初如何计算距离。然而在聚类过程中,这些准则(单链接、完全链接、平均链接)是迭代地在整个簇之间应用距离,而不仅是单个点。

Ward 方法

Ward 方法是平均链接的一种变体,最小化所有簇内平方差之和。与其他方法相比,它更倾向于生成大小更均匀的簇,并且常常得到分离良好、近球形的簇。

from scipy.cluster.hierarchy import linkage
linkage_ward = linkage(data, method='ward')
print("Ward's Linkage Matrix:\n", linkage_ward)

每种方法都有其优势,应根据数据与用例进行选择。

3. 距离度量

所用的距离度量会显著影响聚类结果。

欧氏距离

欧氏距离是在多维空间中两点间的直线距离。对几何分布的数据最常用。

曼哈顿距离

曼哈顿距离, 亦称出租车距离或 L1 距离,是各维度绝对差之和。适用于网格状数据,如城市街区或金融交易。

余弦相似度

余弦相似度衡量两个非零向量之间夹角的余弦值。当不关心量级(如词频向量)时,此度量非常适合文本数据。

在 Python 中实现层次聚类

本节将演示如何用 Python 实现凝聚式层次聚类。从数据预处理到使用树状图可视化结果,我们将逐步讲解。

依赖库

首先导入所需的库:

import numpy as np
import pandas as pd
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt

上面的代码导入了:

  • numpy 与 pandas:用于数据处理
  • scipy.cluster.hierarchy:用于聚类与生成树状图
  • sklearn.preprocessing.StandardScaler:用于特征缩放
  • matplotlib:用于可视化

示例数据

开始分析之前,我们先创建一个二维点的玩具数据集并进行缩放。对于基于距离的算法,缩放至关重要。

我们的示例数据集如下所示。

example data array

data = np.array([[1, 2], [2, 3], [5, 8], [8, 8], [1, 0], [0, 1]])
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)

以下是缩放后的数据数组。

scaled example data array

生成连接矩阵与树状图

我们应用 Ward 方法生成连接矩阵,其中包含层次聚类信息。随后绘制树状图。

linkage_matrix = linkage(data_scaled, method='ward')
plt.figure(figsize=(8, 4))
dendrogram(linkage_matrix)
plt.title('Dendrogram')
plt.xlabel('Data Points')
plt.ylabel('Distance')
plt.show()

在上述代码中,linkage 构建簇层级结构,dendrogram 用于可视化簇的形成过程。

dendogram example

通过观察合并前最长的竖线位置,我们可以决定在何处“切割”以形成簇。寻找不与其他簇相交的最高竖线处进行切割。

在本例中,您需要切分为 2 个簇。

创建簇

接下来,使用 fcluster 按指定簇数对树状图进行切割:

clusters = fcluster(linkage_matrix, t=2, criterion='maxclust')
print(clusters)

plt.scatter(data_scaled[:, 0], data_scaled[:, 1], c=clusters, cmap='rainbow')
plt.title('Clusters')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()

这会将每个数据点分配到两个簇中的一个。

有关代码的一些补充说明:

  • t=2:指定期望的簇数量。
  • criterion='maxclust':确保获得恰好 t 个簇。

您可以更改 t 以尝试不同的簇数量。

实践示例:客户细分

层次聚类常用于客户细分。我们来看一个实际实现的示例。

1. 数据集概览

我们将模拟一个包含两个特征(年龄与收入)的客户数据集。使用 make_blobs 生成该合成数据。

from sklearn.datasets import make_blobs
X, _ = make_blobs(n_samples=100, centers=3, n_features=2, random_state=42)

2. 预处理

接下来我们需要标准化特征,以归一化尺度并提升聚类准确性。

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

以下是缩放后的数据:

scaled customer data

3. 应用层次聚类

数据处理完毕后,我们绘制树状图以可视化簇的形成。

linkage_matrix = linkage(X_scaled, method='ward')
plt.figure(figsize=(10, 5))
dendrogram(linkage_matrix)
plt.title('Customer Dendrogram')
plt.xlabel('Sample Index')
plt.ylabel('Distance')
plt.show()

customer clustering dendogram

4. 结果解读

根据树状图,我们选择一个高度进行“切割”,创建三个簇:

labels = fcluster(linkage_matrix, t=3, criterion='maxclust')
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='rainbow')
plt.title('Customer Segments')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()

Customer Segment Clusters

业务启示:

  • 这些簇可能代表注重预算型、中端型和高端型客户群。
  • 支持个性化营销并改进客户关系策略。

层次聚类的优势与局限

在分析中选择层次聚类既有优势也有局限。 

以下是一些常见点:

优点

  • 无需预先指定簇数:可在查看树状图后再行选择。
  • 直观可视化:树状图有助于理解簇的形成。
  • 灵活性强:可根据连接方式与距离度量处理多样的簇形状。

缺点

  • 计算量大:不适合大型数据集。
  • 对噪声与异常值敏感:少量异常值可能显著扭曲树状图。
  • 不可回溯重分配:一旦合并无法撤销,可能导致次优聚类。

结论

层次聚类是一种多才多艺且易于解释的聚类技术,尤其适用于探索性数据分析,以及基因组学和客户细分等领域。尽管计算成本较高,其优势在于能够发现嵌套分组,并可通过树状图在事后灵活选择簇的数量。

对于小到中等规模且重视可解释性的数据集,层次聚类仍是数据科学家与分析师的常用方法。

想更深入地学习机器学习的细节?我们的Python 机器学习基础学习路径以及Python 层次聚类入门教程都是很好的起点。

层次聚类常见问答

层次聚类比 K-means 更好吗?

这取决于数据以及您的目标。层次聚类更适合小型数据集以及当数据点间关系尚不明确时;而 K-means 更适合大型数据集以及能够清晰识别簇的情况。

层次聚类可以用于分类变量吗?

可以,您可以先将分类变量转换为数值再应用层次聚类。但这并不总是合适,需视具体数据集而定。

如何选择层次聚类的簇数量?

可使用多种方法,如解读树状图、轮廓系数或“肘部法”,来帮助确定数据集的最优簇数。

层次聚类与 K-means 聚类有何不同?

层次聚类通过将相似的数据点分组来构建簇的层级结构;而 K-means 则基于最小化簇内方差将数据划分为预设数量的簇。此外,K-means 需要用户事先指定簇数,而层次聚类没有此要求。

层次聚类如何处理异常值?

与其他聚类方法相比,层次聚类受异常值影响相对较小,因为它考虑的是数据的整体结构而非单个点。然而,在某些情况下,异常值仍可能扭曲结果,需要谨慎处理。处理异常值的技术包括从数据集中移除异常值,或使用对异常值不太敏感的距离度量。

Austin Chia

作者

Austin Chia

我是 Austin,一名博主和科技作者,曾在医疗领域担任数据科学家与数据分析师,拥有多年的从业经验。我以生物学背景开启技术之路,如今通过我的科技博客帮助他人完成同样的转型。我对技术的热情促使我为数十家 SaaS 公司撰写内容,分享经验、启发他人。

主题