#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
【项目背景】
聚类是无监督机器学习的核心任务,用于将无标签数据划分为具有相似特征的簇。KMeans是工业界最常用的聚类算法之一,
其核心思想是通过迭代寻找k个聚类中心,使每个样本到其最近聚类中心的距离平方和最小。但KMeans存在显著局限性:
1. 假设数据簇呈球形分布、各维度方差相等,对旋转后的数据聚类效果下降;
2. 对不同簇的方差差异敏感,方差不等时聚类准确性降低;
3. 对簇内样本数量不均衡的情况适应性差。

本项目通过构建多种特征的模拟数据集(标准分布、旋转后、方差不等、样本数量不等),对比原始数据标签与聚类结果,
验证KMeans(代码中实际替换为高斯混合模型GMM做对比验证)在不同数据分布下的聚类表现,揭示KMeans的适用场景与局限性,
为实际业务中聚类算法的选择提供实验依据。

【解决问题的方案】
1. 数据生成:使用sklearn生成4类二维模拟数据,包含标准分布、旋转后、方差不等、样本数量不等四种场景;
2. 聚类建模:分别使用KMeans(注释中保留对比项)和高斯混合模型(GaussianMixture)对不同数据集聚类;
3. 可视化分析:通过matplotlib绘制原始数据分布和聚类结果,直观对比不同数据分布下的聚类效果;
4. 结论验证:验证KMeans对数据分布的敏感性,以及GMM作为更灵活的聚类算法的优势。
"""

# 导入数值计算库,用于数据处理和矩阵运算
import numpy as np
# 导入绘图库,用于数据可视化
import matplotlib.pyplot as plt
# 导入sklearn的数据集生成模块,用于创建模拟聚类数据
import sklearn.datasets as ds
# 导入matplotlib的颜色处理模块,用于自定义颜色映射
import matplotlib.colors
# 导入KMeans聚类算法(核心对比算法)
from sklearn.cluster import KMeans
# 导入MiniBatchKMeans(批量版KMeans,本代码未实际使用,保留导入)
from sklearn.cluster import MiniBatchKMeans
# 导入高斯混合模型(GMM),作为对比KMeans的更灵活聚类算法
from sklearn.mixture import GaussianMixture


def expand(a, b):
    """
    扩展坐标轴范围的辅助函数,避免绘图时数据点紧贴坐标轴边缘
    参数:
        a: 坐标轴最小值
        b: 坐标轴最大值
    返回:
        扩展后的最小值和最大值(向两侧各扩展10%的区间)
    """
    # 计算当前区间的10%长度
    d = (b - a) * 0.1
    # 扩展最小值和最大值并返回
    return a-d, b+d


if __name__ == "__main__":
    # ===================== 1. 生成多种特征的模拟数据集 =====================
    # 总样本数量
    N = 400
    # 聚类中心数量(固定为4类)
    centers = 4
    
    # 生成标准分布的聚类数据:4个簇,二维特征,随机种子固定保证可复现
    # data: 特征数据(400x2),y: 真实聚类标签(400x1)
    data, y = ds.make_blobs(N, n_features=2, centers=centers, random_state=2)
    
    # 生成方差不等的聚类数据:4个簇,每个簇的标准差分别为1,2.5,0.5,2
    # 用于验证KMeans对方差敏感的问题
    data2, y2 = ds.make_blobs(N, n_features=2, centers=centers, cluster_std=(1, 2.5, 0.5, 2), random_state=2)
    
    # 生成样本数量不等的聚类数据:
    # 第0类取全部100个样本,第1类取50个,第2类取20个,第3类取5个
    # 用于验证KMeans对样本数量不均衡的适应性
    data3 = np.vstack((data[y == 0][:], data[y == 1][:50], data[y == 2][:20], data[y == 3][:5]))
    # 生成data3对应的真实标签
    y3 = np.array([0] * 100 + [1] * 50 + [2] * 20 + [3] * 5)

    # ===================== 2. 聚类模型训练与预测 =====================
    # 可选模型1:KMeans(k-means++初始化,避免随机初始化的局部最优问题)
    # cls = KMeans(n_clusters=4, init='k-means++')
    # 可选模型2:高斯混合模型(GMM),假设数据服从高斯分布,支持非球形、方差不等的簇
    cls = GaussianMixture(n_components=4)
    
    # 对标准分布数据聚类并预测标签
    y_hat = cls.fit_predict(data)
    # 对方差不等数据聚类并预测标签
    y2_hat = cls.fit_predict(data2)
    # 对数量不等数据聚类并预测标签
    y3_hat = cls.fit_predict(data3)
    
    # 生成旋转矩阵(用于验证KMeans对旋转数据的敏感性)
    m = np.array(((1, 1), (1, 3)))
    # 对标准数据进行矩阵乘法实现旋转变换
    data_r = data.dot(m)
    # 对旋转后数据聚类并预测标签
    y_r_hat = cls.fit_predict(data_r)

    # ===================== 3. 可视化配置 =====================
    # 设置matplotlib的中文字体为黑体,避免中文乱码
    matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
    # 解决负号显示为方块的问题
    matplotlib.rcParams['axes.unicode_minus'] = False
    # 创建自定义颜色映射:红、绿、蓝、品红,对应4个聚类簇
    cm = matplotlib.colors.ListedColormap(list('rgbm'))

    # ===================== 4. 绘图展示聚类效果 =====================
    # 创建画布:尺寸9x10,背景白色
    plt.figure(figsize=(9, 10), facecolor='w')
    
    # 子图1:标准分布的原始数据
    plt.subplot(421)
    plt.title(u'原始数据')
    # 散点图:x轴为第一特征,y轴为第二特征,颜色按真实标签区分,无边缘线
    plt.scatter(data[:, 0], data[:, 1], c=y, s=30, cmap=cm, edgecolors='none')
    # 计算数据的最小/最大值,用于设置坐标轴范围
    x1_min, x2_min = np.min(data, axis=0)
    x1_max, x2_max = np.max(data, axis=0)
    # 扩展坐标轴范围
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    # 设置x/y轴范围
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    # 显示网格线,便于观察数据分布
    plt.grid(True)

    # 子图2:标准分布数据的聚类结果
    plt.subplot(422)
    plt.title(u'KMeans++聚类')  # 注:实际使用GMM时可修改标题为"高斯混合模型聚类"
    plt.scatter(data[:, 0], data[:, 1], c=y_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图3:旋转后的原始数据
    plt.subplot(423)
    plt.title(u'旋转后数据')
    plt.scatter(data_r[:, 0], data_r[:, 1], c=y, s=30, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data_r, axis=0)
    x1_max, x2_max = np.max(data_r, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图4:旋转后数据的聚类结果
    plt.subplot(424)
    plt.title(u'旋转后KMeans++聚类')
    plt.scatter(data_r[:, 0], data_r[:, 1], c=y_r_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图5:方差不等的原始数据
    plt.subplot(425)
    plt.title(u'方差不相等数据')
    plt.scatter(data2[:, 0], data2[:, 1], c=y2, s=30, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data2, axis=0)
    x1_max, x2_max = np.max(data2, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图6:方差不等数据的聚类结果
    plt.subplot(426)
    plt.title(u'方差不相等KMeans++聚类')
    plt.scatter(data2[:, 0], data2[:, 1], c=y2_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图7:样本数量不等的原始数据
    plt.subplot(427)
    plt.title(u'数量不相等数据')
    plt.scatter(data3[:, 0], data3[:, 1], s=30, c=y3, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data3, axis=0)
    x1_max, x2_max = np.max(data3, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 子图8:样本数量不等数据的聚类结果
    plt.subplot(428)
    plt.title(u'数量不相等KMeans++聚类')
    plt.scatter(data3[:, 0], data3[:, 1], c=y3_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    # 调整子图间距:水平/垂直间距为2,整体布局调整(避免标题重叠)
    plt.tight_layout(2, rect=(0, 0, 1, 0.97))
    # 设置总标题,字体大小18
    plt.suptitle(u'数据分布对KMeans聚类的影响', fontsize=18)
    # 显示图形
    plt.show()
    # 可选:保存图片到本地
    # plt.savefig('cluster_kmeans')

核心注释说明补充

  1. 关键算法对比:代码中注释了KMeans,实际使用GaussianMixture(GMM),这是因为GMM能更好地处理非球形、方差不等的簇,而KMeans假设簇是球形且方差相等的,通过这种对比可更清晰展示KMeans的局限性。
  2. 数据场景设计
    • 旋转数据:验证KMeans对数据旋转的敏感性(KMeans基于距离聚类,旋转会改变距离分布);
    • 方差不等数据:验证KMeans对簇方差差异的适应性;
    • 数量不等数据:验证KMeans对簇规模不均衡的处理效果。
  3. 可视化逻辑:每个子图分为“原始数据”和“聚类结果”成对展示,便于直观对比聚类算法是否能正确还原数据的真实簇结构。

项目结论(补充说明)

通过该实验可得出:

  • KMeans在标准球形分布、样本均衡的数据集上表现良好;
  • 当数据旋转、簇方差不等或样本数量不均衡时,KMeans聚类效果显著下降;
  • 高斯混合模型(GMM)作为概率型聚类算法,能更好地适应非标准分布的数据,是KMeans的有效替代方案。

运行结果:

在这里插入图片描述

更多推荐