news 2026/10/3 15:12:48

Python实现KMeans聚类算法:源码解析与数据集实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实现KMeans聚类算法:源码解析与数据集实战指南

简介:这份资源面向机器学习初学者与数据挖掘实践者,提供一套可直接运行的KMeans聚类算法Python实现方案,帮助读者理解从数据预处理、核心算法执行到结果可视化的完整聚类分析流程。压缩包共246个文件,约35.02MB,其中141个CSV数据集文件用于承载多组实验数据,16个Python程序文件构成算法主体,43个PNG与2个JPG图表文件呈现聚类结果,另有若干备份与说明文件,目录结构清晰、便于按模块检索。已有62人学习下载,适合作为课程作业、算法练习或聚类研究的参考范例。读者可借此掌握KMeans的编码实现思路、数据组织方式与结果呈现方法,并基于现成数据集快速复现实验、对比不同数据下的聚类效果,为后续调参与扩展打下基础。

1. 从一堆散点到 KMeans:Python 聚类源码与数据集到底该怎么落地

手上有一份客户行为表、一批传感器读数,或者几千条文本向量,老板让你「分个群看看」。这时候 KMeans 往往是第一个被想起来的算法:不用标签、调参少、跑得快。但真到写代码,问题立刻冒出来——数据要不要标准化、K 怎么定、初始点怎么选、结果怎么评估、数据集从哪来。标题里的「Python 实现 KMeans 聚类算法源码及数据集」,讲的正是这条从原始数据到可用分群的完整链路。它适合两类人:刚学完 python 语法、想找一个能跑通的项目练手的入门者;以及手上有真实业务数据、需要快速拿到基线分群结果的工程师。下面按「原理选型 → 源码实现 → 数据集处理 → 避坑 → 进阶验证」的顺序,把每一步都落到能复现的代码和参数上。

2. KMeans 的数学直觉与选型边界:为什么它仍是聚类基线

2.1 目标函数与迭代逻辑

KMeans 要解决的问题可以用一句话概括:把 n 个样本划分到 K 个簇里,让每个样本到它所属簇中心的距离平方和最小。这个量叫 SSE(Sum of Squared Errors),也叫簇内平方和:

SSE = Σ(k=1→K) Σ(x∈Ck) ||x − μk||²

其中 μk 是第 k 个簇的均值向量。算法本身是个交替优化过程:先固定中心,把每个点分配给最近的中心;再固定分配,把每个中心更新为簇内点的均值。这两步反复执行,SSE 单调不增,因此一定收敛——但收敛到的是局部最优,不是全局最优。这一点决定了后面所有关于初始化的讨论。

理解这个目标函数很关键,因为它直接解释了 KMeans 的三个隐含假设:簇是凸的、各簇大小大致均衡、各维度方差接近。数据一旦违反这些假设,比如出现环形簇、密度差异极大的簇,KMeans 就会给出反直觉的结果。这不是代码写错了,是模型本身的边界。

2.2 和 DBSCAN、层次聚类的选型对比

热搜里 dbscan 聚类算法 经常和 KMeans 一起出现,很多人纠结选哪个。我的经验是按数据形态和数据量两个维度判断:

维度KMeansDBSCAN层次聚类
是否需要预设簇数需要不需要不需要
能识别任意形状簇否是部分
对噪声点处理全部分配标记为噪声全部分配
时间复杂度O(n·K·d·迭代)O(n log n) 左右O(n²) 以上
适合数据量十万级以上万级千级以内

选型结论很直接:数据量大、簇形状接近球形、需要快速出结果,用 KMeans;数据里有明显噪声、簇形状不规则,用 DBSCAN;数据量小、想要看聚类树状结构,用层次聚类。实际项目里我一般先用 KMeans 跑一版基线,看轮廓系数和业务解释性,再决定要不要换模型。

2.3 距离度量与标准化的必要性

KMeans 默认用欧氏距离。欧氏距离对量纲极其敏感:如果一个特征是「年收入」(单位元,数值几万到几十万),另一个是「年龄」(单位岁,数值 18 到 65),那么距离几乎完全由收入决定,年龄这个维度等于白给。所以标准化不是可选项,是必做项。

