前言:学习记录
K-Means 是最经典的无监督聚类算法,不需要标签,仅依靠样本特征之间的距离,自动把相似样本划分成若干簇。本文结合啤酒真实数据集,完整复现 K‑Means 全流程:数据读取、特征标准化、肘部法则选 K、模型训练、轮廓系数评估、结果可视化,同时记录踩坑细节,适合机器学习入门学习。
环境:Python3.11 + scikit‑learn + pandas + matplotlib
一、K‑Means 算法原理
核心思想
1.预先指定聚类数量 K;
2.随机初始化 K 个聚类中心点;
3.分配:计算每个样本到 K 个中心点的欧式距离,样本划分给距离最近的中心点,形成 K 个簇;
4.更新:每个簇内部所有样本求均值,作为该簇新的聚类中心;
5.迭代重复分配‑更新,直到聚类中心几乎不再变化,达到收敛条件,算法结束。
关键概念
1.簇内误差平方和 (inertia):每个样本到所属簇中心的距离平方总和。K 越大,inertia 一定越小。肘部法则依靠 inertia 寻找拐点确定 K。
2.欧式距离:衡量样本相似度,特征量纲会直接影响距离结果,因此 K‑Means 必须做特征标准化。
3.轮廓系数:取值 -1,1,越接近 1,簇内越紧密、簇与簇之间区分越明显。
优缺点
优点:原理简单,计算速度快,适合大规模数据;
缺点:需要手动指定 K;对初始质心敏感;对离群点敏感;只能发现球形分布簇。
附件【参数 : 】
n_clusters: 类中心的个数,就是要聚成几类。【默认是8个】
init : 参初始化的方法,默认为'k-means++'
(1)'k-means++': 用一种特殊的方法选定初始质心从而能加速迭代过程的收敛.
(2) 'random': 随机从训练数据中选取初始质心。
(3) 如果传递的是一个ndarray,则应该形如 (n_clusters, n_features) 并给出初始质心。
n_init: 整形,缺省值=10
用不同的质心初始化值运行算法的次数,最终解是在inertia意义下选出的最优结果。
max_iter :
执行一次k-means算法所进行的最大迭代数。
Tol: 与inertia结合来确定收敛条件。
precompute_distances :三个可选值,'auto',True 或者 False。
预计算距离,计算速度更快但占用更多内存。
(1)'auto':如果 样本数乘以聚类数大于 12million 的话则不预计算距离。
(2)True:总是预先计算距离。
(3)False:永远不预先计算距离。
verbose :整形,默认值=0
random_state : 随机状态
copy_x: 布尔型,默认值=True
当我们precomputing distances时,将数据中心化会得到更准确的结果。如果把此参数值设为True,则原始数据不会被改变。如果是False,则会直接在原始数据 上做修改并在函数返回值时将其还原。但是在计算过程中由于有对数据均值的加减运算,所以数据返回后,原始数据和计算前可能会有细小差别。
algorithm: 'auto','full' or 'elkan'.默认为'auto'
full:采用经典的EM算法
elkan:通过使用三角不等式从而更有效,但不支持稀疏数据
auto:数据稀疏选择full模式,数据稠密选择elkan模式
附件【属性】
cluster_centers_: 一个n-clusters*n_features的矩阵,表示聚类中心的坐标
Labels_:
每个点的分类标签。
inertia_: float形
每个点到其簇的质心的距离之和。
n_iter_ : int
迭代次数。
二、数据集介绍
数据集data.txt,啤酒数据集,一共 20 条样本。
字段:
name:啤酒名称(文本,不参与聚类)
calories:热量
sodium:钠含量
alcohol:酒精度
cost:单价
三、完整代码实现
一、库导入与绘图环境配置
-
导入
matplotlib绘图库,设置后端TkAgg,解决 PyCharm 画布渲染报错;配置中文字体SimHei、负号正常显示,防止图表中文方框乱码。 -
导入
pandas用于读取、处理 txt 表格数据。 -
导入
KMeans聚类模型、StandardScaler标准化工具、silhouette_score轮廓系数评估指标。
二、数据集读取与特征筛选
-
使用
pd.read_csv读取本地data.txt,通过sep=r"\s+"匹配多空格分隔符,适配啤酒数据集原始空格格式,打印前 5 行查看原始数据。 -
筛选 4 个数值特征:热量、钠含量、酒精度、单价;啤酒名称
name为文本标识,不参与距离计算与聚类。 -
实例化标准化器
StandardScaler,对特征矩阵执行标准化,消除各特征量纲差异(热量数值上百、价格仅零点几,量级差距会干扰欧式距离计算),输出标准化后特征矩阵X_scaled。
三、肘部法则循环,确定最优聚类数 K
-
定义空列表存储每一个 K 对应的簇内误差平方和
inertia,遍历 K=2~10。 -
循环内创建临时 KMeans 模型,参数严格遵循官方 API:
-
n_clusters=k:当前循环聚类数量; -
init="k-means++":智能初始化聚类中心,避免局部最优; -
n_init=10:10 组不同初始中心运行,选取误差最小结果; -
max_iter=300:单次迭代上限;tol=0.0001:收敛阈值; -
algorithm="lloyd":标准 K-Means 算法;固定随机种子保证复现。
-
-
训练临时模型,将
inertia_(簇内总误差)存入列表。 -
绘制 K 与 inertia 的折线图并保存图片,通过曲线拐点判断最佳聚类数量,本实验拐点为 K=3。
四、正式训练 K-Means 聚类模型
-
以
n_clusters=3创建正式聚类模型,其余参数与循环内保持统一。 -
调用
fit()在标准化特征上完成迭代训练,完整 K-Means 迭代逻辑: 1)分配样本至距离最近聚类中心; 2)更新每个簇的中心点为簇内均值; 3)重复分配更新,直至中心变化小于阈值或达到最大迭代次数。 -
打印模型内置核心属性:
-
cluster_centers_:3 个聚类中心标准化坐标; -
labels_:每条啤酒样本所属类别标签(0/1/2); -
inertia_:全部样本到对应中心距离平方总和; -
n_iter_:算法实际迭代次数。
-
-
将聚类标签合并到原始数据表,打印啤酒名称与对应分组,直观查看分类结果。
五、模型效果量化评估
调用silhouette_score计算轮廓系数,取值范围 -1,1,数值越接近 1 代表簇内样本越紧凑、簇间区分越清晰,量化评价本次聚类质量。
六、聚类结果可视化
-
选取热量、酒精度两个特征作为横纵坐标绘制散点图;
-
不同颜色区分 3 个聚类,红色叉号标记聚类中心点;
-
添加坐标轴标签、图表标题、图例与网格,保存可视化图片,直观展示样本分布与聚类边界。
七、程序收尾
控制台输出提示,告知程序运行完成,已生成肘部曲线图、聚类散点图两张可视化文件。
python
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
# 1、读取本地txt数据集,空格分隔
df = pd.read_csv("data.txt", sep=r"\s+", engine="python")
print("原始数据预览:")
print(df.head())
# 选取参与聚类的数值特征
feature_cols = ["calories", "sodium", "alcohol", "cost"]
X = df[feature_cols]
# 特征标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 2、肘部法则,寻找最优K
inertia_list = []
k_range = range(2, 11)
for k in k_range:
km_temp = KMeans(
n_clusters=k,
init="k-means++",
n_init=10,
max_iter=300,
tol=0.0001,
algorithm="lloyd",
random_state=1
)
km_temp.fit(X_scaled)
inertia_list.append(km_temp.inertia_)
# 绘制肘部曲线
plt.figure(figsize=(8, 4))
plt.plot(k_range, inertia_list, "o-", color="#2E86AB")
plt.xlabel("聚类数量 K")
plt.ylabel("簇内误差平方和 Inertia")
plt.title("肘部法则选择最优K值(啤酒数据集)")
plt.grid(alpha=0.3)
plt.savefig("KMeans_啤酒_肘部曲线.png", bbox_inches="tight", dpi=120)
plt.close()
# 3、训练K‑Means模型,选定K=3
kmeans = KMeans(
n_clusters=3,
init="k-means++",
n_init=10,
max_iter=300,
tol=0.0001,
algorithm="lloyd",
random_state=1
)
kmeans.fit(X_scaled)
# 输出模型关键属性
print("\n=====KMeans模型输出结果=====")
print("聚类中心点坐标:\n", kmeans.cluster_centers_)
print("每个样本聚类标签:\n", kmeans.labels_)
print("簇内总误差inertia:", kmeans.inertia_)
print("迭代次数:", kmeans.n_iter_)
# 将聚类标签合并回原始数据表
df["cluster_label"] = kmeans.labels_
print("\n啤酒聚类分组结果:")
print(df[["name", "cluster_label"]])
# 4、轮廓系数评估聚类效果
sil_score = silhouette_score(X_scaled, kmeans.labels_)
print(f"\n轮廓系数:{sil_score:.4f}")
# 5、聚类结果可视化,选取热量、酒精度两个维度绘图
plt.figure(figsize=(8, 6))
plt.scatter(X_scaled[:, 0], X_scaled[:, 2], c=kmeans.labels_, cmap="coolwarm", alpha=0.7)
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 2], c="red", marker="X", s=200, label="聚类中心")
plt.xlabel("标准化热量 calories")
plt.ylabel("标准化酒精度 alcohol")
plt.title("啤酒KMeans聚类可视化(K=3)")
plt.legend()
plt.grid(alpha=0.3)
plt.savefig("KMeans_啤酒_聚类散点图.png", bbox_inches="tight", dpi=120)
plt.close()
print("\n程序运行结束,已生成肘部曲线、聚类散点图")
四、实验结果分析
- 肘部法则确定 K 值
观察肘部曲线,随着 K 增大,簇内误差平方和持续下降;当K=3的时候出现明显拐点。
K 大于 3 之后,inertia 下降变得平缓,增加聚类数量带来的收益很小,因此选择K=3作为本次实验聚类数。

- 轮廓系数
本次实验轮廓系数为 0.4578。
轮廓系数区间 -1,1:
接近 1:簇内样本紧密,簇间差距大,聚类效果优秀
0 左右:簇存在重叠,边界模糊
负数:聚类划分混乱
0.4578 属于中等效果。数据集只有 20 条样本,部分啤酒特征本身很接近,簇边界模糊,属于真实数据集正常现象。
- 聚类分组解读
模型自动将啤酒分为 3 类:
类别 0:低热量、低酒精度,大多属于淡啤;
类别 1:中等热量,中等酒精度;
类别 2:高热量、高酒精度啤酒。
从散点图可以看到红色 X 标记的 3 个聚类中心,不同颜色散点代表不同簇,样本围绕各自簇中心分布。

