层次聚类
(四)层次聚类
层次聚类是一种通过逐层合并或分裂数据点构建树状结构(树状图,Dendrogram)的聚类方法。它分为两种类型:
- 凝聚层次聚类(Agglomerative Hierarchical Clustering):这是最常用的层次聚类方法。开始时每个数据点都被视为自己的簇,然后算法重复执行以下步骤直到所有点被合并为一个簇:
- 找出最近的两个簇。
- 将它们合并成一个新的簇。
- 重新计算新簇与其他簇之间的距离。
- 分裂层次聚类(Divisive Hierarchical Clustering):与凝聚方法相反,这种较少使用的方法从整个数据集作为一个单一的簇开始,然后逐步分裂簇,直到每个数据点成为独立的簇为止。
- 商业应用场景:客户分群、市场细分、供应链优化等。
分层聚类分析(Hierarchical Clustering Analysis)生成的树状图,也称为树形图(Dendrogram),是展示数据点或簇之间层次关系的一种可视化方式。通过树状图,可以直观地看出哪些数据点或簇之间的相似度更高,以及如何逐步合并形成更大的簇。下面是一些解读树状图的关键点:
树状图的基本结构
- 垂直轴:通常表示距离或相似性度量。在树状图中,分支的长度代表了合并的数据簇之间的距离。越长的分支意味着簇间距离更大,即这些簇中的数据点彼此间的相似度较低。
- 水平轴:代表参与聚类的数据点或者最终形成的簇。每个叶子节点对应一个原始数据点,而内部节点则代表由其子节点所构成的簇。
簇的形成过程
- 树状图从底部开始,每个数据点最初都是单独的一个簇。随着向上移动,距离较近的簇会逐渐合并成新的簇,这一过程持续进行直到所有数据点都被合并到一个单一的簇中。这个自底向上的过程被称为凝聚层次聚类。
如何决定簇的数量
- 切割树状图:通过在垂直方向上设定一个阈值来“切割”树状图,可以确定簇的数量。例如,在树状图上画一条水平线,这条线与多少个垂直线段相交就决定了将数据分成多少个簇。
- 观察大跳跃:有时可以通过观察垂直轴上的较大跳跃(即较长的分支)来判断合理的切割点,因为较大的跳跃意味着显著不同的簇。
1.算法步骤
数据标准化
- 目的:消除量纲差异,确保特征权重一致;
- 方法:Z-score 标准化、最大-最小归一化;
- 公式(Z-score): x s c a l e d = x − μ σ x_{scaled}=\frac{x−μ}{σ} xscaled=σx−μ
计算距离矩阵
- 输入:标准化后的数据矩阵 X ∈ R n × m X∈R^{n×m} X∈Rn×m, n n n 为样本数, m m m 为特征数;
- 输出:对称矩阵 D ∈ R n × n D∈R^{n×n} D∈Rn×n,表示样本间距离;
- 距离度量:
- 欧氏距离(默认): d i j = ∑ k = 1 m ( x i k − x j k ) 2 d_{ij}=\sqrt{\sum_{k=1}^m (x_{ik}-x_{jk})^2} dij=∑k=1m(xik−xjk)2;
- 曼哈顿距离、余弦相似度等。
合并最近的两个簇
- 链接方法(Linkage Criterion):
- 单链接(Single):两簇中最近样本的距离;
- 全链接(Complete):两簇中最远样本的距离;
- 平均链接(Average):两簇所有样本对的平均距离;
- Ward方法:合并后簇的方差增量最小(适合商业数据)。
- 链接方法(Linkage Criterion):
更新距离矩阵
- 将合并后的新簇与其他簇的距离重新计算,更新矩阵 D D D。
终止条件
- 重复步骤3-4,直到所有样本合并为一个簇。
生成树状图
- 树状图(Dendrogram):可视化合并过程,横轴为样本,纵轴为合并距离。
- 截断阈值:通过树状图高度或业务需求确定最终簇数。
2.MATLAB 实现
%% 用户价值分层,绿色为高价值、红色为中价值、蓝色为低价值
clc; % 清空命令行窗口
clear; % 清除工作区变量
close all; % 关闭所有图形窗口
%% ====================== 生成模拟客户数据(200个样本) ======================
rng(42); % 固定随机种子,确保每次运行结果可复现
n_samples = 200; % 总样本数
% 特征1:年消费金额(万元)—— 高价值客户消费更高
% 前100个样本为高价值客户,消费金额服从 N(3, 0.5) 分布
% 后100个样本为低价值客户,消费金额服从 N(1, 0.3) 分布
monetary = [abs(randn(n_samples/2,1)*0.5 + 3); % 高价值组
abs(randn(n_samples/2,1)*0.3 + 1)]; % 低价值组
% 特征2:购买频率(次/年)—— 高价值客户更活跃
% 高价值组频率服从 N(10, 2),低价值组服从 N(5, 3)
frequency = [abs(randn(n_samples/2,1)*2 + 10);
abs(randn(n_samples/2,1)*3 + 5)];
% 特征3:最近消费时间(天)—— 高价值客户更近期消费
% 高价值组最近消费时间服从 N(30, 30),低价值组服从 N(90, 60)
recency = [abs(randn(n_samples/2,1)*30 + 30);
abs(randn(n_samples/2,1)*60 + 90)];
% 合并特征矩阵:每行代表一个客户,三列分别为消费金额、频率、最近时间
X = [monetary, frequency, recency];
% 真实标签(仅用于验证,实际聚类无监督,不需要标签)
labels = [ones(n_samples/2,1); % 前100个样本标签为1(高价值)
2*ones(n_samples/2,1)]; % 后100个样本标签为2(低价值)
%% ====================== 数据标准化(Z-score) ======================
% 目的:消除量纲差异,使各特征均值为0,标准差为1
X_scaled = zscore(X); % zscore函数自动计算每列的均值和标准差
%% ====================== 层次聚类(使用Ward方法) ======================
% 步骤1:计算样本间距离矩阵
% pdist计算欧氏距离,返回压缩格式的距离向量(上三角部分)
D = pdist(X_scaled, 'euclidean');
% 步骤2:构建层次聚类树(链接矩阵Z)
% linkage函数使用Ward方法(最小化簇内方差)进行层次聚类
% Z矩阵格式:每行代表一次合并,列依次为合并簇1、簇2、距离、新簇包含的样本数
Z = linkage(D, 'ward');
%% ====================== 手动划分聚类(3个簇) ======================
n = size(X_scaled, 1); % 样本数量(200)
k = 3; % 目标簇数
num_merges = n - k; % 需要合并的次数(200-3=197次)
max_clusters = 2 * n - 1; % 最大可能簇数(初始n个簇 + 每次合并新增1个簇)
cluster_members = cell(1, max_clusters); % 存储每个簇的成员索引
% 初始化:每个样本为一个独立簇
for i = 1:n
cluster_members{i} = i; % 第i个簇仅包含样本i
end
% 逐次合并簇(共197次)
for i = 1:num_merges
c1 = Z(i, 1); % 当前合并的第一个簇索引(来自Z矩阵第i行)
c2 = Z(i, 2); % 当前合并的第二个簇索引
new_cluster_idx = n + i; % 新簇的索引(从n+1开始递增)
% 合并簇成员:将c1和c2的成员合并到新簇
cluster_members{new_cluster_idx} = [cluster_members{c1}, cluster_members{c2}];
% 清空原簇(避免重复使用)
cluster_members{c1} = [];
cluster_members{c2} = [];
end
% 收集最终的k个簇(非空簇)
final_clusters = {}; % 存储最终簇的成员列表
for j = 1:length(cluster_members)
if ~isempty(cluster_members{j})
final_clusters{end+1} = cluster_members{j}; % 添加非空簇
end
end
% 为每个样本分配簇标签(1到k)
clusters = zeros(n, 1); % 初始化标签向量
for cluster_idx = 1:length(final_clusters)
members = final_clusters{cluster_idx}; % 当前簇的成员索引
clusters(members) = cluster_idx; % 分配簇标签
end
%% ====================== 可视化与业务分析 ======================
% 绘制树状图(Dendrogram)
figure;
dendrogram(Z); % 显示层次聚类过程
title('客户分群树状图');
xlabel('样本索引');
ylabel('合并距离');
% 绘制3D散点图展示聚类结果
figure;
scatter3(X_scaled(:,1), X_scaled(:,2), X_scaled(:,3), 50, clusters, 'filled');
xlabel('标准化消费金额');
ylabel('标准化购买频率');
zlabel('标准化最近消费时间');
title('客户价值分层结果');
colormap(jet); % 使用jet颜色映射区分簇
% 统计各簇特征均值和标准差
% grpstats按簇分组计算统计量
cluster_stats = grpstats(X, clusters, {'mean', 'std'});
disp('各簇统计特征:');
disp([' [Mean_Monetary] ','[Mean_Frequency] ','[Mean_Recency]']);
disp(['高价值组:',num2str(cluster_stats(2,:))]);
disp(['中价值组:',num2str(cluster_stats(3,:))]);
disp(['低价值组:',num2str(cluster_stats(1,:))]);
% 输出业务策略
fprintf('=== 业务策略 ===\n');
fprintf('簇1(高价值): 消费金额高、频率高、近期活跃 → 提供VIP服务与专属折扣\n');
fprintf('簇2(中价值): 消费中等 → 推送促销活动提升复购\n');
fprintf('簇3(低价值): 消费低且久未活跃 → 发送唤醒优惠券\n');
参考资料
[1] 层次聚类的基本原理_哔哩哔哩_bilibili
[2] 层次聚类的树状图如何看?序列分析和回归分析如何结合?哔哩哔哩_bilibili