MATLAB KMeans聚类实战:异常加热数据定K与可视化
发布时间:2026/10/1 10:35:14来源:尧图网络
简介这份资源围绕KMeans聚类算法展开面向机器学习入门者与需要做故障类型分析的数据从业者帮助解决无监督场景下自动分组、聚类数目难以确定的问题。包内共2个文件含1个m脚本与1个xlsx数据表压缩包约57KB脚本用于实现聚类流程与结果可视化表格则提供异常加热片段等故障样本数据可直接作为算法输入。资源重点涉及Calinski-Harabasz指数评估最佳聚类数并结合散点图、直方图或箱线图展示各簇分布便于理解聚类紧凑性与分离性。已有4508人学习下载适合希望掌握聚类评估指标、可视化思路与故障分类实践的读者参考可快速复现从数据预处理到聚类结果解读的完整过程。1. 从一份异常加热数据说起KMeans 聚类到底能解决什么设备异常加热的片段数据拿到手第一反应往往是先分类。可故障类型到底有几类、每类的边界在哪现场没人能拍胸脯说清。这份ML_Cluster.zip就是冲着这个场景来的里面一个 MATLAB 脚本Copy_of_Cluster_Flow.m一份异常加热片段数据abnormal_heating_fragments11111.xlsx把 KMeans 聚类、最佳簇数评估和可视化串成了一条能跑的流程。它适合两类人一类是手上有一堆没打标签的工况数据、想先看看能自然分成几堆的现场工程师另一类是想拿一份能直接改的聚类可视化源码当模板的学生和转行者。核心价值不在算法本身而在于它把「簇数怎么定」和「结果怎么看」这两件最容易翻车的事用 Calinski-Harabasz 指数和散点图给落地了。2. 拆开压缩包文件结构、数据形态与运行前置条件2.1 三个文件各自扮演什么角色拿到ML_Cluster.zip先别急着解压运行花两分钟认清每个文件的定位后面调参会省很多事。文件类型在流程中的位置需要关注的点Copy_of_Cluster_Flow.mMATLAB 脚本主流程读数据、标准化、选 K、聚类、画图路径、列名、归一化方式abnormal_heating_fragments11111.xlsxExcel 数据表输入样本特征列、缺失值、量纲差异ML_Cluster.zip压缩包分发载体解压后保持同目录Copy_of_Cluster_Flow.m这个名字里的Copy_of说明它是从某个原始流程复制出来的分支版本常见做法是保留原始脚本不动、在副本上改这样出问题能回退。脚本承担的是整条流水线把 Excel 读进来、对特征做标准化、用循环试不同的 K 值、算 Calinski-Harabasz 指数挑最优、最后出散点图。数据表abnormal_heating_fragments11111.xlsx从命名看是「异常加热片段」每一行大概率是一个时间片段或一次加热事件的采样列里混着温度、时长、功率之类的数值特征。文件名里那串11111通常是导出时的批次或版本标记别当成参数。2.2 运行前必须确认的三件事MATLAB 脚本对环境和数据形态比 Python 敏感跑之前把下面三件事确认掉能挡掉一大半「一运行就报错」。第一确认 MATLAB 版本和工具箱。脚本用到readtable读 Excel、kmeans做聚类、scatter3或scatter画图这些在 Statistics and Machine Learning Toolbox 里。如果kmeans报未定义基本就是没装这个工具箱而不是代码写错了。第二确认数据列全是数值。Excel 里只要有一列是文本比如设备编号写成DEV-01readtable读进来就是 cell 或 string后面做标准化会直接崩。常见做法是读进来后先看一眼变量类型% 读取异常加热片段数据先做体检再进流程 data readtable(abnormal_heating_fragments11111.xlsx); disp(size(data)); % 看行列数确认样本量 disp(varfun(class, data, OutputFormat, cell)); % 逐列看类型 summary(data); % 看缺失值和分布readtable会自动识别表头size给出样本数和特征数varfun逐列返回类型summary一次性暴露缺失值和异常量纲。这一步的逻辑是聚类对量纲和缺失极其敏感先体检比跑完再排查便宜得多。参数上没什么可调的重点看输出里有没有cell/string列和NaN计数。第三确认工作路径。MATLAB 的当前文件夹必须同时包含.m和.xlsx否则readtable找不到文件。我一般会在脚本开头加一句cd(fileparts(mfilename(fullpath)))让脚本无论从哪启动都定位到自己所在目录省得每次手动切路径。2.3 为什么聚类前一定要标准化这是新手最容易跳过、也最容易导致结果没法解释的一步。异常加热数据里温度可能是几十到几百时长可能是几秒到几小时功率又是另一个数量级。KMeans 用欧氏距离算样本到簇心的远近量纲大的特征会单方面主导距离结果就是聚类几乎只按那一个特征分其他特征等于没参与。常见做法是 z-score 标准化把每个特征变成均值 0、标准差 1% 只对数值特征做 z-score 标准化避免量纲主导距离 X table2array(data); % 转成数值矩阵 X (X - mean(X)) ./ std(X); % 逐列标准化table2array把表转矩阵前提是 2.2 里确认过全是数值列。(X - mean(X)) ./ std(X)是逐列操作mean和std默认按列算正好对应每个特征。如果某列标准差为 0整列同一个值这里会除出Inf所以体检时看到常数列要提前删掉。标准化之后所有特征在同一尺度上距离才有意义后面 Calinski-Harabasz 指数算出来的类间类内比也才可信。3. 定 K 值用 Calinski-Harabasz 指数替代拍脑袋3.1 为什么不能直接指定聚类数KMeans 有个绕不开的前提你得先告诉它分几类。可异常加热场景里故障类型数量本来就是未知的拍脑袋定 K 等于把主观偏见塞进结果。定 K 的常见思路有两类一类是肘部法看簇内平方和随 K 下降的拐点另一类是用 Calinski-Harabasz简称 CH指数直接找最大值。这份脚本走的是 CH 路线原因是 CH 指数同时考虑了类间离散度和类内紧凑度比只看簇内平方和的肘部法更少依赖肉眼判断拐点。CH 指数的定义是类间平方和除以类内平方和再乘一个跟样本数、簇数有关的系数。直观理解类间越大、类内越小聚类分得越开越紧指数越高。所以遍历一串 K取 CH 最大的那个 K就是数据自己「投票」出来的最佳簇数。3.2 遍历 K 值并计算 CH 指数下面这段是定 K 的核心逻辑是让 K 从 2 试到 10每个 K 跑一次 KMeans记录对应的 CH 值最后取最大。% 遍历候选簇数用 CH 指数挑最优 K K_range 2:10; % 候选范围按样本量调整 ch_scores zeros(size(K_range)); % 预分配存每个 K 的 CH 值 for i 1:length(K_range) k K_range(i); rng(42); % 固定随机种子保证结果可复现 [idx, ~] kmeans(X, k, Replicates, 5); % 多次重启取最优 ch_scores(i) evalclusters(X, idx, CalinskiHarabasz); end [~, best_i] max(ch_scores); % 找 CH 最大的位置 best_k K_range(best_i); fprintf(最佳聚类数 K %dCH %.2f\n, best_k, ch_scores(best_i));逐项说明。K_range 2:10是候选范围下限取 2 是因为分 1 类没有聚类意义上限 10 是经验值样本量大可以往上加但别超过样本数的平方根量级。rng(42)固定随机种子KMeans 初始簇心是随机的不固定种子每次结果都不一样这在调参时是灾难。Replicates, 5让 KMeans 用 5 组不同初始簇心各跑一遍、取簇内平方和最小的那次能显著降低陷入局部最优的概率代价是耗时翻几倍。evalclusters是 MATLAB 自带的聚类评估函数传CalinskiHarabasz就返回 CH 值。最后max取最大 CH 对应的 K。参数怎么改样本只有几十条时K_range上限压到 5 左右Replicates可以提到 10 补偿小样本的随机性样本上万时Replicates降到 3 甚至 1 换速度因为大样本下局部最优的概率本来就低。3.3 把 CH 曲线画出来别只看一个数只打印一个best_k是不够的CH 曲线本身的形状能告诉你数据到底「可分」到什么程度。如果曲线在某个 K 处是一个尖锐的峰说明这个簇数很明确如果曲线一路平缓或者有多个接近的峰说明数据结构本身模糊这时候硬选一个 K 要谨慎。% 画出 CH 随 K 的变化辅助判断聚类结构是否清晰 figure; plot(K_range, ch_scores, -o, LineWidth, 1.5); xlabel(聚类数 K); ylabel(Calinski-Harabasz 指数); title(CH 指数随 K 的变化); grid on;plot的-o让每个点带标记方便看具体数值。看这张图的习惯峰越尖越可信峰和次高峰差距越大越可信。如果两个 K 的 CH 值只差个位数百分比我会把两个 K 都跑一遍看散点图用业务可解释性做最终裁决而不是死磕指数。4. 聚类结果可视化散点图怎么画才有信息量4.1 二维散点图与簇心标注聚类跑完拿到idx每个样本的簇标签和簇心接下来是把它画出来。二维散点图是最直接的前提是特征维度降到 2 或者只挑两个关键特征。% 用最优 K 重新聚类画二维散点图并标出簇心 rng(42); [idx, C] kmeans(X, best_k, Replicates, 5); figure; gscatter(X(:,1), X(:,2), idx); % 按簇标签着色 hold on; plot(C(:,1), C(:,2), kx, MarkerSize, 12, LineWidth, 2); % 簇心 xlabel(特征 1标准化后); ylabel(特征 2标准化后); title(sprintf(KMeans 聚类结果K%d, best_k)); legend(off); hold off;gscatter按idx自动给不同簇上不同颜色比手动循环scatter省事。C是簇心矩阵每行一个簇心plot用黑色叉号标出来MarkerSize调大让它显眼。注意坐标轴标签写「标准化后」因为画的是标准化后的X不是原始量纲这点在给别人看图时要讲清楚否则容易被误读成原始温度值。4.2 三维散点图与特征选择异常加热数据往往不止两个有意义的特征硬压到二维会丢信息。如果特征数在 3 个左右直接上三维散点图更实在。% 三维散点图展示三个特征上的簇分布 figure; scatter3(X(:,1), X(:,2), X(:,3), 30, idx, filled); xlabel(特征 1); ylabel(特征 2); zlabel(特征 3); title(sprintf(三维聚类分布K%d, best_k)); colorbar;scatter3第四个参数30是点大小第五个idx用簇标签映射颜色filled让点实心更好看。colorbar给出颜色和簇编号的对应。特征超过 3 个时常见做法是先做主成分分析降到 2 到 3 维再画或者挑方差最大的几个特征。这份脚本默认用前几个特征实际用时按业务含义挑——比如温度、升温速率、持续时间这三个通常比随便取前三列更有解释力。4.3 用箱线图看每个簇的内部特征分布散点图看的是样本在空间里的分堆箱线图看的是每个簇内部某个特征的分布两者互补。想知道「哪一类故障的温度普遍偏高」箱线图比散点图直接。% 按簇分组看关键特征在各簇内的分布差异 figure; boxplot(X(:,1), idx); xlabel(簇编号); ylabel(特征 1标准化后); title(各簇特征 1 的分布对比);boxplot(X(:,1), idx)按idx分组画特征 1 的箱线图。中位数线高低不同说明这个特征在各簇间有区分度箱子长短反映簇内离散程度。如果某个特征在所有簇的箱线图几乎重叠说明它对分类没贡献可以考虑从特征里去掉再重跑往往能让 CH 指数更干净。5. 避坑与排查聚类跑不通时先看这几条5.1 现象kmeans报错「未定义函数或变量」原因基本是没装 Statistics and Machine Learning Toolbox或者 MATLAB 版本太老。解决命令行敲ver看已安装工具箱列表没有就去装实在装不了退而求其次用kmeans的开源替代实现但 CH 指数那步evalclusters也得跟着换工作量不小优先补工具箱。5.2 现象每次运行聚类结果都不一样原因是 KMeans 随机初始化簇心没固定种子。解决在每次kmeans调用前加rng(42)数字随便定但全程统一。注意Replicates只是缓解局部最优不解决可复现性种子才是后悔药。5.3 现象CH 指数一路升高选出的 K 等于样本数原因通常是没做标准化或者特征里有接近唯一的标识列比如每行都不同的编号。K 越大类内平方和越小CH 会单调上升最后每个样本自成一类。解决先做 2.3 的标准化再把明显是 ID 的列从X里删掉重跑 CH 曲线正常应该出现一个明确的峰。5.4 现象散点图所有点挤成一团看不出分簇原因是特征量纲差异过大或者选来画图的两个特征本身区分度低。解决确认画图用的是标准化后的X如果还是挤换两个方差更大的特征或者先做主成分分析用前两个主成分画图。5.5 现象Excel 读进来列名变成Var1、Var2原因是表头行没被正确识别常见于表头前有空行或合并单元格。解决在 Excel 里把表头整理成干净的单行或者readtable时显式指定VariableNamingRule, preserve保留原始列名再不行就ReadVariableNames, true手动确认。6. 进阶把聚类流程封装成可复用函数并做稳定性验证跑通一次不算数能反复用、结果稳才算把这套源码吃透。我一般会把定 K、聚类、画图封成一个函数输入数据矩阵和候选 K 范围输出最优 K、簇标签和 CH 曲线句柄。这样换一份异常加热数据改个文件名就能复用。function [best_k, idx, C] cluster_flow(X, K_range) % 输入X 标准化后的数值矩阵K_range 候选簇数 % 输出最优 K、簇标签、簇心 ch_scores zeros(size(K_range)); for i 1:length(K_range) rng(42); [tmp_idx, ~] kmeans(X, K_range(i), Replicates, 5); ch_scores(i) evalclusters(X, tmp_idx, CalinskiHarabasz); end [~, best_i] max(ch_scores); best_k K_range(best_i); rng(42); [idx, C] kmeans(X, best_k, Replicates, 5); figure; plot(K_range, ch_scores, -o); grid on; xlabel(K); ylabel(CH 指数); end封装的关键是把rng(42)放在循环内每次kmeans之前保证每个 K 的初始条件一致CH 值之间才可比。函数返回best_k、idx、C画图留在函数里但把句柄暴露出去方便外面再叠加标注。封装完还要做稳定性验证这是很多人省掉但很值的一步。做法是换几个随机种子各跑一遍看best_k是否稳定% 换种子验证最优 K 是否稳定 seeds [1, 7, 42, 100, 2024]; ks zeros(size(seeds)); for s 1:length(seeds) rng(seeds(s)); [tmp_idx, ~] kmeans(X, 2:10, Replicates, 5); % 这里简化示意实际按单 K 循环算 CH 后取最大 end如果不同种子下best_k在 3 和 4 之间反复横跳说明数据结构本身模糊这时候别硬选把两个 K 的结果都拿给业务方看用「哪一类的故障描述更符合现场经验」来定。我吃过一次亏当时死磕 CH 最大值选了 K4结果现场反馈第 4 类其实是两类故障混在一起回头看 CH 曲线K3 和 K4 只差不到 5%。从那以后我每次定 K 都强制走一遍多种子稳定性验证CH 只当参考不当判决。希望这份源码和这套流程能帮你把手上那堆没标签的异常加热数据先理出个头绪。本文还有配套的精品资源点击获取
网站建设高端定制企业官网