说人话版 K-means 解析

目录

k-means人话解释!

[第一步:随便选3个"标杆学生" 📍](#第一步:随便选3个“标杆学生” 📍)

[第二步:学生们找"最近标杆" 👥](#第二步:学生们找“最近标杆” 👥)

[第三步:重新选"更好的标杆" 🔄](#第三步:重新选“更好的标杆” 🔄)

[第四步:重复排队 🔁](#第四步:重复排队 🔁)

[第五步:直到没人换组 ✅](#第五步:直到没人换组 ✅)

[用买菜来比喻 🥦](#用买菜来比喻 🥦)

为什么叫k-means?

核心思想简单说:

实际例子:

一句话总结:

什么情况下会使用k-means?

[1. 当你需要"自动分组"时 📊](#1. 当你需要“自动分组”时 📊)

[2. 当你知道要分几组时 🔢](#2. 当你知道要分几组时 🔢)

[3. 当数据量比较大时 ⚡](#3. 当数据量比较大时 ⚡)

[4. 当你的数据是"球形分布"时 ⚽](#4. 当你的数据是“球形分布”时 ⚽)


情景:你带了100个学生去操场,要按身高把他们分成3组做游戏。


k-means人话解释!

第一步:随便选3个"标杆学生" 📍

你随便挑了3个学生(A、B、C),对他们说:

"你们仨站开一点,其他人看自己离谁最近,就站到谁后面去!"

第二步:学生们找"最近标杆" 👥

学生们开始比较:

  • "我离A最近!" → 站A后面

  • "我离B最近!" → 站B后面

  • 以此类推...

最后形成了3个人堆

第三步:重新选"更好的标杆" 🔄

你发现这3个标杆位置可能不合适:

  • A后面全是高个子

  • B后面全是矮个子

  • C后面全是不高不矮的

于是你说:"现在每组里,找个最中间的人当新标杆 !"

(这个"最中间"就是计算这组人的平均身高)

第四步:重复排队 🔁

新标杆选好后,你对全班喊:

"重新排队!再看离哪个新标杆最近,就站谁后面!"

第五步:直到没人换组

这样反复几次后,大家发现:

"我站在这个组,再怎么换标杆,我还是离这个标杆最近。"

这时候分组就完成了!


用买菜来比喻 🥦

任务:把一堆大小不同的土豆分成3堆

  1. 随便拿3个土豆放桌上当"代表"

  2. 每个土豆对比:我离哪个代表最近?

  3. 形成3堆土豆

  4. 每堆里重新选个"平均大小"的土豆当新代表

  5. 重新分堆,再选新代表...

  6. 直到土豆不再换堆为止


为什么叫k-means?

  • k = 你想分几组(比如3组)

  • means = 每组的"平均值"(中心点)

核心思想简单说:

"物以类聚,人以群分"

通过不断调整"组长"位置,让组员离自己的组长最近。

实际例子:

给客户分组

  1. 想分3类客户(高价值、中价值、低价值)

  2. 随便选3个客户当"代表客户"

  3. 其他客户根据消费习惯,找最像的代表

  4. 每类客户里重新选个"典型客户"当新代表

  5. 重复直到稳定


一句话总结:

"找几个中心点,让大家跟最近的中心点抱团,然后中心点挪到团中央,重新抱团,直到团不再变。"

什么情况下会使用k-means?

1. 当你需要"自动分组"时 📊

常见场景:

  • 客户细分:把客户分成几类(高价值、普通、潜在流失)

  • 市场分析:找出相似购买习惯的人群

  • 图片压缩:把相似颜色归为一类,减少颜色种类

  • 文档归类:自动给新闻分主题(体育、财经、娱乐)

比如 :电商平台有100万用户,你不知道他们有什么特点,让k-means帮你自动发现几种典型的用户类型。

2. 当你知道要分几组时 🔢

前提条件:你心里大概有数要分多少类

  • 产品分3个档次(低、中、高端)

  • 员工按绩效分4档(A、B、C、D)

  • 城市按发展水平分5类

注意 :如果你完全不知道该分几组,要先探索(用肘部法则找最佳k值)。

3. 当数据量比较大时

k-means计算快,适合大数据:

  • 10万条以上的客户数据

  • 社交媒体的用户行为数据

  • 传感器采集的海量数据

4. 当你的数据是"球形分布"时

什么样的数据?

  • 各类数据点像"一团一团的"

  • 每团数据比较紧密

  • 团与团之间分得比较开

不适合的情况:数据像月亮、环形、交叉分布时效果不好。

复制代码
//随机生成三组坐标
//使用k-means对其进行聚类



import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

# 设置中文字体(可选)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False    # 用来正常显示负号

# 1. 随机生成三组坐标数据
np.random.seed(42)  # 设置随机种子,保证每次运行结果相同

# 生成300个点,分为3簇
n_samples = 300
n_clusters = 3

# 使用make_blobs生成聚类数据(比纯随机更好展示效果)
# 参数说明:
# n_samples: 总样本数
# centers: 中心点数量(即簇的数量)
# cluster_std: 簇的标准差(控制簇的紧密程度)
# random_state: 随机种子
X, y_true = make_blobs(n_samples=n_samples, 
                       centers=n_clusters,
                       cluster_std=0.8,  # 簇的紧密程度,值越小越紧密
                       random_state=42)

print(f"生成数据形状: {X.shape}")
print(f"前5个点的坐标:")
for i in range(5):
    print(f"  点{i+1}: ({X[i, 0]:.2f}, {X[i, 1]:.2f})")

# 2. 可视化原始数据
plt.figure(figsize=(15, 5))

# 子图1:原始数据(带真实标签)
plt.subplot(1, 3, 1)
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', s=50, alpha=0.7, edgecolors='k')
plt.title('原始数据 (3个自然簇)')
plt.xlabel('X 坐标')
plt.ylabel('Y 坐标')
plt.grid(True, alpha=0.3)

# 3. 应用K-means聚类
print("\n" + "="*50)
print("开始K-means聚类...")

# 创建K-means模型
kmeans = KMeans(n_clusters=3,       # 指定分为3簇
                init='k-means++',   # 智能初始化,避免随机性带来的问题
                n_init=10,          # 用不同初始中心运行10次,取最好结果
                max_iter=300,       # 最大迭代次数
                random_state=42)    # 随机种子

# 训练模型
kmeans.fit(X)

# 获取聚类结果
labels = kmeans.labels_            # 每个点所属的簇标签 (0, 1, 2)
centers = kmeans.cluster_centers_  # 三个簇的中心点坐标
inertia = kmeans.inertia_          # 所有点到其所属簇中心的距离平方和(越小越好)

print(f"聚类完成!")
print(f"簇中心点坐标:")
for i, center in enumerate(centers):
    print(f"  簇{i}中心: ({center[0]:.2f}, {center[1]:.2f})")
print(f"总距离平方和 (inertia): {inertia:.2f}")

# 4. 可视化聚类结果
# 子图2:聚类结果
plt.subplot(1, 3, 2)
colors = ['red', 'blue', 'green']  # 为每个簇指定颜色

# 按簇绘制点
for i in range(n_clusters):
    cluster_points = X[labels == i]
    plt.scatter(cluster_points[:, 0], cluster_points[:, 1], 
                c=colors[i], label=f'簇 {i}', s=50, alpha=0.7, edgecolors='k')

# 绘制簇中心点
plt.scatter(centers[:, 0], centers[:, 1], 
           c='black', marker='X', s=200, label='簇中心', linewidths=2)

plt.title('K-means聚类结果')
plt.xlabel('X 坐标')
plt.ylabel('Y 坐标')
plt.legend()
plt.grid(True, alpha=0.3)

# 5. 对比真实标签和预测标签
# 子图3:对比图(真实 vs 预测)
plt.subplot(1, 3, 3)

# 左侧:真实标签
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', s=50, alpha=0.7, edgecolors='k')
plt.title('真实标签 vs 聚类标签\n(左侧: 真实, 右侧: 聚类)')
plt.xlabel('X 坐标')
plt.ylabel('Y 坐标')

# 在右侧添加聚类结果的边界示意
# 计算数据的范围
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1

# 生成网格点
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
                     np.linspace(y_min, y_max, 100))

# 预测网格点的标签
Z = kmeans.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 绘制决策边界
plt.contourf(xx, yy, Z, alpha=0.1, cmap='viridis')
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=30, alpha=0.7, edgecolors='k')

plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# 6. 打印详细统计信息
print("\n" + "="*50)
print("聚类统计信息:")

# 计算每个簇的大小
unique, counts = np.unique(labels, return_counts=True)
for cluster, count in zip(unique, counts):
    cluster_points = X[labels == cluster]
    print(f"\n簇 {cluster}:")
    print(f"  样本数量: {count} ({count/n_samples*100:.1f}%)")
    print(f"  中心点: ({centers[cluster, 0]:.2f}, {centers[cluster, 1]:.2f})")
    
    # 计算簇内点到中心的平均距离
    distances = np.linalg.norm(cluster_points - centers[cluster], axis=1)
    print(f"  平均距离: {distances.mean():.2f}")
    print(f"  最大距离: {distances.max():.2f}")

# 7. 评估聚类质量
print("\n" + "="*50)
print("聚类质量评估:")

from sklearn.metrics import silhouette_score, adjusted_rand_score

# 轮廓系数(-1到1,越大越好)
sil_score = silhouette_score(X, labels)
print(f"轮廓系数 (Silhouette Score): {sil_score:.3f}")
print("  解释: >0.7(强聚类) 0.5-0.7(合理) <0.2(无意义)")

# 调整兰德指数(与真实标签比较,-1到1,越大越好)
ari_score = adjusted_rand_score(y_true, labels)
print(f"调整兰德指数 (Adjusted Rand Index): {ari_score:.3f}")
print("  解释: 与真实标签的一致性,1表示完全一致")

# 8. 简单应用:对新点进行预测
print("\n" + "="*50)
print("预测新点属于哪个簇:")

# 创建一些新测试点
test_points = np.array([[0, 0],     # 在中间
                        [4, 4],     # 可能在簇1
                        [-2, 10]])  # 可能在簇2

# 预测这些点属于哪个簇
test_labels = kmeans.predict(test_points)

print("\n新点预测结果:")
for i, (point, label) in enumerate(zip(test_points, test_labels)):
    print(f"  点 {point} → 属于 簇 {label} (颜色: {colors[label]})")
    
# 可视化新点
plt.figure(figsize=(8, 6))
for i in range(n_clusters):
    cluster_points = X[labels == i]
    plt.scatter(cluster_points[:, 0], cluster_points[:, 1], 
                c=colors[i], label=f'簇 {i}', s=50, alpha=0.3, edgecolors='k')

# 绘制簇中心
plt.scatter(centers[:, 0], centers[:, 1], 
           c='black', marker='X', s=200, label='簇中心')

# 绘制新点并突出显示
for i, point in enumerate(test_points):
    plt.scatter(point[0], point[1], 
               c=colors[test_labels[i]], s=200, 
               marker='*', edgecolors='black', linewidth=2,
               label=f'新点{i} (簇{test_labels[i]})')

plt.title('新点预测示例')
plt.xlabel('X 坐标')
plt.ylabel('Y 坐标')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
相关推荐
ASS-ASH2 小时前
机器人灵巧手:技术演进、市场格局与未来前景
人工智能·深度学习·神经网络·机器学习·计算机视觉·机器人·灵巧手
weixin_395448912 小时前
TDA4工程和tda2工程相比,数据预处理部分tda4有哪些升级?带来了什么好处,tda2原来的数据预处理有哪些坏处
人工智能·python·机器学习
小O的算法实验室2 小时前
2026年SEVC SCI2区,基于差分向量内学习策略的自适应指数交叉差分进化算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
gloomyfish2 小时前
【最新技术】多模态零样本工业缺陷检测概述
人工智能·算法·计算机视觉
渡过晚枫2 小时前
[蓝桥杯/java/算法]攻击次数
java·算法·蓝桥杯
风筝在晴天搁浅2 小时前
hot100 3.无重复字符的最长子串
数据结构·算法·leetcode
liuyao_xianhui2 小时前
寻找旋转排序数组中的最小值_优选算法(二分算法)
算法
薛不痒3 小时前
机器学习算法之决策树
人工智能·决策树·机器学习
努力学算法的蒟蒻3 小时前
day37(12.18)——leetcode面试经典150
算法·leetcode·面试