KMeans聚类实战案例解析
KMeans 是一种经典的无监督学习聚类算法,能够将数据自动划分为若干个簇,使得同一簇内的数据点尽可能相似,不同簇的数据点尽可能不同。本文以啤酒属性数据为例,详细讲解如何使用 KMeans 聚类,并通过轮廓系数(Silhouette Score)来选取最佳簇数。
K-Means 的核心思想是:
随机选择 K 个点作为初始聚类中心(质心,Centroid)。
计算每个样本到各质心的距离,将样本分配到最近的质心所在的簇。
重新计算每个簇的质心(即簇内所有点的均值)。
重复步骤 2 和 3,直到簇不再变化或达到最大迭代次数。
其优化目标是最小化簇内的平方误差和(Sum of Squared Errors, SSE):
其中:
Ck :第 k 个簇
μk :第 k 个簇的质心
1. 数据准备
创建一个.txt文件
name calories sodium alcohol cost
Budweiser 144 15 4.7 0.43
Schlitz 151 19 4.9 0.43
Lowenbrau 157 15 0.9 0.48
Kronenbourg 170 7 5.2 0.73
Heineken 152 11 5.0 0.77
Old_Milwaukee 145 23 4.6 0.28
Augsberger 175 24 5.5 0.40
Srohs_Bohemian_Style 149 27 4.7 0.42
Miller_Lite 99 10 4.3 0.43
Budweiser_Light 113 8 3.7 0.40
Coors 140 18 4.6 0.44
Coors_Light 102 15 4.1 0.46
Michelob_Light 135 11 4.2 0.50
Becks 150 19 4.7 0.76
Kirin 149 6 5.0 0.79
Pabst_Extra_Light 68 15 2.3 0.38
Hamms 139 19 4.4 0.43
Heilemans_Old_Style 144 24 4.9 0.43
Olympia_Goled_Light 72 6 2.9 0.46
Schlitz_Light 97 7 4.2 0.47
首先,我们导入需要的库并读取数据。
数据存储在 data.txt
文件中
字段包含啤酒的卡路里(calories)、钠含量(sodium)、酒精度数(alcohol)和价格(cost)等指标。
import pandas as pd
from sklearn.cluster import KMeans
from sklearn import metrics
# 读取数据,使用空格作为分隔符
beer = pd.read_table("data.txt", sep=' ', encoding='utf8', engine='python')
# 选取用于聚类的特征列
x = beer[["calories","sodium","alcohol","cost"]]
2. 选择合适的聚类簇数 K
KMeans 需要预先设定簇的数量 K
,但是选择合适的 K 往往需要一定的探索。本文使用轮廓系数(Silhouette Score)来评估不同 K 值对应的聚类质量。
轮廓系数取值范围为 -1 到 1。
越接近 1 表示聚类效果越好,簇内紧密且簇间分离明显。
约为 0 表示样本可能处于两个簇的边界,聚类不明确。
趋近于 -1 表示聚类效果很差,可能样本被错误分配。
代码如下:
scores = []
for k in range(2, 10):
labels = KMeans(n_clusters=k).fit(x).labels_ # 训练模型并获取每个样本的簇标签
score = metrics.silhouette_score(x, labels) # 计算轮廓系数
scores.append(score)
print(scores)
通过这段代码,我们可以得到不同簇数下的轮廓系数列表,方便选择得分最高的 K
。
3. 绘制轮廓系数趋势图
将不同簇数对应的轮廓系数可视化,便于直观判断:
import matplotlib.pyplot as plt
plt.plot(list(range(2,10)), scores, marker='o')
plt.xlabel("Number of Clusters (K)")
plt.ylabel("Silhouette Score")
plt.title("KMeans 聚类簇数选择 - 轮廓系数曲线")
plt.show()
通过曲线图,可以看到某个 K
值对应的轮廓系数达到峰值,即为较优的聚类簇数。
可以看到 k = 2 时,轮廓系数达到峰值,即为较优的聚类簇数。
4. 以最佳 K 值进行聚类
假设轮廓系数最高时 K=2
,我们使用 K=2
对数据进行聚类,并将结果写回数据集:
km = KMeans(n_clusters=2).fit(x)
beer['cluster'] = km.labels_ # 保存每个样本的簇标签
5. 聚类结果评估
再次计算聚类结果的轮廓系数,评估最终聚类的效果:
score = metrics.silhouette_score(x, beer['cluster'])
print(f"最终聚类的轮廓系数为: {score}")
轮廓系数越接近 1 表明聚类越合理。
6. 总结与扩展
通过轮廓系数指标,自动寻找合适的聚类簇数,有效避免了盲目设定
K
。KMeans 对数值型特征表现良好,适合对啤酒数据这类属性进行划分。
若数据量较大或者维度较高,可以先做标准化或降维处理。
聚类结果可以用来发现不同类别的啤酒特征差异,辅助市场分析和产品定位。
以下是提升 K-means 算法轮廓系数的优化方法:
优化方向 | 具体措施 | 作用原理 |
---|---|---|
数据预处理优化 | 1. 标准化 / 归一化特征(Z-score/Min-Max) 2. 去除噪声和异常值(Z-score/IQR/ 孤立森林) 3. 特征选择与降维(PCA / 方差选择法) |
消除特征尺度差异,减少异常值干扰,缓解维度灾难,使距离度量更合理 |
k 值选择优化 | 1. 计算不同 k 值的轮廓系数,选择最大值对应的 k 2. 结合业务场景约束确定 k |
避免 k 过小(簇内松散)或 k 过大(簇间重叠),找到最优簇数量 |
初始质心优化 | 采用 K-means++ 初始化: 1. 随机选第一个质心 2. 按距离概率选择后续质心(距离越远概率越高) |
使初始质心更分散,减少局部最优陷阱,提升簇间分离度 |
距离度量优化 | 1. 连续型数据:欧氏距离(球形分布)、马氏距离(消除相关性) 2. 高维稀疏数据:余弦相似度 3. 离散型数据:汉明距离 / Jaccard 系数 |
选择适配数据类型的距离度量,避免相似度计算失真 |
迭代策略优化 | 1. 增加最大迭代次数(如从 100 增至 500) 2. 收紧收敛阈值(如从 1e-4 降至 1e-6) |
确保算法充分收敛,提升簇内紧凑性 |
抗噪声算法变种 | 1. K-medians(用中位数代替均值) 2. 加权 K-means(降低噪声样本权重) |
减少异常值对质心的影响,提升聚类稳定性 |
聚类后处理优化 | 1. 合并距离过近的簇(基于簇中心距离) 2. 拆分 SSE 过大的松散簇 3. 手动调整低轮廓系数样本的归属 |
优化簇结构,修正错误分配,进一步提升簇内紧凑性和簇间分离度 |