Python实战:用sklearn的KMeans算法快速找到数据集的质心(附完整代码)
Python实战:用sklearn的KMeans算法快速找到数据集的质心(附完整代码)
当你面对一堆杂乱无章的数据点,想要找出其中的规律和分组时,KMeans算法就像一位经验丰富的侦探,能帮你快速锁定每个群组的核心位置——质心。今天我们就来聊聊这个在数据分析领域广受欢迎的聚类算法,以及如何用Python的sklearn库轻松实现它。
想象一下,你是一家电商的数据分析师,手上有成千上万用户的购物行为数据。你想把这些用户分成几个群体,以便针对不同群体制定精准的营销策略。KMeans算法正是解决这类问题的利器,它能自动将相似的用户聚在一起,并告诉你每个群体的"典型代表"——质心在哪里。
1. KMeans算法核心原理
KMeans算法的核心思想简单而优雅:通过迭代寻找数据集中K个簇的最佳质心,使得每个数据点到其所属簇质心的距离平方和最小。这个距离平方和在算法中被称为Inertia(惯性),是衡量聚类效果的重要指标。
算法的工作流程可以分为以下几个关键步骤:
- 初始化质心:随机选择K个数据点作为初始质心
- 分配数据点:将每个数据点分配到距离最近的质心所在的簇
- 更新质心:重新计算每个簇中所有点的均值作为新质心
- 迭代优化:重复步骤2和3,直到质心不再显著变化或达到最大迭代次数
在数学上,KMeans试图最小化以下目标函数:
J = Σ(每个点到其质心的距离²)这个优化过程实际上是一个**期望最大化(EM)**算法的特例。E步(期望步)对应数据点的分配,M步(最大化步)对应质心的更新。
有趣的是,虽然KMeans算法看起来简单,但它与高斯混合模型(GMM)有着深刻的联系——可以看作是GMM在各向同性协方差和硬分配情况下的特例。
2. 环境准备与数据加载
在开始编码前,我们需要确保环境配置正确。以下是推荐的Python库版本:
# 必需库及推荐版本 import numpy as np # 1.21.0+ import pandas as pd # 1.3.0+ import matplotlib.pyplot as plt # 3.4.0+ from sklearn.cluster import KMeans # 0.24.0+ from sklearn.datasets import make_blobs # 用于生成示例数据为了演示KMeans的效果,我们先创建一个模拟数据集。make_blobs函数可以生成适合聚类的各向同性高斯斑点:
# 生成模拟数据 X, y = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42) # 可视化原始数据 plt.scatter(X[:, 0], X[:, 1], s=50) plt.title("原始未聚类数据") plt.show()这个数据集有300个样本点,分布在4个中心周围,每个簇的标准差为0.6。可视化后,你应该能看到4个相对明显的点群。
提示:在实际项目中,数据预处理(如标准化)对KMeans效果至关重要。不同量纲的特征会导致距离计算偏差。
3. 完整KMeans实现步骤
现在让我们进入核心部分——使用sklearn的KMeans实现聚类分析。以下是完整的代码实现:
# 初始化KMeans模型 kmeans = KMeans(n_clusters=4, # 设定聚类数量 init='k-means++', # 智能初始化方法 n_init=10, # 用不同质心种子运行算法的次数 max_iter=300, # 最大迭代次数 random_state=42) # 拟合模型并预测簇标签 predicted_labels = kmeans.fit_predict(X) # 获取质心坐标 centroids = kmeans.cluster_centers_ # 可视化聚类结果 plt.scatter(X[:, 0], X[:, 1], c=predicted_labels, s=50, cmap='viridis') plt.scatter(centroids[:, 0], centroids[:, 1], c='red', s=200, alpha=0.8, marker='X') plt.title("KMeans聚类结果与质心位置") plt.show()这段代码做了以下几件事:
- 创建KMeans实例,设置关键参数
- 拟合数据并预测每个点的簇标签
- 提取计算得到的质心坐标
- 可视化展示聚类结果和质心位置
关键参数说明:
n_clusters:这是K值,即你希望数据被分成多少类init:初始化方法,'k-means++'能有效避免随机初始化的不良结果n_init:为避免局部最优,算法会运行多次并选择最佳结果max_iter:单次运行的最大迭代次数
4. 结果分析与优化技巧
得到聚类结果后,我们需要评估其质量并思考如何改进。以下是几个关键分析点:
4.1 评估聚类效果
除了直观的可视化检查,我们可以用一些量化指标评估聚类质量:
# 计算Inertia(簇内平方和) inertia = kmeans.inertia_ print(f"簇内平方和(Inertia): {inertia:.2f}") # 轮廓系数(需要真实标签或评估聚类紧密度) from sklearn.metrics import silhouette_score silhouette_avg = silhouette_score(X, predicted_labels) print(f"平均轮廓系数: {silhouette_avg:.2f}")轮廓系数介于-1到1之间,值越大表示聚类效果越好。一般来说:
0.5:聚类结构强
- 0.25-0.5:中等结构
- <0.25:弱结构
4.2 如何选择最佳K值
KMeans需要预先指定K值,但现实中我们往往不知道数据应该分成多少类。以下是两种常用方法:
肘部法则(Elbow Method):
# 尝试不同的K值计算Inertia inertias = [] K_range = range(1, 10) for k in K_range: kmeans = KMeans(n_clusters=k, random_state=42).fit(X) inertias.append(kmeans.inertia_) # 绘制肘部曲线 plt.plot(K_range, inertias, 'bx-') plt.xlabel('K值') plt.ylabel('Inertia') plt.title('肘部法则确定最佳K值') plt.show()寻找曲线拐点(肘部)对应的K值。当增加K值带来的Inertia下降变得不显著时,就是合适的K值。
轮廓系数法:
# 计算不同K值的轮廓系数 silhouette_scores = [] for k in range(2, 10): kmeans = KMeans(n_clusters=k, random_state=42).fit(X) score = silhouette_score(X, kmeans.labels_) silhouette_scores.append(score) # 绘制轮廓系数曲线 plt.plot(range(2, 10), silhouette_scores, 'bx-') plt.xlabel('K值') plt.ylabel('轮廓系数') plt.title('轮廓系数法确定最佳K值') plt.show()选择轮廓系数最大的K值作为最佳聚类数。
4.3 常见问题与解决方案
在实际应用中,你可能会遇到以下挑战:
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 聚类结果不稳定 | 随机初始化导致局部最优 | 增加n_init值,使用k-means++初始化 |
| 聚类效果差 | 特征尺度不一致 | 数据标准化(StandardScaler) |
| 计算速度慢 | 数据量太大 | 使用MiniBatchKMeans,减少max_iter |
| 质心位置不合理 | K值选择不当 | 用肘部法则或轮廓系数重新选择K |
5. 高级应用与实战技巧
掌握了基础用法后,让我们看几个进阶应用场景:
5.1 处理非球形簇
标准KMeans假设簇是凸形的且各向同性,这在现实中不一定成立。对于复杂形状的数据,可以尝试:
from sklearn.cluster import SpectralClustering # 谱聚类更适合非凸形状 model = SpectralClustering(n_clusters=4, affinity='nearest_neighbors') labels = model.fit_predict(X)5.2 图像颜色量化
KMeans可用于图像压缩,通过减少颜色数量:
from sklearn.utils import shuffle from PIL import Image # 加载图片并预处理 image = Image.open('photo.jpg') image = np.array(image, dtype=np.float64) / 255 w, h, d = image.shape image_array = np.reshape(image, (w * h, d)) # 用KMeans找到主要颜色 kmeans = KMeans(n_clusters=16, random_state=42).fit(image_array) compressed_image = kmeans.cluster_centers_[kmeans.labels_] compressed_image = np.reshape(compressed_image, (w, h, d)) # 显示压缩前后对比 fig, (ax1, ax2) = plt.subplots(1, 2) ax1.imshow(image) ax2.imshow(compressed_image) plt.show()5.3 文本聚类
结合TF-IDF向量化,KMeans可以对文档进行主题聚类:
from sklearn.feature_extraction.text import TfidfVectorizer documents = ["机器学习算法介绍", "Python数据分析教程", "深度学习框架比较", "大数据处理技术"] # 文本向量化 vectorizer = TfidfVectorizer() X = vectorizer.fit_transform(documents) # 文本聚类 kmeans = KMeans(n_clusters=2, random_state=42).fit(X) print("文档聚类结果:", kmeans.labels_)6. 性能优化与大规模数据处理
当数据量达到百万级别时,标准KMeans可能遇到性能瓶颈。以下是几种优化策略:
MiniBatch KMeans:
from sklearn.cluster import MiniBatchKMeans # 使用数据子集进行迭代,适合大数据集 minibatch_kmeans = MiniBatchKMeans(n_clusters=4, batch_size=100) minibatch_kmeans.fit(X)并行计算:
# 利用多核CPU加速 kmeans = KMeans(n_clusters=4, n_init=10, n_jobs=-1) # n_jobs=-1使用所有核心内存映射: 对于无法全部加载到内存的超大数据集,可以使用joblib.Memory进行内存映射:
from sklearn.utils import Memory mem = Memory(location='/tmp') kmeans_memoized = mem.cache(KMeans)(n_clusters=4) kmeans_memoized.fit(X)在实际项目中,我发现k-means++初始化配合适当的数据预处理(如标准化)能显著提升聚类效果。对于高维数据,先使用PCA降维再进行聚类往往能得到更稳定的结果。
