ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB kmeans聚类实战:从数据准备到结果验证的完整指南

MATLAB kmeans聚类实战:从数据准备到结果验证的完整指南 简介这份资源是面向本科及以上学习者、科研人员与工程实践者的MATLAB聚类分析工具包围绕k-means算法解决数据分组与模式识别问题适合课程作业、论文实验及项目原型开发等场景。压缩包共10个文件约378KB包含2个.m主程序脚本、1个.xls与1个.xlsx数据表格以及6张jpg结果示意图代码完整且附有注释数据齐全便于直接运行与后续扩展。资源已有232人学习下载说明其在教学与实践中具有一定参考价值。读者可获得一套可复用的k-means实现流程包括数据读取、聚类计算与结果可视化配合示例数据与运行截图能快速理解算法参数设置与效果评估并在此基础上修改距离度量、聚类数目或替换数据集完成创新性扩展。若运行中遇到疑问也可联系作者获取支持。1. 从一份能跑的 kmeans 聚类 MATLAB 代码说起数据齐全到底意味着什么很多人第一次接触聚类是在一堆没有标签的样本面前发懵分类模型用不了因为没人告诉你哪条数据属于哪一类。kmeans 聚类分析就是干这个的——它不需要标签只靠样本之间的距离把相似的东西自动归堆。MATLAB 做这件事有天然优势矩阵运算快、可视化顺手、统计与机器学习工具箱里现成的函数拿来就能用。但真正卡住新手的往往不是算法本身而是「代码完整、数据齐全」这六个字背后的东西数据长什么样、维度怎么统一、聚类数 K 怎么定、结果怎么验证。这篇笔记就围绕一份可直接复现的 kmeans 聚类 MATLAB 代码把数据准备、参数设置、结果评估和踩坑记录一次讲透适合刚上手聚类、手里有数据但不知道怎么落地的人。2. kmeans 在 MATLAB 里到底怎么算原理、选型与最小可跑代码2.1 算法核心与 MATLAB 的实现路径kmeans 的目标很朴素把 N 个样本分成 K 个簇让每个样本到它所属簇中心的距离平方和最小。数学上就是最小化簇内平方误差和这个目标函数没有解析解所以用迭代逼近。标准流程是四步初始化 K 个中心、把每个样本分配给最近的中心、重新计算每个簇的中心、重复分配和更新直到中心不再明显移动或达到迭代上限。MATLAB 里实现 kmeans 有两条路。一条是统计与机器学习工具箱的kmeans函数一行调用就能出结果支持距离度量、重复次数、并行等参数另一条是自己写循环适合理解算法细节或做教学演示。实际项目里我一般先用内置函数跑通基线确认数据没问题、K 值合理再考虑要不要手写改造。内置函数底层用的是 Lloyd 算法配合 kmeans 初始化能显著降低陷入局部最优的概率。选型上要注意如果你的数据维度很高比如上百维欧氏距离会失效这时候要么先降维PCA、t-SNE要么换余弦距离。如果簇的形状不是球形kmeans 本身就不合适得考虑 DBSCAN 或谱聚类。这些边界在动手前就要想清楚否则跑出来的结果看着有模有样实际没法用。2.2 数据准备从原始表格到聚类矩阵「数据齐全」不是指文件多而是指数据能直接喂进算法。kmeans 要求输入是一个 N×D 的数值矩阵每行一个样本每列一个特征。常见的数据问题有三类缺失值、量纲不统一、类别型字段没编码。缺失值处理上我一般先看缺失比例。低于 5% 的用列均值或中位数填补高于 20% 的考虑直接删列或换特征。量纲问题更隐蔽比如一个特征是年龄0-100另一个是年收入0-1000000不标准化的话收入会完全主导距离计算年龄等于白给。标准做法是 z-score 标准化让每个特征均值为 0、标准差为 1。下面是一段数据准备代码假设原始数据存在 Excel 里包含数值列和几个类别列% 读取原始数据第一行为表头 rawData readtable(raw_data.xlsx); % 查看前几行确认列名和数据类型 head(rawData); % 分离数值特征和类别特征 numFeatures rawData(:, {Age, Income, Score, Spend}); catFeatures rawData(:, {Gender, City}); % 类别特征做独热编码转成数值 catEncoded onehotencode(catFeatures, 1:width(catFeatures)); % 合并成完整特征矩阵 featureMatrix [table2array(numFeatures), catEncoded]; % 缺失值用列中位数填补 featureMatrix fillmissing(featureMatrix, constant, ... median(featureMatrix, 1, omitnan)); % z-score 标准化每列减均值除标准差 featureMatrix zscore(featureMatrix); % 确认最终矩阵尺寸 fprintf(样本数: %d, 特征数: %d\n, size(featureMatrix, 1), size(featureMatrix, 2));这段代码的逻辑是先读表把数值列和类别列分开处理类别列用独热编码变成 0/1 向量再拼回一个大矩阵。fillmissing用列中位数填补比均值更抗异常值。zscore是标准化关键少了这一步后面聚类结果基本不可信。参数上onehotencode的第二个参数指定对哪些列编码fillmissing的constant配合中位数是常见组合。跑完看输出尺寸如果特征数和你预期对不上多半是独热编码把某一列拆成了多列。2.3 最小可跑的 kmeans 调用与参数含义数据准备好之后核心调用就一行。但这一行里的参数决定了结果好坏不能随便填% 设定聚类数 K先用肘部法粗定一个范围 K 4; % 调用 kmeans关键参数逐个说明 [idx, C, sumd, D] kmeans(featureMatrix, K, ... Distance, sqeuclidean, ... % 距离度量默认平方欧氏 Replicates, 10, ... % 重复 10 次取最优降低局部最优风险 Start, plus, ... % kmeans 初始化 MaxIter, 500, ... % 单次迭代上限 Display, final); % 只输出最终结果避免刷屏 % idx 是每个样本的簇编号C 是 K 个簇中心sumd 是簇内距离和 fprintf(各簇样本数: ); disp(histcounts(idx, 1:K1));idx是 N×1 的簇标签C是 K×D 的中心矩阵sumd是每个簇内样本到中心的距离平方和D是每个样本到所有中心的距离。Replicates设 10 是经验值数据量大或 K 大时可以加到 20代价是时间线性增长。Start用plus就是 kmeans比默认的均匀采样稳。MaxIter一般 300 到 500 够用设太小可能没收敛就停了。跑完用histcounts看各簇样本数如果某一簇只有个位数样本要么是 K 设大了要么是数据里有离群点。3. 聚类数 K 怎么定肘部法、轮廓系数与业务约束的三方博弈3.1 肘部法的计算与读图K 是 kmeans 唯一需要人为指定的关键参数也是最容易拍脑袋的地方。肘部法的思路是随着 K 增大簇内距离和必然下降但下降速度会在某个点明显变缓那个拐点就是候选 K。实现上就是循环跑不同 K记录sumd总和% 测试 K 从 1 到 10 的簇内距离和 K_range 1:10; wss zeros(length(K_range), 1); for i 1:length(K_range) [~, ~, sumd] kmeans(featureMatrix, K_range(i), ... Replicates, 5, Start, plus, Display, off); wss(i) sum(sumd); end % 画肘部图 figure; plot(K_range, wss, -o, LineWidth, 1.5); xlabel(聚类数 K); ylabel(簇内距离和); title(肘部法确定 K); grid on; % 计算相邻点的下降率辅助判断拐点 dropRate -diff(wss) ./ wss(1:end-1); disp(table(K_range(2:end), dropRate, VariableNames, {K, DropRate}));wss是 within-cluster sum of squares随 K 单调下降。看图时找下降率突然变小的位置比如从 K3 到 4 降了 30%从 4 到 5 只降了 8%那 4 就是候选。代码里额外算了dropRate比肉眼读图更客观。注意Replicates这里设 5 就够因为只是比较趋势不需要每个 K 都跑到最优。3.2 轮廓系数比肘部法更硬的指标肘部法主观性强轮廓系数silhouette能给出每个样本的聚类质量分数范围 -1 到 1越接近 1 说明样本离本簇近、离其他簇远。MATLAB 里silhouette函数直接算% 对候选 K 计算平均轮廓系数 K_candidates 2:8; silScores zeros(length(K_candidates), 1); for i 1:length(K_candidates) idx kmeans(featureMatrix, K_candidates(i), ... Replicates, 10, Start, plus, Display, off); silScores(i) mean(silhouette(featureMatrix, idx)); end % 输出对比表 disp(table(K_candidates, silScores, VariableNames, {K, Silhouette})); % 找最高分对应的 K [bestScore, bestIdx] max(silScores); fprintf(最佳 K %d, 轮廓系数 %.4f\n, K_candidates(bestIdx), bestScore);轮廓系数对距离度量敏感如果前面没做标准化这里分数会普遍偏低且不可比。一般平均轮廓系数高于 0.5 算结构清晰0.3 到 0.5 算可接受低于 0.25 就要怀疑数据本身没有明显簇结构。注意轮廓系数在 K2 时往往偏高这是它的已知偏向所以不能只看分数要结合肘部法和业务含义。3.3 业务约束下的 K 选择纯数学指标给的是候选最终定 K 还要看业务能不能用。比如做用户分群分成 3 群和 5 群对应的运营策略完全不同5 群可能细到没法针对性投放。我一般会做一张对照表把不同 K 下的簇大小、中心特征、轮廓系数列出来和业务方一起过一遍。如果某个 K 下出现一个超大簇加几个极小簇通常说明 K 偏大或者数据里有离群点没处理。这一步没有代码能替代但前面算出的C和idx就是讨论的素材。4. 结果可视化与簇特征解读让聚类结果能讲出人话4.1 二维和三维散点图的画法聚类结果如果只给一堆标签没人看得懂。可视化是让结果落地的关键一步。高维数据没法直接画常规做法是先用 PCA 降到 2 维或 3 维再按簇标签上色% PCA 降到二维用于可视化 [coeff, score, ~, ~, explained] pca(featureMatrix); score2d score(:, 1:2); % 按簇标签画散点图 figure; gscatter(score2d(:,1), score2d(:,2), idx, lines(K), ., 12); xlabel(sprintf(PC1 (%.1f%%), explained(1))); ylabel(sprintf(PC2 (%.1f%%), explained(2))); title(kmeans 聚类结果PCA 二维投影); grid on; % 叠加簇中心在 PCA 空间的投影 hold on; center2d (C - mean(featureMatrix)) * coeff(:, 1:2); plot(center2d(:,1), center2d(:,2), kx, MarkerSize, 14, LineWidth, 2); hold off;gscatter按idx分组上色比手动循环scatter省事。explained告诉你前两个主成分解释了多少方差如果加起来不到 50%说明二维投影丢失信息太多图只能当参考不能下结论。中心点投影那一步是把原始空间的C通过同样的均值和coeff变换到 PCA 空间这样中心点和样本点在同一坐标系里方便看簇的紧致程度。4.2 簇中心反标准化与特征画像PCA 图看的是整体分布要解释每个簇是什么得回到原始特征空间看中心。但前面做了 z-score中心值是标准化的得反变换回去% 保存标准化参数在 zscore 那一步之后 mu mean(featureMatrix_raw, 1); sigma std(featureMatrix_raw, 0, 1); % 反标准化簇中心 C_original C .* sigma mu; % 把中心转成表格方便对照列名 centerTable array2table(C_original, ... VariableNames, featureMatrix_colnames); disp(centerTable); % 对每个簇找出中心值最高的三个特征 for k 1:K [~, topIdx] maxk(C_original(k,:), 3); fprintf(簇 %d 主导特征: %s\n, k, strjoin(featureMatrix_colnames(topIdx), , )); end这里的关键是mu和sigma必须在标准化之前从原始矩阵算出来并保存否则反变换对不上。C_original的每一行是一个簇在原始量纲下的中心比如年龄 35、收入 8000 这样业务方一看就懂。maxk找每个簇最突出的特征快速生成画像描述。如果某个簇在多个特征上都偏高说明这个簇的特征不单一可能需要拆或者合并。4.3 用轮廓图定位问题样本整体轮廓系数是平均值掩盖了个体差异。轮廓图能把每个样本的分数画出来一眼看出哪些样本分错了figure; [silVals, ~] silhouette(featureMatrix, idx, sqeuclidean); title(各样本轮廓系数); % 找出轮廓系数为负的样本这些是可能分错的 negIdx find(silVals 0); fprintf(轮廓系数为负的样本数: %d (占比 %.1f%%)\n, ... length(negIdx), 100*length(negIdx)/length(silVals)); % 输出这些样本的原始索引和当前簇标签 if ~isempty(negIdx) disp(table(negIdx, idx(negIdx), silVals(negIdx), ... VariableNames, {SampleIndex, Cluster, Silhouette})); end轮廓系数为负意味着样本到其他簇的平均距离比到本簇还近基本可以判定分错了。占比低于 5% 可以接受高于 10% 就要回头检查 K 是否合理、特征是否够区分。这些负分样本往往是边界情况业务上可能正好是需要单独关注的那批人。5. 避坑与排查kmeans 聚类 MATLAB 实现里最容易翻车的五件事5.1 没标准化导致某列特征独大现象聚类结果里某一簇的样本在某个特征上高度一致其他特征完全随机看起来像按单一维度分的。原因不同特征量纲差异大距离计算被大量纲特征主导。解决聚类前对所有数值特征做 z-score 或 min-max 标准化并在反标准化解读中心时用对应的均值和标准差还原。判断方法很简单看簇中心表里各特征的数值范围如果某一列数值比其他列大几个数量级基本就是这个问题。5.2 K 值拍脑袋定结果没法解释现象跑出来的簇大小严重不均或者业务方问「为什么是 4 类不是 3 类」时答不上来。原因只跑了一次 kmeans没做 K 的扫描和对比。解决至少用肘部法和轮廓系数各扫一遍 K 的范围把不同 K 下的簇大小、轮廓系数、中心特征列成表选数学指标和业务含义都说得通的那个。我一般会把 2 到 8 的结果都留着业务讨论时随时调出来看。5.3 忽略 Replicates 导致结果每次不一样现象同样的数据和 K两次运行得到的簇标签和中心不同。原因kmeans 对初始中心敏感单次运行容易陷入局部最优。解决Replicates设 10 以上Start用plus。如果数据量特别大导致重复太慢可以先用sample初始化跑一次看大概再用plus加Replicates精跑。另外注意即使这样簇的编号也可能不同比较两次结果时要看中心而不是看标签数字。5.4 缺失值没处理直接进 kmeans现象代码报错NaN相关或者结果里某些样本的簇标签异常。原因kmeans不接受含 NaN 的输入矩阵。解决进 kmeans 之前必须fillmissing或rmmissing。填补方法上数值列用中位数比均值稳类别列用众数。如果某列缺失超过 30%我倾向于直接删掉这列因为填补引入的偏差可能比丢掉这列更大。5.5 把聚类结果当分类标签用现象拿 kmeans 的idx去训练一个分类器然后在新数据上预测发现效果很差。原因kmeans 给出的簇编号没有跨数据集的一致性新数据跑一遍 kmeans 得到的编号和旧数据对不上。解决如果要做预测应该用聚类中心训练一个分类器比如最近邻或 SVM把中心作为「伪标签」的来源而不是直接用编号。或者用knnsearch把新样本分配到最近的已有中心。这个坑很隐蔽因为在自己数据集上验证时看着没问题一上生产就露馅。6. 从能跑到好用kmeans 结果稳定性验证与增量分配的一个实用技巧代码能跑通只是起点真正投入使用前我会做一件事验证聚类结果的稳定性。方法不复杂把数据随机分成两半各自跑 kmeans然后比较两半得到的簇中心是否接近。如果中心差异很大说明数据本身没有稳定结构或者 K 选得不对。MATLAB 里可以用pdist2算两组中心的距离矩阵看最小距离是否在可接受范围内。% 随机对半切分 n size(featureMatrix, 1); halfIdx randperm(n, floor(n/2)); dataA featureMatrix(halfIdx, :); dataB featureMatrix(setdiff(1:n, halfIdx), :); % 各自跑 kmeans K 4; [~, CA] kmeans(dataA, K, Replicates, 10, Start, plus, Display, off); [~, CB] kmeans(dataB, K, Replicates, 10, Start, plus, Display, off); % 计算两组中心的两两距离 centerDist pdist2(CA, CB); minDist min(centerDist, [], 2); fprintf(各中心到另一组最近中心的距离: ); disp(minDist); % 如果每个中心都能在另一组找到距离小于阈值的对应中心认为稳定 threshold 0.5; % 标准化空间下的经验阈值 stable all(minDist threshold); fprintf(聚类结果稳定: %s\n, string(stable));这段代码的核心是pdist2算两组中心的距离矩阵minDist是每个 A 组中心到 B 组最近中心的距离。阈值 0.5 是在标准化空间下的经验值因为标准化后特征标准差为 1中心距离小于半个标准差算接近。如果某个中心的最小距离超过 1说明这一簇在两半数据里位置差异大要么是样本太少不稳定要么是 K 偏大。另一个实用技巧是增量分配当有新样本进来时不需要重新跑整个 kmeans直接用已有的中心做最近邻分配。knnsearch或者手动算距离都行% 新样本标准化用训练时的 mu 和 sigma newSample (newRaw - mu) ./ sigma; % 分配到最近的中心 [d, assignedCluster] min(pdist2(newSample, C), [], 2); fprintf(新样本分配到簇 %d距离 %.4f\n, assignedCluster, d);这样做的前提是新样本的分布和训练数据一致如果业务发生突变比如用户行为模式整体偏移增量分配会失效这时候需要重新聚类。我一般会监控新样本到最近中心的平均距离如果持续上升就是重新训练的触发信号。这套流程跑下来从数据准备到结果验证大概两三百行代码覆盖了 kmeans 聚类分析在 MATLAB 里落地的完整链路。我自己踩过最深的坑是早期不做标准化直接跑结果对着簇中心表看了半天没看出规律后来才发现是收入那一列把距离全吃掉了。希望帮到你。本文还有配套的精品资源点击获取
返回列表