简介:这份资源围绕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.m | MATLAB 脚本 | 主流程:读数据、标准化、选 K、聚类、画图 | 路径、列名、归一化方式 |
abnormal_heating_fragments11111.xlsx | Excel 数据表 | 输入样本 | 特征列、缺失值、量纲差异 |
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-01),readtable读进来就是 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 = %d,CH = %.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 最大值选了 K=4,结果现场反馈第 4 类其实是两类故障混在一起,回头看 CH 曲线,K=3 和 K=4 只差不到 5%。从那以后我每次定 K 都强制走一遍多种子稳定性验证,CH 只当参考,不当判决。希望这份源码和这套流程,能帮你把手上那堆没标签的异常加热数据先理出个头绪。
本文还有配套的精品资源,点击获取