常见做法是 Z-Score 标准化(减均值除标准差)或 Min-Max 归一化。我一般用 Z-Score,因为它对异常值的鲁棒性略好,且保留了分布形状。代码上就是 sklearn 的 StandardScaler。注意:标准化参数必须只在训练集上 fit,再 transform 到全部数据,否则会引入数据泄漏——虽然聚类没有严格意义上的标签泄漏,但评估时如果用全量数据 fit 标准化器,轮廓系数会偏乐观。

提示:如果特征里有类别型变量,先做独热编码再标准化;如果特征维度超过 50,建议先做 PCA 降维,否则距离计算会被大量弱相关维度稀释。

3. 用 Python 从零实现 KMeans:源码逐段拆解与 sklearn 对照

3.1 纯 NumPy 版本:核心循环只有 40 行

先给一份不依赖 sklearn 的实现,目的是让你看清每一步在做什么。这份代码可以直接复制运行:

import numpy as np def kmeans_numpy(X, k, max_iter=300, tol=1e-4, random_state=42): """ X: (n_samples, n_features) 已标准化的数据 k: 簇数量 max_iter: 最大迭代次数 tol: 中心点变化小于该阈值时提前停止 """ rng = np.random.RandomState(random_state) n_samples, n_features = X.shape # 1. 随机选 k 个样本作为初始中心 idx = rng.choice(n_samples, k, replace=False) centers = X[idx].copy() for i in range(max_iter): # 2. 分配步:计算每个点到各中心的距离,取最近 # 用 (a-b)^2 = a^2 + b^2 - 2ab 展开,避免显式双重循环 dists = np.sum(X**2, axis=1, keepdims=True) \ - 2 * X @ centers.T \ + np.sum(centers**2, axis=1) labels = np.argmin(dists, axis=1) # 3. 更新步:重新计算每个簇的均值 new_centers = np.array([ X[labels == j].mean(axis=0) if np.any(labels == j) else centers[j] # 空簇保持原中心 for j in range(k) ]) # 4. 收敛判断:中心移动量小于 tol 就停 shift = np.sum((new_centers - centers) ** 2) centers = new_centers if shift < tol: print(f"在第 {i+1} 次迭代收敛") break return labels, centers

逻辑说明:分配步用矩阵运算一次性算出所有点到所有中心的距离,比双重 for 循环快一到两个数量级。更新步里对空簇做了保护——如果某个中心没有分到任何点,保持原位而不是变成 NaN,这是很多人手写时翻车的地方。收敛判断用的是中心位移平方和,tol 设 1e-4 是经验值,数据标准化后这个量级比较合适。

参数说明:max_iter 默认 300 足够,实际数据通常 20 到 50 次就收敛;random_state 固定后结果可复现;k 需要外部指定,下一章讲怎么定。

3.2 sklearn 版本:生产环境该用的写法

手写版用来理解原理,生产环境直接用 sklearn.cluster.KMeans,它内置了 k-means++ 初始化和多轮重启:

from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score import numpy as np # 假设 X_raw 是原始特征矩阵 scaler = StandardScaler() X = scaler.fit_transform(X_raw) km = KMeans( n_clusters=4, # 簇数,需调 init='k-means++', # 初始中心选择策略 n_init=10, # 用不同随机种子跑 10 次,取 SSE 最小的 max_iter=300, tol=1e-4, random_state=42 ) labels = km.fit_predict(X) print("SSE:", km.inertia_) print("轮廓系数:", silhouette_score(X, labels))

逻辑说明:n_init=10 是 sklearn 较新版本的默认值,含义是用 10 组不同初始中心各跑一遍,保留 SSE 最小的结果。这直接缓解了 KMeans 对初始点敏感的问题。init='k-means++' 让初始中心尽量分散,比纯随机好很多。km.inertia_ 就是 SSE,用来做肘部法则。

参数说明:n_clusters 是唯一必须人工决定的参数;n_init 越大越稳但越慢,数据量大时设 5 到 10 即可;max_iter 和 tol 一般不用动。

