1. 项目概述
今天要分享的是一个完整的k-medoids聚类算法MATLAB实现,特别适合刚接触聚类分析的朋友们快速上手。这个项目最大的亮点在于数据导入和可视化部分都提供了详细的中文注释,对于英语不太好的学习者来说简直是福音。
k-medoids算法作为k-means的"近亲",在鲁棒性方面表现更出色。它不像k-means那样使用均值作为中心点,而是选择实际存在的样本点作为medoids(中心点),这使得算法对异常值不那么敏感。我在金融风控项目中就曾用它成功识别出了异常交易模式。
2. 核心算法解析
2.1 k-medoids算法原理
k-medoids的核心思想其实很直观:从数据集中选出k个代表点(medoids),然后将其余点分配到最近的medoid形成簇。与k-means不同之处在于:
- medoid必须是数据集中的真实样本点
- 使用曼哈顿距离(L1范数)而非欧式距离(L2范数)
- 通过最小化绝对误差而非平方误差来优化
这种设计使得算法对噪声和异常值具有更强的抵抗力。举个例子,在一个包含月收入数据的数据集中,如果有个别亿万富翁的数据点,k-means可能会被严重干扰,但k-medoids受影响就小得多。
2.2 MATLAB实现要点
在MATLAB中实现k-medoids时,有几个关键环节需要注意:
- 距离矩阵计算:预先计算所有点之间的距离可以大幅提升效率
- medoid选择:初始medoid的选择对结果影响很大,常见方法有随机选择和最大距离法
- 簇分配:使用矩阵运算而非循环可以加速计算
- medoid更新:需要遍历当前簇内所有点作为候选,计算总距离
提示:对于大型数据集,可以考虑使用近似算法或采样技术来降低计算复杂度。
3. 代码实现详解
3.1 数据导入模块
matlab复制% 从Excel文件导入数据
data = xlsread('dataset.xlsx'); % 读取Excel数据文件
% 检查数据有效性
if isempty(data)
error('数据导入失败,请检查文件路径和格式');
end
% 数据标准化(Z-score标准化)
data = zscore(data); % 使各特征具有相同尺度
% 显示前5行数据预览
disp('数据预览:');
disp(data(1:5,:));
这部分代码有几个实用技巧:
- 使用xlsread兼容.xls和.xlsx格式
- 添加了数据有效性检查
- 自动进行Z-score标准化,这对基于距离的算法很重要
- 提供数据预览方便调试
3.2 核心算法实现
matlab复制function [medoids, clusters] = kmedoids(data, k, max_iter)
% 初始化:随机选择k个medoids
n = size(data,1);
medoids = datasample(1:n, k, 'Replace', false);
for iter = 1:max_iter
% 计算距离矩阵
dist_matrix = pdist2(data, data(medoids,:), 'cityblock');
% 分配点到最近的medoid
[~, clusters] = min(dist_matrix, [], 2);
% 更新medoids
new_medoids = zeros(1, k);
for i = 1:k
cluster_points = data(clusters==i, :);
[~, idx] = min(sum(pdist2(cluster_points, cluster_points, 'cityblock'), 2));
new_medoids(i) = find(clusters==i, idx);
end
% 检查收敛
if isequal(medoids, new_medoids)
break;
end
medoids = new_medoids;
end
end
这段代码实现了经典PAM(Partitioning Around Medoids)算法。有几个优化点值得注意:
- 使用pdist2的'cityblock'选项计算曼哈顿距离
- 矩阵运算替代循环提升效率
- 提前终止机制避免不必要迭代
3.3 可视化模块
matlab复制function plot_clusters(data, medoids, clusters)
% 创建颜色映射
colors = lines(length(medoids));
figure;
hold on;
% 绘制各簇点
for i = 1:length(medoids)
scatter(data(clusters==i,1), data(clusters==i,2), 36, colors(i,:), 'filled');
end
% 标记medoids
scatter(data(medoids,1), data(medoids,2), 100, colors, 'x', 'LineWidth', 2);
% 添加图例和标题
legend('Cluster 1', 'Cluster 2', 'Cluster 3', 'Medoids');
title('k-medoids聚类结果');
xlabel('特征1');
ylabel('特征2');
grid on;
hold off;
end
可视化时特别注意:
- 使用lines颜色映射确保颜色区分度
- medoids用大号"x"标记突出显示
- 添加网格线方便观察分布
4. 完整使用示例
4.1 端到端流程
matlab复制% 1. 数据准备
data = xlsread('customer_data.xlsx');
data = data(:, [3,5]); % 选择年收入和消费频率两列
% 2. 运行k-medoids
k = 3; % 假设我们要分3类
[medoids, clusters] = kmedoids(data, k, 100);
% 3. 可视化结果
plot_clusters(data, medoids, clusters);
% 4. 评估结果
silhouette_values = silhouette(data, clusters);
disp(['平均轮廓系数:', num2str(mean(silhouette_values))]);
4.2 参数调优建议
-
k值选择:
- 使用肘部法则(观察误差下降拐点)
- 轮廓系数法(-1到1,越大越好)
- 实际业务需求决定
-
距离度量选择:
- 数值数据:曼哈顿距离(默认)
- 分类数据:汉明距离
- 混合数据:自定义距离函数
-
最大迭代次数:
- 一般设置50-100足够
- 可以添加收敛检测提前终止
5. 常见问题与解决方案
5.1 算法不收敛
症状:迭代达到最大值仍未收敛
解决方法:
- 检查数据是否有异常值(先用箱线图检测)
- 尝试不同的初始medoid选择策略
- 增加最大迭代次数(但通常不是根本解决办法)
5.2 内存不足
症状:大数据集时报内存错误
优化方案:
- 使用稀疏矩阵存储距离矩阵
- 分批次计算距离
- 考虑使用近似算法如CLARA
5.3 聚类结果不理想
诊断步骤:
- 检查数据是否适合聚类(先做PCA可视化)
- 尝试不同的k值
- 考虑数据是否需要特殊预处理(如对数变换)
注意:k-medoids对数据尺度敏感,务必进行标准化处理(如Z-score)。
6. 性能优化技巧
6.1 向量化计算
避免使用循环,改用矩阵运算。例如距离计算:
matlab复制% 低效写法
for i = 1:n
for j = 1:k
dist(i,j) = sum(abs(data(i,:) - medoids(j,:)));
end
end
% 高效写法
dist = pdist2(data, medoids, 'cityblock');
6.2 并行计算
利用MATLAB并行计算工具箱加速:
matlab复制parfor i = 1:k
cluster_points = data(clusters==i, :);
[~, idx] = min(sum(pdist2(cluster_points, cluster_points, 'cityblock'), 2));
new_medoids(i) = find(clusters==i, idx);
end
6.3 近似算法
对于超大数据集,可以考虑:
- CLARA算法:在数据子集上应用PAM
- 基于采样的方法:先采样再聚类
- 使用KD-tree加速最近邻搜索
7. 实际应用案例
7.1 客户细分
我曾用这个算法为零售企业分析客户行为:
- 使用年消费额和访问频率作为特征
- 识别出高价值客户、潜在流失客户等群体
- 为不同群体制定精准营销策略
关键发现:k-medoids比k-means更能识别出边缘客户群体,因为对异常消费行为不敏感。
7.2 图像分割
将算法应用于图像处理:
- 将像素的RGB值作为特征
- 使用加权曼哈顿距离考虑空间信息
- 实现简单的图像分割效果
matlab复制img = imread('test.jpg');
[h,w,~] = size(img);
pixels = double(reshape(img, h*w, 3));
[~, clusters] = kmedoids(pixels, 4, 20); % 分成4类
segmented = reshape(clusters, h, w);
imshow(label2rgb(segmented));
8. 进阶扩展方向
8.1 核k-medoids
通过核函数将数据映射到高维空间:
- 使用RBF核计算相似度
- 适用于非线性可分数据
- 实现代码只需修改距离计算部分
8.2 模糊k-medoids
类似模糊c-means,允许点属于多个簇:
- 引入隶属度概念
- 需要修改目标函数和更新规则
- 对边界点处理更灵活
8.3 在线k-medoids
处理数据流场景:
- 增量式更新medoids
- 使用滑动窗口机制
- 适用于实时监控系统
我在实际项目中发现,将k-medoids与其他技术结合往往能取得更好效果。比如先用PCA降维再聚类,或者用聚类结果作为特征输入到分类模型中。MATLAB的强大矩阵运算能力使得这些组合方法实现起来非常高效。