层次聚类实战:用Python和sklearn搞定AgglomerativeClustering(附完整代码)
·
层次聚类实战:从原理到Python实现的全流程指南
当我们需要对一组缺乏标签的数据进行探索性分析时,聚类算法往往能帮我们发现数据中隐藏的结构和模式。在众多聚类方法中,层次聚类以其直观的树状结构和无需预先指定簇数量的优势,成为数据科学家工具箱中的重要组成部分。
1. 层次聚类核心原理与算法选择
层次聚类通过构建树状图(dendrogram)来展示数据点之间的层次关系,这种可视化方式让我们能够直观地理解数据在不同粒度下的分组情况。算法主要分为两类:
- 凝聚式(Agglomerative):自底向上,每个数据点初始为一个簇,逐步合并最相似的簇
- 分裂式(Divisive):自顶向下,所有数据点初始为一个簇,逐步分裂为更小的簇
在sklearn中,AgglomerativeClustering实现了凝聚式层次聚类,其核心计算流程如下:
- 计算所有数据点间的距离矩阵
- 将每个点视为单独的簇
- 重复以下步骤直到满足终止条件:
- 找出距离最近的两个簇
- 合并这两个簇
- 更新距离矩阵
关键参数解析:
| 参数名 | 类型 | 说明 | 常用值 |
|---|---|---|---|
| n_clusters | int | 最终需要的簇数量 | 2-10 |
| affinity | str | 距离度量方法 | 'euclidean', 'l1', 'cosine' |
| linkage | str | 簇间距离计算方式 | 'ward', 'complete', 'average' |
提示:当使用'ward'连接方式时,affinity必须设置为'euclidean'
2. 数据准备与预处理实战
良好的数据准备是聚类成功的前提。我们首先生成一个具有明显簇结构的模拟数据集:
import numpy as np
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 生成包含4个簇的二维数据
X, y = make_blobs(
n_samples=500,
n_features=2,
centers=[[-1, -1], [0, 0], [1, 1], [2, 2]],
cluster_std=[0.3, 0.2, 0.2, 0.1],
random_state=42
)
# 数据可视化
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c='steelblue', s=50, alpha=0.7)
plt.title('Generated Data with 4 Clusters')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.grid(True)
plt.show()
对于真实数据集,预处理步骤通常包括:
- 缺失值处理:删除或填充缺失值
- 标准化:使用StandardScaler或MinMaxScaler
- 降维:对高维数据可先使用PCA
from sklearn.preprocessing import StandardScaler
# 标准化数据
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
3. 模型训练与参数调优
选择合适的距离度量和连接方式对聚类结果影响显著。下面我们比较不同参数组合的效果:
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics import silhouette_score
# 测试不同参数组合
param_grid = {
'affinity': ['euclidean', 'cosine', 'l1'],
'linkage': ['ward', 'complete', 'average']
}
best_score = -1
best_params = {}
for affinity in param_grid['affinity']:
for linkage in param_grid['linkage']:
# ward方法只能与euclidean配合使用
if linkage == 'ward' and affinity != 'euclidean':
continue
model = AgglomerativeClustering(
n_clusters=4,
affinity=affinity,
linkage=linkage
)
labels = model.fit_predict(X_scaled)
score = silhouette_score(X_scaled, labels)
if score > best_score:
best_score = score
best_params = {'affinity': affinity, 'linkage': linkage}
print(f"Best parameters: {best_params} with score: {best_score:.3f}")
实际项目中,我们还需要考虑:
- 确定最佳簇数:通过轮廓系数或树状图切割高度
- 处理大规模数据:使用connectivity矩阵限制计算范围
- 评估指标选择:除轮廓系数外,还可考虑Calinski-Harabasz指数
4. 结果可视化与分析
层次聚类的结果可以通过两种方式直观展示:
4.1 簇分布可视化
# 使用最佳参数训练模型
best_model = AgglomerativeClustering(
n_clusters=4,
affinity=best_params['affinity'],
linkage=best_params['linkage']
)
labels = best_model.fit_predict(X_scaled)
# 绘制聚类结果
plt.figure(figsize=(10, 8))
for i in range(4):
plt.scatter(
X_scaled[labels == i, 0],
X_scaled[labels == i, 1],
label=f'Cluster {i+1}',
s=50,
alpha=0.7
)
plt.title('Agglomerative Clustering Results')
plt.xlabel('Standardized Feature 1')
plt.ylabel('Standardized Feature 2')
plt.legend()
plt.grid(True)
plt.show()
4.2 树状图绘制
树状图展示了完整的层次合并过程,帮助我们理解数据的内在结构:
from scipy.cluster.hierarchy import dendrogram, linkage
# 计算连接矩阵
Z = linkage(X_scaled, method=best_params['linkage'])
# 绘制树状图
plt.figure(figsize=(12, 8))
dendrogram(
Z,
truncate_mode='lastp',
p=12,
show_leaf_counts=True,
leaf_rotation=90.,
leaf_font_size=12.,
show_contracted=True
)
plt.title('Hierarchical Clustering Dendrogram')
plt.xlabel('Sample index or Cluster size')
plt.ylabel('Distance')
plt.show()
在实际分析树状图时,可以关注:
- 纵轴距离:反映簇间差异程度
- 水平切割线:决定最终簇数量
- 簇大小分布:识别异常小或大的簇
5. 高级应用与性能优化
当处理真实世界数据时,我们常遇到以下挑战及解决方案:
5.1 处理非球形簇
对于复杂形状的簇,欧氏距离可能表现不佳。解决方案包括:
- 使用适合的距离度量(如余弦相似度)
- 结合核方法进行数据转换
- 采用谱聚类等更高级技术
# 示例:使用余弦相似度处理文本数据
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import make_pipeline
texts = [...] # 假设有一组文本数据
pipeline = make_pipeline(
TfidfVectorizer(),
AgglomerativeClustering(
n_clusters=5,
affinity='cosine',
linkage='average'
)
)
pipeline.fit(texts)
5.2 大规模数据加速技巧
层次聚类的时间复杂度为O(n³),对大数据集可考虑:
- 使用近似算法:如FastCluster
- 限制连接范围:通过connectivity矩阵
- 分批处理:结合Mini-Batch方法
from sklearn.neighbors import kneighbors_graph
# 创建k近邻连接矩阵
connectivity = kneighbors_graph(
X_scaled,
n_neighbors=10,
include_self=False
)
model = AgglomerativeClustering(
n_clusters=4,
affinity='euclidean',
linkage='ward',
connectivity=connectivity
)
model.fit(X_scaled)
5.3 特征重要性分析
了解哪些特征对聚类贡献最大:
import pandas as pd
# 假设我们有特征名称
feature_names = ['Feature1', 'Feature2']
# 计算每个簇的特征均值
cluster_means = pd.DataFrame(
[X_scaled[labels == i].mean(axis=0) for i in range(4)],
columns=feature_names
)
# 可视化
cluster_means.plot(
kind='bar',
figsize=(10, 6),
title='Feature Means by Cluster'
)
plt.ylabel('Mean Value (Standardized)')
plt.xticks(rotation=0)
plt.show()
6. 实战案例:客户细分分析
以一个电商用户行为数据集为例,展示完整分析流程:
import pandas as pd
from sklearn.preprocessing import StandardScaler
# 加载数据
data = pd.read_csv('customer_behavior.csv')
# 选择特征
features = ['purchase_freq', 'avg_order_value', 'last_purchase_days']
# 预处理
X = data[features].fillna(0)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 确定最佳簇数
scores = []
for n in range(2, 8):
model = AgglomerativeClustering(n_clusters=n)
labels = model.fit_predict(X_scaled)
scores.append(silhouette_score(X_scaled, labels))
best_n = np.argmax(scores) + 2 # 从2开始
# 最终模型
final_model = AgglomerativeClustering(n_clusters=best_n)
data['cluster'] = final_model.fit_predict(X_scaled)
# 分析各簇特征
cluster_profiles = data.groupby('cluster')[features].mean()
print(cluster_profiles)
在这个案例中,我们发现了4个明显的客户群体:
- 高价值常客:高频购买、高客单价
- 低频高消费:不常购买但单次消费高
- 常规客户:中等频率和消费水平
- 流失风险客户:很久未购买
针对不同群体可以制定差异化营销策略:
- 对高价值常客提供VIP服务
- 对低频高消费客户发送大促提醒
- 对流失风险客户实施召回活动
更多推荐


所有评论(0)