恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
kmeans聚类算法Matlab实现指南:原理、参数与常见坑
首页
资讯中心
/
kmeans聚类算法Matlab实现指南:原理、参数与常见坑
kmeans聚类算法Matlab实现指南:原理、参数与常见坑
发布时间:2026/10/9 11:38:41
简介K均值K-means聚类算法的Matlab实现详解以单个docx文档形式打包面向需要掌握无监督学习基础、利用Matlab开展数据聚类分析的初学者与开发者。文档从算法原理入手逐步讲解kmeans函数的调用方式、基本迭代步骤、聚类结果可视化方法并附有可直接运行的示例代码及逐行解析示例以7个二维数据点演示K2聚类过程借助散点图不同颜色区分簇成员、叉号标记质心直观展示索引矩阵与质心坐标的含义。内容同时梳理了K值选择困难、对异常值敏感、初始化影响等常见局限并引入K-means、Bisecting K-means与Kernel K-means等改进思路为读者后续深入研究提供方向。资源包仅包含1个docx文件大小15KB下载后即可用Word或WPS打开阅读目前已有441人学习适合作为机器学习入门或Matlab数据分析实践的参考资料。1. 一份 kmeans 聚类算法 Matlab 代码文档为什么直接复制跑不通从某个共享盘、实验存档或者同事转发的资料里拿到一份《kmeans聚类算法matlab代码.docx》时大多数人的第一反应是打开文档、复制代码、点运行。实际结果通常是三种直接报错——比如 Undefined function kmeans能跑但画出来的聚类图完全不合预期或者跑了很久却看不出结果哪里不对。原因不难理解这类文档里贴的代码要么是不同 Matlab 版本写的要么省略了数据预处理要么默认你已经装了统计工具箱甚至作者自己都没把参数说明写全。这篇笔记不打算把文档内容重新誊一遍而是把 kmeans 聚类算法在 Matlab 里的完整落地路径讲清楚。从造一份可靠的测试数据开始到跑通内置 kmeans 函数再到手写一个循环迭代版最后落到参数设定、常见坑和结果验证。适合谁准备做聚类实验的学生、要复现论文结果的研究者以及被老板丢来一份祖传代码、得自己把它跑明白的从业者。2. kmeans 聚类的原理与 Matlab 实现路径选择2.1 迭代收敛的本质分配与更新两步循环kmeans 算法的核心不是复杂的公式推导而是一个极其朴素的交替优化过程。给定 n 个样本和预设的簇数 K算法不断重复两个动作分配阶段把每个样本划到离它最近的聚类中心更新阶段计算每个簇内所有样本的均值作为新的聚类中心。这两个动作交替执行直到聚类中心不再移动或者目标函数的变化量小于阈值或者迭代次数达到上限。目标函数通常写作所有样本到其所属簇中心的距离平方之和也就是 SSESum of Squared Errors公式上可以表示为 J Σ||x_i - μ_{c_i}||²。注意这里是「簇内距离平方和」kmeans 的每一次迭代都在直接降低这个值。这个交替过程有一个重要性质每一步更新都在降低目标函数因此理论上保证收敛。但它只保证收敛到局部极小值不保证全局最优。也就是说同一个数据集、同一个 K换一组初始中心点得到的结果可能完全不同。这不是算法写错了而是 kmeans 的固有特性具体来说它属于坐标下降法的一种对初始位置天然敏感。理解这一点才能理解为什么后面要引入 Replicates 参数、为什么推荐用 kmeans 初始化、为什么不少文档里会写「多次运行取 SSE 最优」。看代码文档之前还需要破除一个常见直觉kmeans 假设簇的形状近似凸的、密度接近并且对离群点相当敏感。如果你的业务数据不是球形分布或者包含明显离群点聚类结果会出现「某个簇只有一个点」或者「两个簇被硬切成两半」的现象。这类问题通常要靠预处理和调参解决跟算法本身写得对不对关系不大。换句话说拿到别人代码先别急着怪代码先问一句数据洗干净没有。2.2 内置 kmeans 与手写实现的取舍标准Matlab 里实现 kmeans 有两条主流路线直接调用 Statistics and Machine Learning Toolbox 提供的 kmeans 函数或者照着算法流程自己写一个循环版。这两条路的差别很明显落在实际工作中就是工具箱依赖、运行速度、可定制性和学习成本之间的权衡。下表是筛选实现路径时的基本框架对比维度内置 kmeans 函数手写循环版工具箱依赖需要 Statistics and Machine Learning Toolbox不需要原生 Matlab 即可运行初始化策略支持 plus、sample、uniform、自定义矩阵自己写 randperm 或 randi空簇处理内置多种策略例如 singleton 提示/重置需要手动写空簇判断运行速度经过编译器优化适合较大数据通常依赖 for 循环大数据下偏慢可定制性只能在参数框架内调整可以改距离公式、加约束、输出中间迭代过程学习价值黑匣子适合工程使用能看清每一步发生了什么拿到 《kmeans聚类算法matlab代码.docx》这类文档时第一步是判断作者给的哪条路线。文档里如果出现[idx,C] kmeans(X,K)这种调用格式说明它依赖工具箱如果出现大段的 for 循环加 mean 函数说明是手写版。我平时做实验的习惯是先手写一版把算法流程跑通因为能看到迭代过程中的中间变量容易定位问题确认逻辑没问题之后再换内置函数去跑正式数据图省事也图速度。手写版跑通之后一定不要丢掉它最大的价值是可以跟内置函数的结果做对比用来验证你自己实现到底有没有写错。另外必须提一句 MatLab 的向量化问题。手写版很容易写成「两层 for 循环挨个点算距离」数据量小没问题数据量一旦超过几万行跑起来能让人怀疑人生。常见做法是把「每个样本到每个中心的距离」一次性计算出来用 pdist2 或者矩阵运算完成而不是逐点计算。这是 Matlab 手写聚类代码里最重要的性能优化点后面写代码时会直接体现出来。3. 把文档里的算法落地成可运行脚本从测试数据到两种实现3.1 先造一份确定性高的测试数据拿到一份代码文档第一步不是去跑它的原始数据而是先构造一份可控的测试数据。可控的意思是你知道数据大致有几簇、簇中心大概在哪里跑完代码看一眼结果就知道算法对不对。如果一开始就上真实业务数据结果错了你也分不清是代码的问题还是数据本身就没有簇结构。这里用随机数生成三簇二维高斯数据每簇 100 个点分布有明显重叠但中心可辨。% 生成三簇二维高斯数据用于后续聚类验证 rng(42); % 固定随机种子保证每次运行生成的数据一致 X [randn(100, 2) * 0.6 [2, 2]; % 第一簇中心在 (2, 2) randn(100, 2) * 0.5 [-1, 3]; % 第二簇中心在 (-1, 3) randn(100, 2) * 0.7 [0, -2]]; % 第三簇中心在 (0, -2)这段代码的逻辑很直白rng(42)是把随机数生成器的种子固定在 42这样不管谁运行这段脚本生成的randn序列完全一致结果可复现这是聚类实验里最容易被人忽略的第一步。randn(100,2)生成 100 行 2 列的标准正态分布随机数均值 0 方差 1然后分别乘以 0.6、0.5、0.7 调整每簇的离散程度再加上不同的中心偏移[2,2]、[-1,3]、[0,-2]让三簇分布在平面上的不同位置。运行结束后X是一个 300 行 2 列的数据矩阵每一行是一个样本两列分别是 x 坐标和 y 坐标。后面所有代码都基于这份数据展开你可以替换成自己的真实数据但建议先把流程跑通再换。3.2 用内置 kmeans 跑通第一版聚类数据准备好了紧接着跑第一版聚类。内置 kmeans 的调用形式非常简洁但参数如果全部使用默认值未必能得到理想的划分。第一次运行我至少会设置三个参数Distance指定距离度量Replicates指定重复次数MaxIter指定最大迭代次数。% 用内置函数跑 kmeans得到每个样本的簇标签和最终聚类中心 [idx, C] kmeans(X, 3, ... Distance, sqeuclidean, ... % 使用欧氏距离平方结果等价于欧氏距离下的最近邻划分 Replicates, 5, ... % 用 5 组不同的随机初始中心返回 SSE 最小的一组 MaxIter, 300); % 单次迭代最大循环 300 轮 % 散点图查看聚类效果不同簇用不同颜色中心用黑色叉号标出 figure; gscatter(X(:,1), X(:,2), idx); hold on; plot(C(:,1), C(:,2), kx, MarkerSize, 12, LineWidth, 2); legend(簇1, 簇2, 簇3, 聚类中心);kmeans的第一个输出idx是 300 行 1 列的整数向量取值 1 到 3表示每个样本最终属于哪个簇第二个输出C是 3 行 2 列的矩阵每一行对应一个簇的最终中心坐标。sqeuclidean是欧氏距离的平方在最近邻划分上取平方和开根号等价所以实际效果与欧氏距离一致。Replicates设为 5 的含义是算法会随机生成 5 组初始中心分别完整迭代 5 次最终返回组内 SSE 最小的那次结果这是对抗局部最优最直接的手段。gscatter按idx分组绘制二维散点图每个簇自动分配不同颜色plot里用黑色叉号把聚类中心叠加上去。如果一切正常你会看到三簇点被清晰地分开黑叉落在每一簇的几何中心附近。需要特别说明的是Replicates的值不是越大越好。每一组重复都是完整的 kmeans 迭代过程数据量大时耗时线性上涨。一般二维小样本设 5 到 10 足够高维数据建议结合MaxIter一起考虑后续第 4 章会展开参数取舍的细节。3.3 按文档思路手写循环版 kmeans内置函数跑通了接下来做一件更见功夫的事按算法最朴素的流程手写一版。很多 docx 文档里给的就是这类代码因为它们不依赖工具箱复制到任何一台装了 Matlab 的机器上都能跑。但手写版往往省略了空簇处理和收敛判断导致实际运行时出现 NaN 或者死循环。这里给出一份相对完整的手写版本关键位置都做了处理。function [idx, C] my_kmeans(X, K, maxIter) % 手写 kmeans交替执行分配与更新两步 % 输入 % X - n x d 数据矩阵 % K - 簇个数 % maxIter - 最大迭代次数 % 输出 % idx - n x 1 每个样本所属簇编号 % C - K x d 聚类中心 [n, d] size(X); % 初始化从样本中随机选 K 个点作为初始中心 rng(42); % 固定种子结果可复现 perm randperm(n); C X(perm(1:K), :); for iter 1:maxIter % 分配阶段计算每个样本到每个中心的距离取最近中心的下标 D pdist2(X, C, squaredeuclidean); % n x K 距离矩阵 [~, idx] min(D, [], 2); % 按行取最小值下标得到簇标签 % 更新阶段每个簇内样本的均值作为新中心 C_new zeros(K, d); for k 1:K if sum(idx k) 0 % 空簇处理该簇没有分到任何样本随机重置一个中心 C_new(k, :) X(randi(n), :); else C_new(k, :) mean(X(idx k, :), 1); end end % 收敛判断中心变化量小于阈值则提前退出 if norm(C_new - C, fro) 1e-6 C C_new; break; end C C_new; end end调用方式跟内置函数对齐% 调用手写 kmeans并和内置函数结果做对比 [idx2, C2] my_kmeans(X, 3, 100); % 对比两类结果的 SSE验证手写实现是否正确 D1 pdist2(X, C, squaredeuclidean); sse_builtin sum(min(D1, [], 2)); D2 pdist2(X, C2, squaredeuclidean); sse_my sum(min(D2, [], 2)); fprintf(内置 kmeans SSE: %.4f\n手写 kmeans SSE: %.4f\n, sse_builtin, sse_my);这段代码的逻辑分三步展开。pdist2(X, C, squaredeuclidean)计算每个样本到每个中心的欧氏距离平方结果是一个 300 行 3 列的矩阵第 i 行第 k 列的值表示样本 i 到中心 k 的距离。min(D, [], 2)沿行方向取最小值第一个返回值是最小距离值第二个返回值是下标也就是样本所属的簇编号。更新阶段mean(X(idx k, :), 1)取出属于第 k 簇的所有样本按列求均值得到新的中心坐标。空簇判断是手写版最容易漏掉的关键点如果某个簇在第一次迭代就一个样本都没分到mean会计算出 NaN并且这个 NaN 会一路传播到后续所有迭代最终中心坐标全是 NaN绘图直接失败。这里的处理方式是随机从数据里选一个点作为新中心简单有效。收敛判断用norm(C_new - C, fro)计算新旧中心的 Frobenius 范数也就是所有坐标差值的平方和再开方小于 1e-6 就认为中心已经稳定提前跳出循环。末尾给出了一组对比代码把内置函数和手写版的 SSE 都算出来。SSE 更小的一方说明该次迭代找到了更好的局部最优解但如果两者相差不到 1%基本可以确认手写逻辑没问题。这一步是对照实验价值在于建立信心你的手写实现跟官方实现行为一致接下来无论怎么改距离公式、加约束都有了一个可信的基线。4. 影响聚类结果的四个关键参数与 K 值怎么定4.1 距离度量sqeuclidean、cityblock、cosine 何时选kmeans 里「距离」的定义直接决定了样本被划分到哪个簇。内置 kmeans 的Distance参数提供了多种选择最常用的有三个sqeuclidean、cityblock和cosine。sqeuclidean是默认值计算欧氏距离的平方适合数值型连续特征、簇形状接近球形分布的数据绝大多数场景用它就没问题。cityblock计算曼哈顿距离即各坐标差值的绝对值之和它对个别维度上的极端值没有那么敏感在特征维度彼此独立且存在离群点的情况下有时能得到比欧氏距离更稳健的划分。cosine计算的是余弦距离只关注方向、不关注模长专门用于文本向量、用户行为占比这类高维稀疏数据。选择距离度量不能靠拍脑袋应该先想清楚你的特征数值代表什么。比如做用户分群特征是「最近 30 天登录次数」和「累计消费金额」两者都是绝对数值用sqeuclidean合理如果特征是「各品类消费占比」加起来为 1这时两个用户一个消费 100 块一个消费 10000 块占比结构完全一样用欧氏距离会把它们分开而用cosine会认为它们高度相似。实战经验是拿不准的时候把三种距离都跑一遍分别计算轮廓系数选平均值高的那一种而不是凭感觉定。手写版里改距离度量也很简单把pdist2的距离参数从squaredeuclidean换成cityblock或cosine即可但注意cosine距离下中心点不一定是均值向量更新步骤需要考虑归一化这里不展开。4.2 初始中心与 Replicateskmeans 为什么能减少翻车初始中心的选取对 kmeans 影响极大这一点在第 2 章讲局部最优时已经提到。Matlab 内置函数里Start参数控制初始化方式最值得用的是plus对应 kmeans 算法。kmeans 的思路是第一个中心从样本中随机选之后每选一个新中心时距离已有中心越远的样本被选中的概率越高。这样做能让初始中心尽量分散降低「多个中心扎堆在同一簇里」的概率后续迭代也更容易收敛到更好的局部最优解。默认的sample是完全随机选 K 个样本简单但质量不稳定。% 使用 kmeans 初始化并重复多次取最优 [idx_plus, C_plus] kmeans(X, 3, ... Start, plus, ... % kmeans 初始化中心更分散 Replicates, 10, ... % 重复 10 次降低随机性影响 Display, final); % 每次运行结束后输出 SSE 值Replicates的含义是完整的「初始化 迭代」重复次数每次使用不同的随机初始中心独立运行最终返回 SSE 最小的一次。我在实际项目中一般先设 5看数据量和稳定性再往上调最多到 20。Display, final 会在命令行打印每次运行的 SSE方便肉眼判断结果稳定程度。如果 10 次运行跑出来的 SSE 几乎不变说明数据本身簇结构清晰如果每次相差很大说明初始中心对结果影响显著这时真正的解决方向是数据预处理、增大 Replicates 或者换距离度量。顺带说明一个操作细节Start和Replicates可以同时使用Start决定每一次重复的初始化方式Replicates决定跑多少轮。两者叠加之后随机性主要靠 Replicates 兜底kmeans 负责让每一轮的起点都相对合理。4.3 用肘部法则和轮廓系数确定 K 值K 值是指定的而不是算法算出来的这是 kmeans 最被人诟病的一点但也是实际项目里最需要认真对待的参数。确定 K 值最常见的方法是肘部法则把 K 从 1 取到 8分别计算总 SSE画成折线图。SSE 随着 K 增大必然下降但下降幅度会逐渐变小曲线出现明显拐点的位置就是「再加一个簇也解释不了太多新方差」的临界点这个拐点俗称肘部。% 用肘部法则看不同 K 下 SSE 的下降趋势 K_list 1:8; sse zeros(size(K_list)); for i 1:length(K_list) [~, C] kmeans(X, K_list(i), Replicates, 3); % 每个 K 重复 3 次 D pdist2(X, C, squaredeuclidean); sse(i) sum(min(D, [], 2)); % 所有样本到最近中心的距离平方和 end figure; plot(K_list, sse, -o, LineWidth, 1.5); xlabel(K); ylabel(SSE); grid on; title(肘部法则SSE 随 K 的变化);代码里的逻辑是每个 K 都跑一次 kmeans用pdist2计算样本到最终中心的距离矩阵min(D,[],2)取每个样本到最近中心的距离最后求和得到该 K 下的总 SSE。注意循环里 K 的取值一般从 1 开始因为 K1 时 SSE 等于所有样本到全局均值的距离平方和它是一个基准值。肘部法则的局限在于拐点不一定明显特别是真实数据往往没有清晰簇结构曲线平滑下降难以判断。这时候用轮廓系数交叉验证。轮廓系数silhouette综合评估每个样本的簇内紧密度和簇间分离度取值在 [-1, 1] 之间越接近 1 说明该样本簇内很近、簇间很远整体值取平均平均值越高说明聚类结构越清晰。% 计算不同 K 下的平均轮廓系数数值越大说明聚类质量越好 K_list 2:8; avg_s zeros(size(K_list)); for i 1:length(K_list) [idx_k, ~] kmeans(X, K_list(i), Replicates, 5); avg_s(i) mean(silhouette(X, idx_k)); end figure; plot(K_list, avg_s, -s, LineWidth, 1.5); xlabel(K); ylabel(平均轮廓系数); grid on;平均轮廓系数通常会在某个 K 值达到峰值这个峰值对应的 K 就是数据里最自然的簇数。注意轮廓系数对应 K1 没有意义所以循环从 2 开始。肘部法则看趋势轮廓系数看峰值两个方法结合使用比单看任何一个都可靠。如果两者给出的 K 不一致优先信轮廓系数因为它更直接反映的是「簇内紧、簇间散」这个聚类目标本身。5. kmeans 在 Matlab 里的常见问题与排查清单5.1 空簇导致中心变 NaN现象手写版 kmeans 运行过程中聚类中心C出现 NaN散点图直接画不出点或者内置函数报错提示Empty cluster created。原因某一轮分配阶段某个簇没有分到任何样本。这时mean(X(idx k, :), 1)的输入是空矩阵Matlab 计算空矩阵的均值返回 NaN而这个 NaN 会替换掉原来的中心后续所有距离计算全部变成 NaN彻底崩盘。空簇通常发生在数据分布极度不均匀、初始中心选在了样本稀疏区域、或者 K 值大于数据本身的结构数量时。解决手写版在更新阶段做一次计数判断sum(idx k) 0时随机重置该中心内置函数可以通过Start, plus 降低空簇概率同时结合Replicates多跑几组取 SSE 最优的结果。数据层面的预处理同样重要先用zscore标准化、剔除明显离群的孤立点能从根本上减少空簇的出现。5.2 忽略标准化量纲大的特征主导聚类现象数据有两个以上特征其中一个特征数值范围远大于其他特征比如年龄 20 到 40收入 5000 到 30000聚类结果几乎只按照收入这一个特征划分年龄完全不起作用。原因欧氏距离计算中数值大的特征贡献了绝大部分距离值小量纲特征被淹没了。kmeans 本身没有内置标准化逻辑聚类前是否做标准化完全由使用者把握。解决聚类之前对特征做 zscore 标准化% 标准化所有特征每个特征减去均值除以标准差 X_std zscore(X); [idx_z, C_z] kmeans(X_std, 3, Replicates, 5);注意zscore对每一列独立操作标准化之后每个特征的均值为 0、标准差为 1。标准化后的聚类中心C_z是在标准化空间里的坐标如果要用原始尺度解释中心点的业务含义需要反标准化回去C_raw C_z .* std(X) mean(X)这里的std(X)和mean(X)必须在标准化之前保存下来。还有一点容易被忽略如果做标准化聚类之前能画出的「两个特征散点图」只能看到原始分布的拉伸标准化之后再做gscatter才更能反映真实聚类边界。那是在标准化的四象限坐标系里观察千万别把两幅图混为一谈。5.3 没有安装统计工具箱导致 kmeans 未定义现象运行内置函数时直接报错Undefined function kmeans for input arguments of type double。原因当前 Matlab 环境没有安装 Statistics and Machine Learning Toolbox。kmeans 是统计工具箱的函数不属于 Matlab 基础模块。这种情况在换了机器、用了精简版安装包的场景下经常出现也是 docx 文档里作者没有交代环境导致的高频踩坑点。解决先确认工具箱是否存在运行ver(stats)如果返回空或报错说明确实缺失。没有工具箱时两条路一是手写循环版第 3.3 节给出的my_kmeans只依赖基础函数pdist2、mean、min、randperm这些都在基础模块里可以直接使用二是确认公司或学校是否有网络许可通过 Add-On Explorer 补装工具箱。还有一类文档里的老代码写的是EmptyAction, singleton或EmptyAction, error这些参数在新版 Matlab 里部分已经改名或移除版本迁移时同样会报参数错误。处理方法是查当前版本的 kmeans 文档把老参数替换成新写法或者干脆改用手写版绕开版本差异。5.4 同样代码两次运行结果不一样现象脚本完全一致第一次运行和第二次运行得到的簇标签或者聚类中心不同甚至簇标签编号整体互换原来标为 1 的变成 2。原因kmeans 的初始中心是随机选择随机性必然带来结果差异。簇标签编号的「互换」不是错误因为 kmeans 本身对簇的编号没有任何顺序含义同一份数据跑两次结果在数学上等价只是编号对不上。真正需要警惕的是聚类中心位置明显变化比如某一簇中心从 (2, 2) 跳到 (5, 5)说明算法陷入了完全不同的局部最优。解决在调用 kmeans 之前固定随机种子让结果可复现% 固定随机种子保证后续 kmeans 运行结果可复现 rng(123); [idx, C] kmeans(X, 3, Replicates, 5);rng(123)之后内置 kmeans 内部的随机数流也被固定多次运行结果完全一致。但要注意两点第一固定种子不等于掩盖问题如果多次运行 SSE 差异很大说明数据簇结构不清晰应该回到数据预处理或者 K 值选择上第二固定种子在不同版本的 Matlab 之间并不保证一致因为内置函数的随机数生成内部实现可能调整。所以论文或者报告里写实验条件时必须把 Matlab 版本、随机种子、Replicates 三者一起写清楚缺一不可。这也是玄学里最现实的坑你复现不了别人的结果可能别人的代码本来就没法跨版本复现。6. 用轮廓图与降维投影验证聚类质量一个收尾技巧代码跑通了、坑也填了还有一个问题容易被人忽略聚类结果到底好不好不能只靠肉眼在散点图上看。二维数据可以画图直接看四维五维甚至更高维的数据看不见这时候需要两个验证工具轮廓图和主成分投影。% 轮廓图每个样本的轮廓值负值说明可能被分错了簇 [idx_final, ~] kmeans(X_std, 3, Replicates, 5); figure; silhouette(X_std, idx_final);轮廓图输出一个横向条形图每个样本一条杠颜色按簇区分。大多数条形的长度越长越好如果出现明显的负值条形说明该样本可能更适合划分到相邻的簇。负值条形的比例超过 10%基本可以判断 K 值偏大或者距离度量不匹配。轮廓图比单一的平均轮廓系数更细致它能指出是哪些样本在拖后腿。高维数据还可以用 PCA 把维度压到二维再按聚类标签着色观察分离情况% PCA 降维后观察簇的分离情况辅助判断聚类是否有效 [coeff, score, ~, ~, explained] pca(X_std); figure; gscatter(score(:,1), score(:,2), idx_final); xlabel([PC1 ( num2str(explained(1), %.1f) %)]); ylabel([PC2 ( num2str(explained(2), %.1f) %)]);这段代码把标准化后的数据投影到主成分空间score的第一列和第二列是前两个主成分得分explained输出每个主成分解释的方差比例。如果前两个主成分能解释 70% 以上的方差且投影后的簇边界清晰聚类结果基本可信。如果投影后簇混成一团也不一定代表聚类失败可能是前两个主成分只解释了少部分方差信息损失太大这种情况可以尝试 t-SNE 做非线性降维再看。做聚类实验这些年我最大的教训是任何聚类脚本的第一行必须先固定随机种子任何结果图旁边都要同时标注 SSE 和平均轮廓系数任何对比实验都必须写明距离度量和 Replicates 的取值。以前我拿到一份代码文档复制运行看到一张还算正常的散点图就直接写进报告结果换一台机器重跑簇标签完全变样整份报表作废重来。后来养成上面这三个习惯再没出过这种问题。希望帮到你。本文还有配套的精品资源点击获取