3.3 k-means++ 初始化为什么能减少翻车

纯随机初始化有个经典失败模式:两个初始中心落在同一个真实簇里,导致这个簇被劈成两半,另一个真实簇被合并。k-means++ 的做法是:第一个中心随机选,之后每个中心以正比于「到已有中心最近距离的平方」的概率被选中。距离现有中心越远的点,越可能成为下一个中心。这样初始中心天然分散,SSE 最终结果通常比随机初始化低 10% 到 30%。

代价是多了一次距离计算,但相对于整体迭代开销可以忽略。所以除非你有特殊理由,init 永远用 k-means++。

注意:即使有 k-means++,也不能保证全局最优。如果业务对分群稳定性要求极高,把 n_init 调到 20 以上,并固定 random_state,保证每次跑出来的标签一致。

4. 数据集从哪来、怎么处理:内置数据与自定义 CSV 两条路

4.1 用 sklearn 内置数据集快速验证

刚上手时不要急着找真实数据,sklearn 自带几个适合聚类的数据集,几行代码就能加载:

from sklearn.datasets import load_iris, make_blobs from sklearn.preprocessing import StandardScaler # 路线一:真实小数据集 iris,150 条 4 维 iris = load_iris() X_iris = StandardScaler().fit_transform(iris.data) # 路线二:合成数据,可控簇数和分布 X_syn, y_true = make_blobs( n_samples=1000, centers=4, n_features=2, cluster_std=1.0, random_state=42 ) X_syn = StandardScaler().fit_transform(X_syn)

逻辑说明:iris 适合验证流程是否跑通,但它只有 3 类且特征区分度高,聚类太容易,不适合评估算法能力。make_blobs 可以指定 centers(真实簇数)、cluster_std(簇的松散程度),用来测试 KMeans 在不同难度下的表现。把 cluster_std 从 1.0 调到 3.0,你会看到轮廓系数明显下降,这就是数据难度对聚类的影响。

参数说明:n_samples 控制数据量;centers 是真实簇数,用来和预测的 K 对比;cluster_std 越大簇越重叠。

4.2 加载自定义 CSV 与缺失值处理

真实项目里数据基本是 CSV 或数据库导出的表格。加载和清洗的典型流程:

import pandas as pd import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer df = pd.read_csv('customer_data.csv') # 1. 只保留数值型特征用于聚类 num_cols = df.select_dtypes(include=[np.number]).columns.tolist() X_raw = df[num_cols].values # 2. 缺失值填充:中位数比均值更抗异常值 imputer = SimpleImputer(strategy='median') X_filled = imputer.fit_transform(X_raw) # 3. 标准化 X = StandardScaler().fit_transform(X_filled) print("处理后形状:", X.shape) print("各维度均值(应接近0):", np.round(X.mean(axis=0), 3))

逻辑说明:select_dtypes 自动筛掉文本列,避免后续报错。缺失值用中位数填充,因为很多业务指标(收入、消费额)是右偏分布,均值会被极端值拉偏。标准化后检查各维度均值是否接近 0、标准差是否接近 1,这是验证预处理是否正确的最快方式。

参数说明:strategy 可选 'mean'、'median'、'most_frequent';如果缺失比例超过 30%,建议直接删列而不是填充。

4.3 高维数据的降维预处理

当特征维度超过 50,距离计算会变得没有区分度——所有点对之间的距离都差不多,这叫维度灾难。热搜里提到的各类高光谱数据集、图像数据集就属于这种场景。常见做法是先 PCA 降到 10 到 20 维:

from sklearn.decomposition import PCA pca = PCA(n_components=0.9) # 保留 90% 方差 X_pca = pca.fit_transform(X) print("降维后维度:", X_pca.shape[1]) print("累计方差贡献:", pca.explained_variance_ratio_.sum())

逻辑说明:n_components 设成 0.9 表示自动选择能解释 90% 方差的最少主成分数,比手动指定维度更省心。降维后再跑 KMeans,速度和稳定性都会提升。注意 PCA 对量纲敏感,必须在标准化之后做。

