k-means代码

一、实操目标

  1. 掌握K-means聚类的核心流程:手动造数据→模型训练→参数调优→结果可视化。
  2. 理解关键参数(聚类数k、初始中心、迭代次数)对聚类效果的影响。
  3. 学会用轮廓系数、肘部法则评估聚类质量,独立排查常见问题。

二、环境准备

1. 安装基础环境

  • 安装Python 3.8及以上版本(官网:https://www.python.org/)。
  • 打开命令行,执行以下命令安装依赖库:
pip install numpy pandas scikit-learn matplotlib

2. 环境验证

运行以下代码,无报错则环境正常:

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
print("环境配置成功!")

三、完整代码(含调参模块)

# 1. 库导入:数据处理、聚类模型、可视化工具
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
import matplotlib.pyplot as plt

# 2. 可视化配置:解决中文乱码、图表不显示问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']  # 中文支持
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
plt.rcParams['backend'] = 'TkAgg'  # 独立渲染后端,避免图表不显示

# ===================== 核心模块1:手动创建数据集(学生可自定义)=====================
np.random.seed(42)  # 固定随机种子,保证结果可复现
n_samples = 500  # 总样本数(可修改:如300、1000)

# 手动构造4个不同特征分布的簇(特征:消费金额、每月光顾次数、优惠券使用次数)
# 簇1:低消费、低频、少用券
cluster1 = {
    '消费金额(元)': np.random.normal(80, 20, size=int(n_samples/4)),
    '每月光顾次数(次)': np.random.normal(4, 1.5, size=int(n_samples/4)),
    '优惠券使用次数(次)': np.random.normal(1, 0.8, size=int(n_samples/4))
}
# 簇2:中消费、中频、常用券
cluster2 = {
    '消费金额(元)': np.random.normal(250, 40, size=int(n_samples/4)),
    '每月光顾次数(次)': np.random.normal(10, 2, size=int(n_samples/4)),
    '优惠券使用次数(次)': np.random.normal(5, 1.2, size=int(n_samples/4))
}
# 簇3:高消费、高频、少用券
cluster3 = {
    '消费金额(元)': np.random.normal(600, 80, size=int(n_samples/4)),
    '每月光顾次数(次)': np.random.normal(18, 3, size=int(n_samples/4)),
    '优惠券使用次数(次)': np.random.normal(2, 1, size=int(n_samples/4))
}
# 簇4:中高消费、低频、常用券
cluster4 = {
    '消费金额(元)': np.random.normal(400, 60, size=n_samples - 3*int(n_samples/4)),
    '每月光顾次数(次)': np.random.normal(6, 2, size=n_samples - 3*int(n_samples/4)),
    '优惠券使用次数(次)': np.random.normal(7, 1.5, size=n_samples - 3*int(n_samples/4))
}

# 合并为DataFrame(学生可添加新特征:如"年龄"、"会员等级")
df = pd.concat([
    pd.DataFrame(cluster1),
    pd.DataFrame(cluster2),
    pd.DataFrame(cluster3),
    pd.DataFrame(cluster4)
], ignore_index=True)

# ===================== 核心模块2:K-means参数调优(重点实操)=====================
# 待调参数说明(学生可修改以下参数观察效果)
k = 4  # 聚类数量(关键参数)
init_method = 'k-means++'  # 初始中心选择:'k-means++'(推荐)或 'random'(随机)
max_iter = 300  # 最大迭代次数(默认300,数据复杂可增大)
tol = 1e-4  # 收敛阈值(默认1e-4,值越小收敛越严格)

# 提取特征矩阵(学生可调整参与聚类的特征)
X = df[['消费金额(元)', '每月光顾次数(次)', '优惠券使用次数(次)']]

# 初始化并训练K-means模型
kmeans = KMeans(
    n_clusters=k,
    init=init_method,
    max_iter=max_iter,
    tol=tol,
    random_state=42,
    n_init=10  # 多次初始化取最优结果(默认10)
)
df['聚类标签'] = kmeans.fit_predict(X)  # 训练模型并为每个样本分配聚类标签
centroids = kmeans.cluster_centers_  # 获取最终簇中心坐标
inertia = kmeans.inertia_  # 簇内平方和(WCSS,用于肘部法则)

# 聚类效果评估指标
silhouette_avg = silhouette_score(X, df['聚类标签'])  # 轮廓系数(-1~1,越接近1越好)

# ===================== 核心模块3:结果输出与解读 =====================
print("="*60)
print("K-means聚类实操结果")
print("="*60)
print("1. 数据集预览(前5行,含聚类标签):")
print(df.head())
print("\n2. 调参配置:")
print(f"   - 聚类数量k:{k}")
print(f"   - 初始中心方法:{init_method}")
print(f"   - 最大迭代次数:{max_iter}")
print(f"   - 收敛阈值:{tol}")
print("\n3. 聚类核心结果:")
print(f"   - 最终簇中心坐标:\n{centroids.round(2)}")
print(f"   - 簇内平方和(WCSS):{inertia:.2f}")
print(f"   - 轮廓系数(聚类效果):{silhouette_avg:.3f}")
print("\n4. 各簇样本数量分布:")
cluster_counts = df['聚类标签'].value_counts().sort_index()
for label, count in cluster_counts.items():
    print(f"   - 簇{label+1}{count}个样本")

# ===================== 核心模块4:可视化图表(直观展示结果)=====================
plt.figure(figsize=(15, 10))

# 子图1:聚类结果散点图(消费金额 vs 每月光顾次数)
plt.subplot(2, 2, 1)
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57', '#FF9FF3']  # 颜色库
for label in range(k):
    cluster_data = df[df['聚类标签'] == label]
    plt.scatter(
        cluster_data['消费金额(元)'],
        cluster_data['每月光顾次数(次)'],
        c=colors[label % len(colors)],
        label=f'簇{label+1}',
        alpha=0.7,
        s=60,
        edgecolors='black',
        linewidth=0.5
    )
# 绘制簇中心(红色菱形标记)
plt.scatter(
    centroids[:, 0], centroids[:, 1],
    c='red', marker='D', s=200, edgecolors='black', linewidth=2, label='簇中心'
)
plt.title('聚类结果:消费金额 vs 每月光顾次数', fontsize=12, pad=15)
plt.xlabel('消费金额(元)', fontsize=10)
plt.ylabel('每月光顾次数(次)', fontsize=10)
plt.legend(fontsize=9)
plt.grid(True, alpha=0.3)

# 子图2:聚类结果散点图(消费金额 vs 优惠券使用次数)
plt.subplot(2, 2, 2)
for label in range(k):
    cluster_data = df[df['聚类标签'] == label]
    plt.scatter(
        cluster_data['消费金额(元)'],
        cluster_data['优惠券使用次数(次)'],
        c=colors[label % len(colors)],
        label=f'簇{label+1}',
        alpha=0.7,
        s=60,
        edgecolors='black',
        linewidth=0.5
    )
plt.scatter(
    centroids[:, 0], centroids[:, 2],
    c='red', marker='D', s=200, edgecolors='black', linewidth=2, label='簇中心'
)
plt.title('聚类结果:消费金额 vs 优惠券使用次数', fontsize=12, pad=15)
plt.xlabel('消费金额(元)', fontsize=10)
plt.ylabel('优惠券使用次数(次)', fontsize=10)
plt.legend(fontsize=9)
plt.grid(True, alpha=0.3)

# 子图3:各簇样本数量分布柱状图
plt.subplot(2, 2, 3)
bars = plt.bar(
    [f'簇{label+1}' for label in cluster_counts.index],
    cluster_counts.values,
    color=colors[:k],
    edgecolor='black',
    alpha=0.8
)
# 添加数值标签
for bar in bars:
    height = bar.get_height()
    plt.text(
        bar.get_x() + bar.get_width()/2., height + 2,
        f'{int(height)}', ha='center', va='bottom', fontsize=10, fontweight='bold'
    )
plt.title('各簇样本数量分布', fontsize=12, pad=15)
plt.xlabel('聚类簇', fontsize=10)
plt.ylabel('样本数量', fontsize=10)
plt.ylim(0, max(cluster_counts.values) * 1.1)
plt.grid(True, alpha=0.3, axis='y')

# 子图4:肘部法则图(辅助选择最优k)
plt.subplot(2, 2, 4)
k_range = range(2, 11)  # 测试k=2到10的效果
wcss_list = []
for k_test in k_range:
    kmeans_test = KMeans(n_clusters=k_test, init='k-means++', random_state=42)
    kmeans_test.fit(X)
    wcss_list.append(kmeans_test.inertia_)
# 绘制肘部图
plt.plot(k_range, wcss_list, 'o-', color='#FF6B6B', linewidth=2, markersize=8)
plt.axvline(x=k, color='red', linestyle='--', alpha=0.7, label=f'当前k={k}')
plt.title('肘部法则(最优k值选择)', fontsize=12, pad=15)
plt.xlabel('聚类数量k', fontsize=10)
plt.ylabel('簇内平方和(WCSS)', fontsize=10)
plt.xticks(k_range)
plt.legend(fontsize=9)
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show(block=True)  # 阻塞程序,直到关闭图表

四、代码详细解析

1. 库导入与环境配置

  • numpy/pandas:用于生成模拟数据和数据处理。
  • KMeans:sklearn中的K-means核心模型,包含完整的聚类逻辑。
  • silhouette_score:轮廓系数,用于量化聚类效果。
  • matplotlib:可视化工具,配置中文显示和独立渲染后端,避免图表不显示。

2. 手动创建数据集(关键实操点)

  • 采用np.random.normal生成符合正态分布的簇数据,模拟真实消费行为。
  • 学生可自定义修改:
    • n_samples:调整样本总数(如改为1000,测试大数据量聚类效果)。
    • 新增特征:如添加'年龄'列,修改cluster1-cluster4的字典结构,同步更新X的特征列表。
    • 簇分布参数:改变np.random.normal的均值(中心位置)和标准差(离散程度),模拟不同密度的簇。

3. 核心参数调优解析(重点)

参数名作用可调范围调优建议
n_clusters(k)聚类数量(核心参数)2~10(常用)用肘部法则+轮廓系数选择:肘部图中"拐点"对应的k,且轮廓系数接近1
init初始中心选择'k-means++'/'random'优先用'k-means++'(避免局部最优),对比'random'观察差异
max_iter最大迭代次数100~1000数据离散时增大(如500),收敛慢时调整
tol收敛阈值1e-5~1e-3要求严格收敛则设为1e-5,追求速度设为1e-3
n_init多次初始化次数5~20数据复杂时增大(如15),提升结果稳定性

4. 结果解读方法

  • 簇中心坐标:每个簇的特征均值,如簇1的消费金额(元)=82.3,代表该簇用户的平均消费水平。
  • 簇内平方和(WCSS):簇内数据点到中心的距离平方和,值越小说明簇内越紧凑。
  • 轮廓系数:-1(聚类极差)~1(聚类极佳),0.5以上为良好,0.7以上为优秀。
  • 样本数量分布:若某簇样本极少(如<总样本的5%),可能是k值过大或数据分布异常。

5. 可视化图表解读

  • 子图1-2:散点图展示不同特征维度的聚类分布,簇中心(红色菱形)直观反映簇的核心位置。
  • 子图3:柱状图展示各簇样本数量,判断聚类是否均衡。
  • 子图4:肘部图辅助选择k值,当k增大到某一值后WCSS下降幅度骤减,该点即为"肘部"。

五、调参实操步骤(必做)

  1. 固定其他参数,调整k值

    • 分别设k=2、3、4、5、6,记录每次的轮廓系数和WCSS。
    • 绘制肘部图,找到"拐点"k(如k=4),对比轮廓系数,确定最优k。
  2. 固定k=最优值,调整初始中心方法

    • 分别用init='k-means++'init='random',运行5次,观察轮廓系数的稳定性。
    • 结论:k-means++的结果更稳定,避免局部最优。
  3. 调整max_iter和tol

    • 设max_iter=100、300、500,观察迭代次数对结果的影响(收敛后迭代次数无意义)。
    • 设tol=1e-3、1e-4、1e-5,观察收敛阈值对WCSS和运行时间的影响。
  4. 修改特征维度

    • 去掉'优惠券使用次数(次)',仅用2个特征聚类,对比3个特征的轮廓系数。
    • 新增'年龄'特征,观察聚类结果是否更贴合真实分类逻辑。

六、常见问题排查

问题现象解决方法
中文乱码检查是否配置plt.rcParams['font.sans-serif'],更换字体(如'Microsoft YaHei'
图表不显示确保配置plt.rcParams['backend'] = 'TkAgg',或更换编辑器(如IDLE、PyCharm)
聚类结果混乱可能是k值不合适,用肘部法则重新选k;或特征维度差异大(如金额是100级,次数是10级),需对特征归一化
轮廓系数极低(<0)聚类数量k过大,或数据本身不适合聚类(如无明显簇结构),减少k值重新尝试

七、拓展练习

  1. 对特征做归一化处理(from sklearn.preprocessing import StandardScaler),对比归一化前后的聚类效果。
  2. 手动添加10个离群点(如消费金额=2000元、光顾次数=1次),观察离群点对簇中心和聚类结果的影响。
  3. 实现K-means++和随机初始化的对比实验,统计10次运行的轮廓系数均值,验证k-means++的优势。

参考结果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

更多推荐