参数说明:n_components 可以是整数(指定维度)或 0 到 1 的小数(指定方差比例);如果数据稀疏,改用 TruncatedSVD。

5. 避坑与排查:KMeans 实战中最容易翻车的 5 个点

5.1 现象:每次运行结果都不一样

原因:没有固定 random_state,且 n_init 较小时不同初始中心导致收敛到不同局部最优。

解决:设置 random_state=42(任意固定值),并把 n_init 提到 10 以上。如果业务要求标签跨批次可比,还需要保存训练好的中心点,新数据用最近中心分配,而不是重新 fit。

5.2 现象:某个簇只有一两个点,或者空簇

原因:K 设得过大,或者数据里存在远离主体的异常点,异常点自己成了一个簇。

解决:先做异常值检测(比如 3 倍标准差或 IQR 方法)剔除极端点;再用肘部法则重新评估 K。sklearn 遇到空簇会自动处理,但手写代码必须自己加保护,否则会得到 NaN 中心。

5.3 现象:轮廓系数很高但业务上没法解释

原因:轮廓系数衡量的是几何分离度,不代表分群有业务含义。高维数据里经常出现「数学上漂亮、业务上无用」的簇。

解决:把每个簇的中心点反标准化回原始量纲,看各维度均值差异。如果簇之间的差异集中在某个无关紧要的维度上,说明特征选择有问题,需要引入业务先验筛选特征。

5.4 现象:数据量到百万级后跑得极慢

原因:标准 KMeans 每轮迭代要算 n×K 个距离,n 大时内存和时间都吃不消。

解决:改用 MiniBatchKMeans,每次只用一小批样本更新中心,速度提升 10 倍以上,精度损失通常在可接受范围。batch_size 设 1000 到 10000 之间,数据量越大可以设越大。

from sklearn.cluster import MiniBatchKMeans mbk = MiniBatchKMeans( n_clusters=8, batch_size=4096, n_init=5, random_state=42 ) labels = mbk.fit_predict(X)

5.5 现象:标准化后结果反而变差了

原因:某些特征本身量纲一致且业务上重要性不同,盲目标准化抹掉了重要性差异。

解决:不要无脑标准化。先看各特征量纲是否一致,如果一致(比如都是 0 到 1 的评分),可以跳过标准化。如果确实需要标准化但想保留重要性差异,给关键特征乘一个权重系数再送进模型。

6. 定 K 与验证:肘部法则、轮廓系数和业务校验三件套

6.1 肘部法则的代码实现与读数技巧

肘部法则的思路是:K 增大时 SSE 必然下降,但下降速度会在某个点明显变缓,那个拐点就是候选 K。

import matplotlib.pyplot as plt from sklearn.cluster import KMeans sse = [] k_range = range(2, 11) for k in k_range: km = KMeans(n_clusters=k, n_init=10, random_state=42) km.fit(X) sse.append(km.inertia_) plt.plot(k_range, sse, marker='o') plt.xlabel('K') plt.ylabel('SSE') plt.title('Elbow Method') plt.show()

逻辑说明:inertia_ 就是 SSE。实际看图时,拐点往往不锐利,这时候不要死磕「最尖的那个点」,而是把拐点附近的 2 到 3 个 K 都跑一遍,结合轮廓系数和业务解释性一起判断。

参数说明:k_range 一般从 2 开始,到 10 或 sqrt(n/2) 为止;n_init 固定 10 保证可比性。

6.2 轮廓系数的计算与阈值判断

轮廓系数衡量每个点与自身簇的紧密度和与最近邻簇的分离度,取值 -1 到 1,越大越好。

from sklearn.metrics import silhouette_score for k in range(2, 8): km = KMeans(n_clusters=k, n_init=10, random_state=42) labels = km.fit_predict(X) score = silhouette_score(X, labels) print(f"K={k}, 轮廓系数={score:.4f}")

经验阈值:0.5 以上说明分群结构清晰;0.3 到 0.5 属于可接受,需要业务判断;低于 0.25 说明数据本身没有明显簇结构,强行聚类意义不大。注意轮廓系数在簇大小极不均衡时会偏低,这时候要结合其他指标。

6.3 用业务指标做最终校验

数学指标只能筛掉明显不合理的 K,最终决定要靠业务。我一般会做一张簇画像表:把每个簇的中心点反标准化,列出各特征均值,再算每个簇的样本占比。如果某个簇占比不到 5%,或者两个簇的画像几乎一样,就说明 K 偏大。反过来,如果某个簇内部业务指标方差极大,说明 K 偏小,需要拆分。

centers_original = scaler.inverse_transform(km.cluster_centers_) profile = pd.DataFrame(centers_original, columns=num_cols) profile['样本数'] = pd.Series(labels).value_counts().sort_index().values profile['占比'] = (profile['样本数'] / len(labels)).round(3) print(profile)

这张表拿给业务方看,比任何系数都有说服力。我踩过的坑是:曾经用轮廓系数选了一个 K,数学上最优,但业务方一看画像说「这两个群在我们眼里是同一类客户」,最后只能重来。所以定 K 这件事,数学给候选,业务拍板。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 15:11:28

稀疏贝叶斯DOA估计:从谱峰搜索到稀疏回归的工程实践解析

简介&#xff1a;面向无线通信、雷达与音频信号处理研究者的 Matlab 智能算法资源包&#xff0c;聚焦方向到达角&#xff08;DOA&#xff09;估计问题&#xff0c;覆盖经典 DOA、稀疏贝叶斯 DOA、投影追踪、聚类分析等方向。无需大型实验平台&#xff0c;在 Matlab 中即可完成从…

作者头像 李华
网站建设 2026/10/3 15:11:25

DDSRF双解耦控制原理与工程实践:正负序分离核心技术解析

1. 项目概述&#xff1a;为什么DDSRF双解耦是正负序分离的“定海神针” 你有没有遇到过这样的情况&#xff1a;光伏电站并网测试时&#xff0c;电网突然出现不对称短路&#xff0c;逆变器输出电流波形瞬间畸变&#xff0c;保护动作跳闸&#xff0c;可后台录波数据里根本看不出问…

作者头像 李华
网站建设 2026/10/3 15:11:21

Nginx应用与运维——Nginx HTTP模块详解(访问控制功能模块)

Nginx HTTP模块详解2、访问控制功能模块2.1、访问镜像模块2.1.1、访问镜像指令——mirror2.1.2、镜像请求体指令——mirror_request_body2.2、referer请求头控制模块2.3、连接校验模块2.4、源IP访问控制模块2.5、基本认证模块2.6、认证转发模块2.7、用户cookie模块2.8、并发连接…

作者头像 李华
网站建设 2026/10/3 15:10:41

SEO在线检测优化源码:从抓取到索引的全链路审计实践

很多人第一次接触“SEO在线检测优化源码”这类项目时&#xff0c;容易把它理解成一个简单的网页打分工具&#xff0c;跑一下给出个分数就完事。实际上&#xff0c;一套能真正帮站点“获得更高收录”的检测分析系统&#xff0c;本质是一个围绕搜索引擎抓取、索引、解析全链路的数…

作者头像 李华
网站建设 2026/10/3 15:09:34

C语言标准化流程与静态动态编译:从源码到可执行文件的完整链路

想搞明白“C语言标准化流程”和“动态编译与静态编译”&#xff0c;光会敲代码是不够的。我见过太多人能把算法题写得飞起&#xff0c;但一问他这个程序从.c文件到最终能跑起来的那个文件到底经历了什么&#xff0c;哪些部分是编译期决定的、哪些是运行期才确定的&#xff0c;他…

作者头像 李华
网站建设 2026/10/3 15:09:33

装修避坑指南:从预算到材料选购的完整资源地图

装修这件事&#xff0c;信息差就是真金白银。同样的户型&#xff0c;有人花30万装出出租屋效果&#xff0c;有人花20万就能住进杂志封面&#xff1b;同样是买瓷砖&#xff0c;有人在建材市场被当韭菜割&#xff0c;有人直接用出厂价拿货。我做了这么多年装修相关的工作&#xf…

作者头像 李华