聚类算法(一)- K-Means聚类

文章目录

  • 一、K-Means聚类相关理论
    • [1. K-Means聚类的核心定义](#1. K-Means聚类的核心定义)
    • [2. K-Means聚类的底层核心原理](#2. K-Means聚类的底层核心原理)
      • [2.1 目标函数:误差平方和(SSE)](#2.1 目标函数:误差平方和(SSE))
      • [2.2 核心逻辑:迭代优化的贪心策略](#2.2 核心逻辑:迭代优化的贪心策略)
      • [2.3 距离度量的选择逻辑](#2.3 距离度量的选择逻辑)
    • [3. K-Means聚类的完整算法流程](#3. K-Means聚类的完整算法流程)
    • [4. K-Means聚类的关键问题与解决方案](#4. K-Means聚类的关键问题与解决方案)
      • [4.1 问题1:如何确定最优簇数K?](#4.1 问题1:如何确定最优簇数K?)
        • [方法1:肘部法则(Elbow Method)](#方法1:肘部法则(Elbow Method))
        • [方法2:轮廓系数(Silhouette Coefficient)](#方法2:轮廓系数(Silhouette Coefficient))
        • 方法3:业务场景约束
      • [4.2 问题2:初始簇中心如何优化?](#4.2 问题2:初始簇中心如何优化?)
      • [4.3 问题3:对异常值敏感如何解决?](#4.3 问题3:对异常值敏感如何解决?)
      • [4.4 问题4:不适用于非球形簇如何解决?](#4.4 问题4:不适用于非球形簇如何解决?)
    • [5. K-Means聚类的特性与适用场景](#5. K-Means聚类的特性与适用场景)
      • [5.1 核心特性](#5.1 核心特性)
      • [5.2 适用场景](#5.2 适用场景)
    • [6. 总结](#6. 总结)
  • 二、scikit-learn中k-means聚类相关方法介绍
    • [1. k-means聚类构造方法](#1. k-means聚类构造方法)
      • [1.1 方法签名及参数解释](#1.1 方法签名及参数解释)
      • [1.2 参数取值说明](#1.2 参数取值说明)
    • [2. k-means聚类构造方法返回对象的方法](#2. k-means聚类构造方法返回对象的方法)
    • [3. k-means聚类构造方法返回对象的属性](#3. k-means聚类构造方法返回对象的属性)
    • [4. k-means聚类模型评估指标](#4. k-means聚类模型评估指标)
  • 三、pandas.DataFrame中的绘图方法
    • [1. 绘图方法介绍](#1. 绘图方法介绍)
    • [2. 参数介绍](#2. 参数介绍)
      • [2.1 图表类型与数据映射参数](#2.1 图表类型与数据映射参数)
      • [2.2 图表样式与标签参数](#2.2 图表样式与标签参数)
      • [2.3 布局与显示参数](#2.3 布局与显示参数)
      • [2.4 特定图表专属参数](#2.4 特定图表专属参数)
    • [3. 使用示例](#3. 使用示例)
  • 四、基于K-Means的鸢尾花数据聚类分析
    • [1. 数据挖掘目标](#1. 数据挖掘目标)
    • [2. 数据收集与加载](#2. 数据收集与加载)
      • [2.1 数据集介绍](#2.1 数据集介绍)
      • [2.2 数据加载](#2.2 数据加载)
    • [3. 探索性数据分析](#3. 探索性数据分析)
      • [3.1 数据基本信息](#3.1 数据基本信息)
      • [3.2 单特征分布分析](#3.2 单特征分布分析)
      • [3.2 特征间相关性分析](#3.2 特征间相关性分析)
      • [3.3 变异系数分析](#3.3 变异系数分析)
    • [4. 数据预处理](#4. 数据预处理)
      • [4.1 异常值检测与处理](#4.1 异常值检测与处理)
      • [4.2 特征标准化](#4.2 特征标准化)
    • [5. 模型构建(模型选择、训练)](#5. 模型构建(模型选择、训练))
      • [5.1 确定最优聚类数k](#5.1 确定最优聚类数k)
      • [5.2 K-Means聚类模型构建](#5.2 K-Means聚类模型构建)
    • [6. 模型评估](#6. 模型评估)
      • [6.1 核心评估指标](#6.1 核心评估指标)
      • [6.2 聚类结果可视化](#6.2 聚类结果可视化)
        • [6.2.1 聚类标签分布可视化](#6.2.1 聚类标签分布可视化)
        • [6.2.2 聚类效果可视化](#6.2.2 聚类效果可视化)
        • [6.2.3 原始尺度的簇中心可视化](#6.2.3 原始尺度的簇中心可视化)
        • [6.2.4 标准化后的簇中心可视化](#6.2.4 标准化后的簇中心可视化)
  • 五、完整代码
    • [1. 完整代码](#1. 完整代码)

一、K-Means聚类相关理论

在无监督学习领域,聚类算法是挖掘数据内在结构与隐藏规律的核心工具,而K-Means聚类以其简单直观、高效可扩展的特性,成为工业界和学术界应用最广泛的聚类算法之一。无论是用户画像细分、商品推荐分组,还是图像分割、异常检测,K-Means都能以较低的计算成本快速输出有价值的聚类结果。本文将从核心定义、底层原理、算法流程、关键问题及优化方向等维度,系统梳理K-Means聚类的相关理论,为后续实践应用奠定基础。

1. K-Means聚类的核心定义

K-Means聚类是一种基于"距离度量"的划分式聚类算法,其核心目标是:将给定的N个样本数据,按照特征相似度划分为K个互不重叠的子集(称为"簇",Cluster),使得每个子集中的样本都具有较高的相似度,而不同子集间的样本相似度较低

从定义中可提炼出K-Means的三个核心要素:

  • 预设簇数K:K是算法的输入参数,需由用户根据业务场景或数据特性预先设定,这也是"K-Means"中"K"的由来。例如,在电商用户细分中,若需将用户划分为"高价值""中价值""低价值"三类,则K取值为3。

  • 相似度度量:K-Means以"距离"衡量样本间的相似度,距离越小,相似度越高。最常用的距离度量是欧几里得距离(Euclidean Distance),适用于连续型特征数据;此外,也可根据数据类型选择曼哈顿距离(Manhattan Distance)、余弦相似度(Cosine Similarity)等。

  • 簇中心(Centroid):每个簇的"代表",是该簇内所有样本特征的均值向量(又称"质心")。K-Means通过迭代更新簇中心,不断优化样本的簇划分结果,最终使簇内样本围绕簇中心聚集。

简单来说,K-Means的本质是"围绕簇中心的样本聚集"------通过反复调整簇中心的位置和样本的簇归属,最终实现"簇内紧凑、簇间分散"的聚类效果。

2. K-Means聚类的底层核心原理

K-Means的聚类过程本质是一个"目标函数优化 "的迭代过程,其底层原理围绕"误差平方和(Sum of Squared Errors, SSE)最小化"展开,通过贪心策略逐步逼近最优聚类结果。

2.1 目标函数:误差平方和(SSE)

K-Means以"簇内样本到其簇中心的距离平方和之和"作为目标函数,称为误差平方和(SSE),其计算公式为:

S S E = Σ (簇 = 1 到 K ) Σ (样本 ∈ 簇) ∣ ∣ x i − μ c ∣ ∣ 2 SSE = Σ(簇=1到K)Σ(样本∈簇)||x_i - μ_c||² SSE=Σ(簇=1到K)Σ(样本∈簇)∣∣xi−μc∣∣2

其中, x i x_i xi是簇c内的第i个样本, μ c μ_c μc是簇c的中心向量, ∣ ∣ ⋅ ∣ ∣ ||·|| ∣∣⋅∣∣表示欧几里得距离。

目标函数SSE的物理意义是"所有样本到其簇中心的整体偏离程度",K-Means的核心任务就是通过迭代优化,找到使SSE最小的K个簇划分方案。SSE越小,说明簇内样本的聚集程度越高,聚类效果越好。

2.2 核心逻辑:迭代优化的贪心策略

K-Means通过"分配-更新"的迭代过程最小化SSE,这是一种典型的贪心策略------每一步迭代都朝着使SSE降低的方向调整,最终收敛到局部最优解。其核心逻辑可拆解为两个关键步骤:

  1. 样本分配(簇归属更新):在簇中心固定的情况下,将每个样本分配到距离其最近的簇中心所在的簇。这一步通过最小化单个样本到簇中心的距离,直接降低整体SSE。

  2. 簇中心更新:在样本簇归属固定的情况下,重新计算每个簇的中心向量(即簇内所有样本特征的均值)。由于簇中心是簇内样本的"平均位置",这一步能进一步拉近簇内样本与簇中心的距离,从而降低SSE。

这两个步骤交替进行,直到SSE的下降幅度小于预设阈值(如0.001),或迭代次数达到上限,算法停止并输出最终聚类结果。需要注意的是,K-Means的贪心策略只能保证收敛到局部最优解,而非全局最优解,其最终结果会受初始簇中心选择的影响

2.3 距离度量的选择逻辑

距离度量直接影响K-Means的聚类效果,需根据数据的特征类型和业务场景合理选择,常见的距离度量及适用场景如下:

  • 欧氏距离 :最常用的距离度量,计算公式为 ∣ ∣ x i − x j ∣ ∣ = √ [ Σ ( d = 1 到 D ) ( x i d − x j d ) 2 ] ||x_i - x_j|| = √[Σ(d=1到D)(x_id - x_jd)²] ∣∣xi−xj∣∣=√[Σ(d=1到D)(xid−xjd)2](D为特征维度)。适用于连续型特征数据,且特征量纲一致(如身高、体重等经过标准化后的特征)

  • 曼哈顿距离 :又称"城市街区距离",计算公式为 ∣ ∣ x i − x j ∣ ∣ = Σ ( d = 1 到 D ) ∣ x i d − x j d ∣ ||x_i - x_j|| = Σ(d=1到D)|x_id - x_jd| ∣∣xi−xj∣∣=Σ(d=1到D)∣xid−xjd∣。适用于特征维度多、且关注"绝对差异"的场景(如文本中词频的差异),对异常值的敏感度低于欧几里得距离

  • 余弦相似度 :衡量两个向量的夹角余弦值,取值范围为[-1,1],值越接近1,相似度越高。计算公式为 c o s θ = ( x i ⋅ x j ) / ( ∣ ∣ x i ∣ ∣ ⋅ ∣ ∣ x j ∣ ∣ ) cosθ = (x_i·x_j) / (||x_i||·||x_j||) cosθ=(xi⋅xj)/(∣∣xi∣∣⋅∣∣xj∣∣)。适用于高维稀疏数据(如文本向量、图像特征),关注样本的"方向一致性"而非"数值大小",例如判断两篇文章的主题相似度。

需要特别注意的是,若特征量纲差异较大(如"年龄"范围为0-100,"收入"范围为0-100000),直接使用欧几里得距离会导致"收入"特征的权重被过度放大,因此在聚类前需对特征进行标准化(Standardization)或归一化(Normalization)处理。

3. K-Means聚类的完整算法流程

K-Means的算法流程简洁清晰,核心围绕"初始化簇中心→迭代优化→收敛停止"三个阶段展开,具体步骤如下:

步骤1:数据预处理与参数设置

  1. 数据清洗:处理原始数据中的缺失值(如用均值/中位数填充)、异常值(如通过箱线图剔除),确保数据质量。

  2. 特征标准化/归一化 :若特征量纲不一致,对每个特征进行标准化 x ′ = ( x − μ ) / σ x' = (x - μ)/σ x′=(x−μ)/σ 或归一化 x ′ = ( x − m i n ) / ( m a x − m i n ) x' = (x - min)/(max - min) x′=(x−min)/(max−min),消除量纲影响。

  3. 设置参数:预设簇数K、最大迭代次数(如100次)、收敛阈值(如SSE下降幅度小于1e-4时停止)。

步骤2:初始化K个簇中心

从N个样本中随机选择K个不重复的样本作为初始簇中心。这一步是K-Means的关键------若初始簇中心选择不当(如过于集中),可能导致算法收敛到较差的局部最优解,或迭代次数大幅增加。为优化初始簇中心选择,后续衍生出了K-Means++算法(将在1.5节介绍)。

步骤3:迭代优化(分配-更新)

这是K-Means的核心阶段,通过交替执行"样本分配"和"簇中心更新"两个步骤,逐步降低SSE,具体如下:

子步骤3.1:样本分配(簇归属更新)

对每个样本 x i x_i xi,计算其到K个簇中心的距离(如欧几里得距离),将$x_i¥分配到距离最近的簇中心对应的簇中,更新所有样本的簇归属标签。

子步骤3.2:簇中心更新

对每个簇c,计算该簇内所有样本的特征均值向量,将其作为新的簇中心 μ c μ_c μc,替代原有的簇中心。若某一簇内无样本(极端情况),可重新随机选择一个样本作为该簇的中心。

子步骤3.3:判断收敛条件

计算当前迭代的SSE,并与上一次迭代的SSE进行对比:

  • 若SSE的下降幅度小于预设阈值,或簇中心的变化量小于阈值,说明算法已收敛,停止迭代。

  • 若未收敛,且迭代次数未达到上限,返回子步骤3.1继续迭代;若达到最大迭代次数仍未收敛,也停止迭代并输出结果。

步骤4:输出聚类结果与评估

算法停止后,输出最终的K个簇划分结果(每个样本的簇归属标签)、各簇的中心向量及最终的SSE值。同时,通过聚类评估指标(如轮廓系数、CH指数)验证聚类效果,若效果不佳,可调整K值或距离度量方式重新执行算法。

4. K-Means聚类的关键问题与解决方案

K-Means虽简单高效,但在实际应用中存在诸多关键问题,如K值难以确定、初始簇中心影响结果、对异常值敏感等。针对这些问题,业界已形成成熟的解决方案,是用好K-Means的核心。

4.1 问题1:如何确定最优簇数K?

K是K-Means的核心参数,但数据本身的"真实簇数"往往未知,盲目设置K值会导致聚类效果偏差。常用的K值确定方法包括:

方法1:肘部法则(Elbow Method)

核心逻辑:随着K值的增加,簇内样本的聚集程度提高,SSE会不断下降;但当K超过数据的真实簇数后,SSE的下降幅度会显著减缓,形成一个"肘部"(Elbow),肘部对应的K值即为最优簇数。

操作步骤:设置K的取值范围(如1到10),对每个K值运行K-Means并计算SSE,以K为横轴、SSE为纵轴绘制折线图,找到折线由陡变缓的"肘部"对应的K值。例如,当K=3时SSE为1000,K=4时SSE为980(下降幅度仅2%),而K=2到K=3时SSE从2000降至1000(下降幅度50%),则K=3为最优值。

方法2:轮廓系数(Silhouette Coefficient)

核心逻辑:从"样本自身的聚类合理性"出发,衡量每个样本的"轮廓系数",所有样本的平均轮廓系数越大,聚类效果越好,对应的K值越优。

轮廓系数的计算逻辑:对样本 x i x_i xi, a i a_i ai是其与簇内其他所有样本的平均距离(簇内相似度), b i b_i bi是其与距离最近的其他簇内所有样本的平均距离(簇间相似度),则 x i x_i xi的轮廓系数为 s i = ( b i − a i ) / m a x ( a i , b i ) s_i = (b_i - a_i)/max(a_i, b_i) si=(bi−ai)/max(ai,bi)。 s i s_i si的取值范围为 [ − 1 , 1 ] [-1,1] [−1,1], s i s_i si越接近1,说明样本聚类越合理;越接近-1,说明样本更适合划分到其他簇。

方法3:业务场景约束

在实际业务中,K值可由业务需求直接确定。例如,电商平台需将商品划分为"热销""平销""滞销"三类,则K=3;金融机构需将客户风险等级划分为"高风险""中风险""低风险",则K=3。这种方法优先满足业务需求,再通过聚类评估指标验证合理性。

4.2 问题2:初始簇中心如何优化?

传统K-Means随机选择初始簇中心,易导致收敛到局部最优解。K-Means++算法通过"距离加权"的方式优化初始簇中心选择,核心思想是"使初始簇中心尽可能分散",具体步骤如下:

  1. 从样本集中随机选择1个样本作为第一个簇中心 μ 1 μ_1 μ1。

  2. 对每个样本 x i x_i xi,计算其到已选簇中心的最短距离 D ( x i ) D(x_i) D(xi)(即到最近簇中心的距离)。

  3. 根据 D ( x i ) D(x_i) D(xi)的平方值进行加权抽样,选择下一个簇中心 μ k ------ D ( x i ) μ_{k}------D(x_i) μk------D(xi)越大的样本,被选中的概率越高,确保新簇中心远离已选中心。

  4. 重复步骤2和3,直到选择出K个簇中心。

K-Means++通过优化初始簇中心,大幅提升了算法收敛到全局最优解的概率,同时减少了迭代次数,已成为主流的K-Means实现方式(如scikit-learn中的KMeans类默认采用K-Means++初始化)。

4.3 问题3:对异常值敏感如何解决?

K-Means的簇中心是样本均值,而均值对异常值极为敏感------一个极端异常值可能会将簇中心"拉偏",导致整个簇的划分结果失真。解决方案包括:

  • 数据预处理剔除异常值:在聚类前,通过箱线图(IQR法则)、Z-score方法等识别并剔除异常值,从源头上减少异常值的影响。

  • 使用中位数替代均值作为簇中心:中位数对异常值的抵抗力远强于均值,基于中位数的聚类算法称为K-Medoids(K-中心点算法),适用于异常值较多的场景,但计算成本高于K-Means。

  • 引入加权K-Means:为每个样本分配权重,降低异常值的权重(如将异常值权重设为0.1,正常样本权重设为1),从而减少其对簇中心的影响。

4.4 问题4:不适用于非球形簇如何解决?

传统K-Means基于欧几里得距离,仅能有效聚类"球形分布"的簇(即簇内样本围绕中心呈圆形或椭圆形分布),对非球形簇(如环形、条形簇)的聚类效果极差。解决方案包括:

  • 使用密度聚类算法辅助:如DBSCAN算法,基于样本的密度分布聚类,无需预设K值,能有效识别任意形状的簇,可先通过DBSCAN确定簇的大致形状和数量,再用K-Means细化聚类。

  • 特征空间转换:通过核函数(如RBF核)将原始数据映射到高维特征空间,使非球形簇在高维空间中呈现球形分布,再使用K-Means聚类,即核K-Means(Kernel K-Means)。

5. K-Means聚类的特性与适用场景

明确K-Means的特性及适用场景,是合理选择聚类算法的关键,避免在不适用的场景中强行使用导致效果不佳。

5.1 核心特性

  • 优点

    简单直观,易于理解和实现,开发成本低。

  • 计算效率高,时间复杂度为 O ( N ∗ K ∗ D ∗ T ) O(N*K*D*T) O(N∗K∗D∗T)(N为样本数,D为特征维度,T为迭代次数),适用于大规模数据。

  • 可扩展性强,支持并行计算(如Spark MLlib中的K-Means实现),能处理百万级甚至亿级样本。

  • 聚类结果易于解释,簇中心可直接对应业务含义(如用户簇中心的"消费金额"特征可代表该簇用户的平均消费能力)。

缺点

  • 需预设簇数K,难以准确确定最优K值。

  • 对初始簇中心敏感,易收敛到局部最优解。

  • 对异常值和噪声数据敏感,影响簇中心的准确性。

  • 仅适用于球形分布的簇,对非球形、非凸形簇的聚类效果差。

  • 对特征量纲敏感,需提前进行标准化处理。

5.2 适用场景

  • 大规模数据的聚类任务:如电商平台的用户行为数据(百万级用户)、互联网日志数据(亿级访问记录),K-Means的高效性使其成为首选。

  • 球形簇分布的数据场景:如客户价值细分(基于消费金额、购买频率等特征,客户自然形成高、中、低价值的球形簇)、图像像素聚类(同一物体的像素特征呈球形分布)。

  • 业务中K值明确的场景:如根据业务需求将商品划分为固定类别的场景,无需通过算法确定K值,直接应用K-Means即可。

  • 快速原型验证场景:在项目初期,需快速验证聚类思路的可行性时,K-Means的简单易用性使其能够快速输出聚类结果,为后续优化提供基准。

6. 总结

K-Means聚类是无监督学习的入门级算法,其"以簇中心为核心的迭代优化"逻辑简单高效,使其在大规模数据聚类任务中占据不可替代的地位。尽管存在需预设K值、对初始中心敏感等缺陷,但通过K-Means++、肘部法则、轮廓系数等优化方法,可有效提升其聚类效果。


二、scikit-learn中k-means聚类相关方法介绍

scikit-learn(sklearn)的sklearn.cluster.KMeans类是实现k-means聚类算法的核心工具,适用于无监督场景下的样本分组。

1. k-means聚类构造方法

k-means聚类的核心逻辑是通过迭代将样本划分为k个簇,使簇内样本相似度最大化、簇间相似度最小化。KMeans类通过参数配置聚类规则,初始化后得到聚类器对象。

1.1 方法签名及参数解释

基础签名:

python 复制代码
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=8, init='k-means++', n_init='auto', max_iter=300, 
                tol=0.0001, random_state=None, algorithm='lloyd')

核心参数解释:

参数名称 功能说明
n_clusters 聚类的簇数量(k值),k-means算法的核心参数,决定最终分组数。
init 初始聚类中心的初始化方法,影响算法收敛速度与最终结果。
n_init 不同初始聚类中心的运行次数,取最优结果(最小惯性值)。
max_iter 单次初始化下的最大迭代次数,迭代达到阈值则停止。
tol 迭代收敛的容忍度,当簇中心变化量小于该值时停止迭代。
random_state 随机种子,保证初始化和迭代过程的可复现性。
algorithm 聚类算法的实现方式,影响计算效率和适用场景。
verbose 日志输出级别,0为无输出,大于0时打印迭代过程。
copy_x 是否复制原始数据,避免算法修改输入特征矩阵。
max_no_improvement 连续无改进的迭代次数阈值,超过则提前停止(仅algorithm='elkan'生效)。

1.2 参数取值说明

参数名称 取值类型/范围 默认值 合法取值 取值说明与建议
n_clusters 整数 8 ≥1的整数 需结合业务场景或肘部法则确定,无固定最优值,常见范围2-10。
init 字符串/数组 'k-means++' 'k-means++'、'random'、形状为(k, n_features)的数组 ① 'k-means++':智能初始化,避免初始中心过近,推荐使用;② 'random':随机选k个样本作为初始中心;③ 数组:自定义初始簇中心(需与特征维度匹配)。
n_init 整数/'auto' 'auto' 正整数、'auto' ① 'auto':init='k-means++'时取1,init='random'时取10;② 手动设为5-20,提升结果稳定性(但增加计算量)。
max_iter 整数 300 ≥1的整数 常见取值100-500,数据量大或特征多可适当增大,确保收敛。
tol 浮点数 1e-4 ≥0的浮点数 常用范围1e-4~1e-2,值越小收敛越严格,计算时间越长。
random_state 整数/None/np.random.RandomState None 任意整数、None、RandomState对象 设为固定整数(如42)可复现结果,便于参数调优对比。
algorithm 字符串 'lloyd' 'lloyd'、'elkan'、'full' ① 'lloyd':经典k-means算法,适用大数据,支持稀疏矩阵;② 'elkan':优化版,利用三角不等式减少距离计算,仅适用于稠密数据;③ 'full':等价于'lloyd'。
verbose 整数 0 ≥0的整数 0(无输出)、1(简要输出)、2(详细输出),调试时设为1即可。
copy_x 布尔值 True True/False ① True:复制数据,保护原始数据;② False:直接修改数据(节省内存),但原始数据会被覆盖。
max_no_improvement 整数/None None ≥1的整数、None None表示不限制,设为10-20可提前终止无改进的迭代,提升效率(仅elkan算法生效)。

2. k-means聚类构造方法返回对象的方法

KMeans初始化并通过fit()训练后,返回的聚类器对象提供一系列方法,覆盖聚类训练、预测、结果转换等核心功能,具体如下:

方法名称 语法格式 功能说明
fit(X) kmeans.fit(X) 核心训练方法,输入特征矩阵X(形状[n_samples, n_features]),完成聚类迭代,更新簇中心等属性。
fit_predict(X) labels = kmeans.fit_predict(X) 训练并直接返回样本的聚类标签(形状[n_samples,]),等价于先fit再predict。
predict(X) labels = kmeans.predict(X) 对新样本X预测所属簇标签,需先调用fit()训练模型。
transform(X) dist = kmeans.transform(X) 计算每个样本到各簇中心的欧氏距离,返回形状[n_samples, n_clusters]的矩阵。
fit_transform(X) dist = kmeans.fit_transform(X) 训练并直接返回样本到簇中心的距离矩阵,等价于先fit再transform。
score(X) score = kmeans.score(X) 计算聚类的负惯性值(-inertia_),值越大表示聚类效果越好(簇内越紧凑)。
inertia_(属性) 注:是属性而非方法,此处补充 计算簇内平方和(惯性值),即每个样本到其簇中心的欧氏距离平方和,是评估聚类紧凑性的核心指标。
get_params() params = kmeans.get_params() 返回模型的参数配置字典,便于查看当前参数或后续调参。
set_params(**params) kmeans.set_params(n_clusters=5) 修改模型参数,无需重新初始化对象,支持动态调整(如n_clusters、max_iter)。

3. k-means聚类构造方法返回对象的属性

KMeans对象经fit()训练后,生成一系列只读属性,存储聚类核心结果,所有属性需在训练后获取有效值,具体如下:

属性名称 数据类型 功能说明
cluster_centers_ 数组(形状[n_clusters, n_features]) 存储各簇的中心坐标,每个行向量对应一个簇的中心,是聚类的核心结果。
labels_ 数组(形状[n_samples,]) 存储训练集样本的聚类标签,标签为0~n_clusters-1的整数,对应样本所属簇。
inertia_ 浮点数 簇内平方和(惯性值),即所有样本到其簇中心的欧氏距离平方和,值越小簇内越紧凑。
n_iter_ 整数 实际迭代次数,若小于max_iter,说明算法提前收敛(簇中心变化≤tol)。
n_features_in_ 整数 训练模型时使用的特征数量,与输入X的特征维度一致。
feature_names_in_ 数组(可选) 训练数据为DataFrame时,存储特征名称;数组形状[n_features_in_]。
tol_ 浮点数 实际使用的收敛容忍度(与构造参数tol一致)。

核心属性使用说明:

  • cluster_centers_:可用于解释各簇的特征特征(如簇1的"平均消费金额"为500元),是聚类结果业务解读的核心依据;
  • labels_:可与原始数据合并,分析不同簇的样本分布规律;
  • inertia_:是"肘部法则"确定最优k值的核心指标,需结合不同k值的inertia_曲线判断。

4. k-means聚类模型评估指标

k-means聚类属于无监督学习,无"真实标签"参考,评估核心围绕"簇内紧凑性""簇间分离度"展开,常用指标如下:

指标名称 计算逻辑 取值范围 评估标准 sklearn实现函数
惯性值(Inertia) 所有样本到其簇中心的欧氏距离平方和 [0, +∞) 值越小,簇内越紧凑;但随k值增大单调递减,需结合肘部法则判断最优k。 直接调用kmeans.inertia_,无单独函数
轮廓系数(Silhouette Score) 综合簇内紧凑性(a)和簇间分离度(b),计算每个样本的轮廓系数:(b-a)/max(a,b),再取平均值。 [-1, 1] 越接近1,聚类效果越好(样本与自身簇紧凑,与其他簇分离);≤0说明聚类重叠严重。 sklearn.metrics.silhouette_score(X, labels)
戴维斯-布尔丁指数(DBI) 计算各簇的"簇内散度/簇间距离"的平均值,衡量簇的紧凑性与分离度。 [0, +∞) 值越小,聚类效果越好;通常<1为优秀,1~2为良好,>3为较差。 sklearn.metrics.davies_bouldin_score(X, labels)
卡尔斯基-哈拉巴斯指数(CHI) 计算"簇间方差和/簇内方差和"×(样本数-簇数)/(簇数-1),反映簇间分离度。 [0, +∞) 值越大,聚类效果越好;对噪声和异常值较敏感。 sklearn.metrics.calinski_harabasz_score(X, labels)

三、pandas.DataFrame中的绘图方法

1. 绘图方法介绍

pandas.DataFrame 内置了 plot() 方法(基于 matplotlib 封装),无需额外导入绘图库即可快速实现数据可视化,支持多种常见图表类型,满足数据分析中的快速探索需求。其核心优势是与 DataFrame 数据结构深度兼容,能直接读取列/行数据作为绘图数据源,无需手动处理数据格式转换。

常用的图表类型通过 kind 参数指定,核心支持以下几种:

  • 折线图(line):默认类型,适合展示数据随时间或有序维度的变化趋势,例如时序数据的波动情况。
  • 柱状图(bar/barh)bar 为垂直柱状图,barh 为水平柱状图,适合对比不同类别数据的数值大小,支持分组对比。
  • 直方图(hist):用于展示数据的分布特征,自动对数据进行分箱统计,直观呈现数据集中趋势和离散程度。
  • 散点图(scatter) :需指定 xy 列,用于分析两个变量之间的相关性,例如身高与体重的关联关系。
  • 箱线图(box):展示数据的四分位数、中位数和异常值,快速识别数据中的离群点和分布离散度。
  • 饼图(pie) :需配合 y 参数指定数值列,展示各类别数据占总体的比例,要求数据非负且总和有实际意义。

2. 参数介绍

DataFrame.plot() 方法的参数丰富,可灵活控制图表样式、布局和数据映射,核心常用参数如下(按功能分类)。

2.1 图表类型与数据映射参数

参数名 作用 可选值/说明
kind 指定图表类型 默认为 'line',可选 'bar'/'barh'/'hist'/'scatter'/'box'/'pie'/'kde'
x 指定x轴数据源(仅散点图、柱状图等需显式指定) DataFrame 列名或数组
y 指定y轴数据源 DataFrame 列名(单个或列表)、数组,饼图需指定单个数值列
data 可选,指定绘图数据源 若未指定,默认使用调用 plot() 的 DataFrame

2.2 图表样式与标签参数

参数名 作用 可选值/说明
title 设置图表标题 字符串,支持换行(\n
xlabel/ylabel 设置x轴/y轴标签 字符串
color 设置线条/填充颜色 单个颜色值(如 'red''#FF0000')或颜色列表(对应多列数据)
alpha 设置颜色透明度 浮点数(0~1),0为完全透明,1为不透明
linestyle 设置线条样式(仅折线图) 默认为 '-'(实线),可选 '--'(虚线)、':'(点线)、'-.'(点划线)
linewidth 设置线条宽度(仅折线图) 正整数
marker 设置数据点标记(仅折线图、散点图) 'o'(圆点)、's'(正方形)、'^'(三角形)

2.3 布局与显示参数

参数名 作用 可选值/说明
figsize 设置图表尺寸 元组 (宽度, 高度),单位为英寸(如 (10, 6)
grid 是否显示网格线 布尔值(默认 False),可指定 grid=True 显示
legend 是否显示图例 布尔值(默认 True,多列数据时自动生成),False 隐藏图例
subplots 是否将多列数据拆分为子图 布尔值(默认 False),True 时每列数据对应一个子图
layout 子图布局(仅 subplots=True 时生效) 元组 (行数, 列数),如 (2, 2) 表示2行2列子图

2.4 特定图表专属参数

参数名 适用图表 作用 可选值/说明
bins 直方图 指定分箱数量 正整数(默认自动分箱)
autopct 饼图 显示百分比标签 格式化字符串(如 '%1.1f%%' 表示保留1位小数)
startangle 饼图 设置起始角度 整数(默认0°,顺时针旋转)
bins 直方图 数据分箱数 整数或数组(指定分箱边界)
vert 直方图/柱状图 是否垂直显示 布尔值(默认 True,直方图设为 False 则水平显示)

3. 使用示例

示例代码(以常见图表为例):

python 复制代码
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 构造示例数据
data = pd.DataFrame({
    'A': np.random.randn(100).cumsum(),  # 累积和模拟时序数据
    'B': np.random.randint(10, 50, 100),  # 随机整数数据
    'C': np.random.choice(['X', 'Y', 'Z'], 100),  # 分类数据
    'D': np.random.normal(50, 10, 100)  # 正态分布数据
})

# 1. 折线图(默认):展示A列时序趋势
# data['A'].plot(title='A列时序变化趋势')
# 综合参数示例:定制化折线图
data['A'].plot(
    kind='line',  # 图表类型
    title='A列时序趋势(定制样式)',  # 标题
    xlabel='索引', ylabel='数值',  # 轴标签
    color='#2E86AB', linewidth=2, linestyle='--',  # 线条样式
    marker='o', markersize=4,  # 数据点标记
    figsize=(10, 4), grid=True, alpha=0.8  # 布局与透明度
)

# 2. 垂直柱状图:对比前10行A、B列数据
data[['A', 'B']].head(10).plot(kind='bar', title='A、B列前10行数据对比')

# 3. 直方图:展示D列数据分布
data['D'].plot(kind='hist', bins=15, title='D列数据分布')

# 4. 散点图:分析A与B的相关性
data.plot(kind='scatter', x='A', y='B', title='A与B的散点图')

# 5. 箱线图:展示A、B列数据分布特征
data[['A', 'B']].plot(kind='box', title='A、B列箱线图')

# 6. 饼图:展示C列各类别占比
data['C'].value_counts().plot(kind='pie', autopct='%1.1f%%', title='C列类别占比')
plt.show()

四、基于K-Means的鸢尾花数据聚类分析

K-Means是无监督学习中最经典的聚类算法之一,核心思想是通过迭代将样本划分为指定数量的簇,使簇内样本相似度最大化、簇间相似度最小化。鸢尾花数据集作为机器学习经典数据集,特征维度低、数据分布规律,非常适合用于K-Means聚类的实战分析。本文将从数据挖掘目标出发,完成数据收集加载、预处理、聚类建模、结果评估全流程,清晰展示K-Means在鸢尾花数据上的应用效果。

1. 数据挖掘目标

本次基于K-Means的鸢尾花数据聚类分析,核心目标分为以下3点:

  1. 验证聚类有效性:鸢尾花数据集包含3类已知品种的样本(山鸢尾、变色鸢尾、维吉尼亚鸢尾),通过K-Means聚类将样本划分为3个簇,验证无监督聚类结果与真实品种标签的匹配度,评估K-Means算法的聚类效果;
  2. 挖掘特征规律:分析聚类后各簇的特征分布(如花瓣长度、萼片宽度等),总结不同簇的核心特征差异,解读聚类结果的业务意义;
  3. 掌握K-Means实战流程:完整落地"数据加载→预处理→模型构建→参数调优→结果评估→可视化"的K-Means聚类流程,形成可复用的无监督分析模板。

2. 数据收集与加载

2.1 数据集介绍

本示例使用的鸢尾花数据集(Iris Dataset) 是机器学习领域最经典的数据集之一,由英国统计学家和生物学家罗纳德·费希尔(Ronald Fisher)于1936年提出,常被用于分类、聚类算法的验证与演示。

字段名称(英文) 中文含义 数据类型 单位 说明
sepal length (cm) 花萼长度 浮点数 厘米 花朵最外层保护结构的长度
sepal width (cm) 花萼宽度 浮点数 厘米 花萼的横向宽度
petal length (cm) 花瓣长度 浮点数 厘米 花朵内层显眼部分的长度
petal width (cm) 花瓣宽度 浮点数 厘米 花瓣的横向宽度
species 类别标签 对象 --- 3个类别标签:setosa(山鸢尾,标签0)、versicolor(变色鸢尾,标签1)、virginica(维吉尼亚鸢尾,标签2)

所有特征均为形态学测量值,具有明确的生物学意义,且数值范围相近,适合直接用于建模(必要时可标准化)。

2.2 数据加载

鸢尾花数据集已内置在scikit-learn的datasets模块中,可直接调用加载,无需手动下载。

python 复制代码
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.datasets import load_breast_cancer, load_iris
from sklearn.tree import DecisionTreeClassifier

# 设置中文字体正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置Pandas全局选项
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', 50)
pd.set_option('display.expand_frame_repr', False)

# ====================加载数据====================

# 加载鸢尾花数据集
iris = load_iris()
# 转换为DataFrame
df_iris = pd.DataFrame(
    data=iris.data,  # 特征矩阵
    columns=iris.feature_names  # 特征名称
)
# 添加真实品种标签列(用于后续对比聚类结果)
df_iris['species'] = iris.target
# 将数字标签映射为品种名称(提升可读性)
df_iris['species_name'] = (df_iris['species'].map({
    0: '山鸢尾',
    1: '变色鸢尾',
    2: '维吉尼亚鸢尾'
}))

3. 探索性数据分析

探索性数据分析(EDA)是聚类建模前的核心环节,旨在通过统计分析和可视化手段挖掘数据内在规律------包括特征分布特征、特征间关联关系及不同品种的特征差异,为后续K-Means聚类的参数设置、结果解读提供依据。

3.1 数据基本信息

在聚类建模前,首先对鸢尾花数据集的基础信息进行梳理,明确数据规模、结构、完整性与统计特征,为后续分析奠定基础。

python 复制代码
# ====================数据基本信息====================

# 基础信息探索
print("=== 鸢尾花数据集基础信息 ===")
print(f"数据集形状:{df_iris.shape}(样本数×特征数)")
print("\n=== 数据前5行预览 ===")
print(df_iris.head())

print("\n=== 数据类型与缺失值检查 ===")
print(df_iris.info())  # 无缺失值,特征均为浮点型

print("\n=== 特征统计描述 ===")
print(df_iris.describe().round(2))  # 查看特征的均值、标准差等

鸢尾花数据集共包含150个样本,每个样本对应4个数值型特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度),以及2个标签列(species为数值型品种标签、species_name为文字型品种名称),整体结构清晰规范。

通过数据类型与缺失值检查可知,所有特征列均无缺失值,4个形态特征为浮点型,标签列分别为整数型与字符型,数据质量较高,无需进行缺失值填充等预处理操作。

从统计描述结果可见,不同特征的数值范围差异较大(如花瓣长度最小值1.0cm、最大值6.9cm,标准差达1.77),花萼宽度的波动相对较小(标准差0.44);各特征的均值与中位数(50%分位数)接近,说明多数特征的分布相对对称,无极端偏离的异常值。

3.2 单特征分布分析

单特征分布是挖掘数据内在规律的基础环节,通过核密度估计图(KDE)可直观呈现鸢尾花4个形态特征的整体分布形态,为后续聚类的"簇划分依据"提供参考。本次分析针对花萼长度、花萼宽度、花瓣长度、花瓣宽度4个特征,分别绘制核密度曲线,以观察各特征的集中趋势与离散特性。可视化中对每个特征单独生成核密度图(填充式曲线更清晰展示分布区间),并开启单独归一化避免样本量干扰。

python 复制代码
# ====================单特征分布分析====================

import matplotlib.pyplot as plt
import seaborn as sns

# 绘制核密度图,KDE:核密度估计
plt.figure(figsize=(15, 10))
feature = df_iris.columns[:-2]
for i in range(len(feature)):
    plt.subplot(2, 2, i + 1)
    sns.kdeplot(
        data=df_iris,
        x=feature[i],
        fill=True,
        common_norm=False,  # 每个类别单独归一化
        alpha=0.5
    )
    plt.title(f'按类别的 {feature[i]} 核密度估计图')
    plt.xlabel(feature[i])
plt.tight_layout()
plt.savefig('单特征分布核密度估计图.png')

从结果可总结出两个核心特征:

(1)分布形态差异

4个形态特征的分布形态呈现明显分化:

  • 花萼长度、花萼宽度的核密度曲线呈单峰对称形态,符合近似正态分布的特征,说明这两个特征的数值集中在均值附近,离散程度相对温和;
  • 花瓣长度、花瓣宽度的核密度曲线呈现双峰形态,曲线中间存在明显的"低谷"区域------这是鸢尾花不同品种在花瓣形态上差异显著的直接体现,两类峰值分别对应不同品种的特征集中区间。

(2)特征离散特性

不同特征的离散程度差异较大:

  • 花萼相关特征(长度、宽度)的分布区间相对紧凑,曲线峰值较高,说明多数样本的花萼尺寸集中在较小范围内;
  • 花瓣相关特征(长度、宽度)的分布区间更宽,且因"双峰"结构呈现明显的"两极分化",这也暗示花瓣特征更可能成为区分不同样本群体的核心依据。

这一结果为后续K-Means聚类提供了重要线索:花瓣特征的"双峰分布"意味着数据天然存在群体划分的基础,聚类过程中这些特征将成为区分不同簇的关键驱动因素。

3.2 特征间相关性分析

特征间的相关性是聚类分析的关键参考------高相关特征会强化该维度对聚类结果的影响,而低相关特征则体现了数据的多维度差异。本次通过斯皮尔曼相关系数结合热力图,分析鸢尾花4个形态特征的线性关联程度。可视化中隐藏了热力图的上三角区域(避免信息重复),以颜色深浅+数值标注的方式呈现相关系数(红色代表正相关、蓝色代表负相关,数值越接近±1关联越强)。

python 复制代码
# ===================特征间相关性分析====================

import matplotlib.pyplot as plt
import seaborn as sns

features = df_iris.columns[:-2]
# 全局特征相关性热力图(不区分品种)
plt.figure(figsize=(10, 8))
# 计算特征间的皮尔逊相关系数
corr_matrix = df_iris[features].corr(method='spearman')
# 绘制热力图
mask = np.triu(m=np.ones_like(corr_matrix, dtype=bool), k=1)  # 隐藏上三角(避免重复)
sns.heatmap(data=corr_matrix, mask=mask, annot=True, fmt='.2f', cmap='coolwarm', linewidths=0.5)
plt.title('鸢尾花特征相关性热力图', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig('特征间全局相关性.png')

从相关性分析热力图可以看出,花瓣相关特征之间及其与花萼长度呈现出显著的正相关关系------花瓣长度与花瓣宽度的相关系数高达0.94,几乎呈线性关联;同时,二者与花萼长度的相关系数也分别达到0.88和0.83。这表明鸢尾花的花瓣尺寸与花萼长度存在明显的协同变化趋势,花瓣越长或越宽,花萼通常也越长。花萼宽度与其他特征的关联较弱,表现为弱负相关(与花萼长度、花瓣长度、花瓣宽度的相关系数分别为-0.17、-0.31和-0.29),说明其变化相对独立。这一特性在K-Means聚类中具有重要意义,强相关的花瓣特征与花萼长度共同强化了"花部整体尺寸"这一聚类主维度,而花萼宽度的独立性则可能为簇的细分提供额外的判别信息。

3.3 变异系数分析

变异系数(CV)是衡量特征离散程度的标准化指标,可消除特征尺度差异的影响,更客观地对比不同特征的波动程度------变异系数越大,说明该特征在样本间的差异越显著,越可能成为聚类的区分依据。本次分析计算了鸢尾花4个形态特征的变异系数,并以条形图可视化排序结果。

python 复制代码
# ===================特征变异系数分析====================

# 计算各特征的变异系数(反映离散程度,变异系数=标准差/均值)
cv_data = []
for feat in df_iris.columns[:-2]:
    cv = df_iris[feat].std() / df_iris[feat].mean()
    cv_data.append({'特征': feat, '变异系数': round(cv, 3)})
cv_df = pd.DataFrame(cv_data).sort_values('变异系数', ascending=False)
print("各特征变异系数(离散程度):")
print(cv_df)
plt.figure(figsize=(8, 5))
sns.barplot(data=cv_df, x='变异系数', y='特征', hue='特征', palette='viridis')
# 添加数值标签
for index, value in enumerate(cv_df['变异系数']):
    plt.text(x=value + 0.001, y=index, s=value)

plt.title('各特征的变异系数(CV = 标准差 / 均值)')
plt.xlabel('变异系数')
plt.ylabel('特征')
plt.tight_layout()
plt.savefig('变异系数条形图.png')

从图中可明确特征的离散程度差异,花瓣宽度的变异系数达0.636,花瓣长度的变异系数为0.47,二者是所有特征中离散程度最高的------这意味着不同样本的花瓣尺寸差异显著,与之前单特征分布的"双峰"规律一致;花萼宽度(0.143)与花萼长度(0.142)的变异系数接近且数值较小,说明这两个特征在样本间的波动相对温和,个体差异不明显。

这一结果进一步强化了"花瓣特征是区分鸢尾花群体核心依据"的结论,高变异系数的特征包含更丰富的群体差异信息,将成为K-Means聚类中"簇划分"的主要驱动维度;而低变异系数的萼片特征则更多体现样本的共性,对聚类结果的影响相对有限。

4. 数据预处理

K-Means聚类算法基于欧氏距离计算样本相似度,对特征的尺度和分布敏感------若不同特征的数值范围差异较大(如"花瓣长度"以厘米为单位,"花瓣宽度"以毫米为单位),会导致距离计算偏向数值范围大的特征,影响聚类结果的公平性。因此,在建模前需对数据进行标准化预处理,消除特征尺度差异,同时验证数据无异常值干扰,确保聚类结果的可靠性。

4.1 异常值检测与处理

异常值会干扰K-Means聚类中心的计算(聚类对极值敏感),因此需在建模前通过箱线图法识别并评估异常值的影响。箱线图基于四分位距(IQR)判定异常值,当样本值超出"Q1-1.5×IQR"(下界)或"Q3+1.5×IQR"(上界)时,判定为异常值。本次分析对4个形态特征分别绘制箱线图,并定量统计异常值数量与占比。

python 复制代码
# ====================异常值检测与处理====================
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# 提取4个形态特征
features = df_iris.columns[:-2]

# 绘制箱线图检测异常值
plt.figure(figsize=(12, 8))
for i, feat in enumerate(features):
    plt.subplot(2, 2, i + 1)
    sns.boxplot(data=df_iris, y=feat, color='lightblue')
    plt.title(f'{feat} 箱线图(异常值检测)')
    plt.xlabel(feat)

plt.tight_layout()
plt.savefig('特征箱线图_异常值检测.png')

# 定量计算异常值数量
outlier_info = {}
for feat in features:
    # 计算四分位数和IQR
    Q1 = df_iris[feat].quantile(0.25)
    Q3 = df_iris[feat].quantile(0.75)
    IQR = Q3 - Q1
    # 异常值判定条件
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    # 统计异常值数量
    outliers = df_iris[(df_iris[feat] < lower_bound) | (df_iris[feat] > upper_bound)]
    outlier_info[feat] = {
        '下界': round(lower_bound, 2),
        '上界': round(upper_bound, 2),
        '异常值数量': len(outliers),
        '异常值占比': f'{round(len(outliers) / len(df_iris) * 100, 2)}%'
    }

# 输出异常值统计结果
outlier_df = pd.DataFrame(outlier_info).T
print("=== 各特征异常值统计 ===")
print(outlier_df)

从图中可以看到,箱线图中仅"花萼宽度"出现少量离散的异常值点,花萼长度、花瓣长度、花瓣宽度的箱线图无明显超出上下界的点;

从图中可以看到,花萼宽度的异常值数量为4个,占比不足3%,其余特征无异常值。结合鸢尾花的生物学特性,花萼宽度的异常值属于"合理的个体形态差异"(数值仅略偏离正常区间),并非数据错误。考虑到异常值占比极低且不影响整体数据分布,本次选择保留异常值,避免因删除样本破坏数据集的完整性,同时保证后续聚类结果更贴合实际样本的形态差异。

4.2 特征标准化

K-Means聚类的核心是基于样本间的距离(如欧氏距离)划分簇,而特征尺度差异会导致距离计算偏向数值范围大的特征(如鸢尾花的花瓣长度数值波动大于花萼宽度,未标准化时会主导距离计算)。因此,需通过标准化消除特征尺度的影响,保证各特征在聚类中权重均衡。本次采用Z-Score标准化,将4个形态特征转换为"均值为0、标准差为1"的标准正态分布。

python 复制代码
# ====================特征标准化====================

from sklearn.preprocessing import StandardScaler

# 提取特征矩阵(仅保留4个形态特征)
X = df_iris[features].values

# 初始化标准化器
scaler = StandardScaler()
# 拟合并转换数据
X_scaled = scaler.fit_transform(X)

# 将标准化后的数据转换为DataFrame,便于后续分析
df_iris_scaled = pd.DataFrame(
    X_scaled,
    columns=features
)
# 保留标签列(用于后续对比聚类结果)
df_iris_scaled['species'] = df_iris['species']
df_iris_scaled['species_name'] = df_iris['species_name']

# 验证标准化效果
print("标准化后特征统计:")
print(df_iris_scaled[features].describe().T.round(2))

从标准化后的特征统计结果可见,所有特征的均值均接近0,标准差统一为1,成功消除了原特征间的尺度差异;各特征的极值(min/max)分布在-2.5~3.5区间内,数值范围一致,确保后续距离计算中各特征的贡献度对等。标准化后的特征矩阵既保留了原特征的分布形态与相对差异,又避免了尺度偏差对聚类结果的干扰,为K-Means聚类提供了公平的特征输入。

5. 模型构建(模型选择、训练)

5.1 确定最优聚类数k

聚类数k是K-Means的核心参数,直接决定簇的划分结果。本次通过肘部法则 (基于惯性值)和轮廓系数(基于簇内紧凑度与簇间分离度)结合的方式,从"簇内相似度"和"簇间区分度"两个维度筛选最优k值,测试范围为2~10。

python 复制代码
# ====================肘部法和轮廓系数法则确定最优k值====================

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score

# 测试k值范围:2~10
k_range = range(2, 11)
# 存储不同k值的惯性值
inertias = []
# 存储不同k值的轮廓系数
sil_scores = []

for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    kmeans.fit(X_scaled)
    inertias.append(kmeans.inertia_)
    labels = kmeans.predict(X_scaled)
    sil_score = silhouette_score(X_scaled, labels)
    sil_scores.append(sil_score)

df_k = pd.DataFrame({
    'k': k_range,
    '惯性值': inertias,
    '轮廓系数': sil_scores
})

print(f"各k值对应的惯性值和轮廓系数:\n{df_k}")

# 绘制肘部法则图
plt.figure(figsize=(14, 5))
plt.subplot(1, 2, 1)
plt.plot(k_range, inertias, 'o-', color='#2E86AB', linewidth=2, markersize=8)
for x, y in zip(k_range, inertias):
    plt.text(x=x, y=y + 3, s=f'{y:.2f}')
plt.title('K-Means肘部法则图(惯性值 vs k值)')
plt.xlabel('聚类数k', fontsize=12)
plt.ylabel('惯性值(Inertia)', fontsize=12)
plt.xticks(k_range)
plt.grid(alpha=0.3)

# 绘制轮廓系数曲线
plt.subplot(1, 2, 2)
plt.plot(k_range, sil_scores, 'o-', color='#F18F01', linewidth=2, markersize=8)
for x, y in zip(k_range, sil_scores):
    plt.text(x=x, y=y + 0.01, s=f'{y:.3f}', ha='center')
plt.title('K-Means轮廓系数图(轮廓系数 vs k值)', fontsize=14, fontweight='bold')
plt.xlabel('聚类数k', fontsize=12)
plt.ylabel('轮廓系数(Silhouette Score)', fontsize=12)
plt.xticks(k_range)
plt.grid(alpha=0.3)

plt.savefig('KMeans肘部法则图和轮廓系数图.png')

肘部法则(惯性值分析) :惯性值代表簇内所有样本到其簇中心的欧氏距离平方和,值越小说明簇内样本越紧凑;当k从2增加到4时,惯性值从222.36快速下降(k=2→3:下降83.08;k=3→4:下降25.73),说明簇内紧凑度提升明显;当k≥4后,惯性值的下降幅度逐渐放缓(如k=4→5仅下降19.16),曲线出现"肘部拐点",提示k=3~4是簇内紧凑度的合理临界点。

轮廓系数分析:轮廓系数的取值范围为[-1,1],值越接近1说明簇内样本越紧凑、簇间区分度越高;当k=2时,轮廓系数达0.682(最高值),但对应簇数与鸢尾花实际3个品种不符;当k=3时,轮廓系数为0.460(处于较高水平),既保证了簇的区分度,又与数据的实际类别数量一致;当k>3后,轮廓系数持续下降(k=6时降至0.317),说明簇划分过细导致簇间差异模糊。

结合鸢尾花数据集的实际品种数(3类),最终确定最优聚类数k=3------该k值既满足肘部法则的紧凑度要求,又具备较高的轮廓系数,同时契合数据的真实类别分布。

5.2 K-Means聚类模型构建

基于最优聚类数( k=3 ),初始化K-Means模型并在标准化后的特征矩阵上完成训练,输出聚类标签、簇中心(含标准化与原始尺度)等核心结果,为后续分析提供依据。

python 复制代码
# ====================K-Means模型训练====================

from sklearn.cluster import KMeans

# 初始化K-Means模型(最优k=3)
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)

# 训练模型(输入标准化后的特征矩阵)
kmeans.fit(X_scaled)

# 获取聚类核心结果
cluster_labels = kmeans.labels_  # 所有样本的聚类标签(0/1/2)
cluster_centers = kmeans.cluster_centers_  # 3个簇的中心(标准化后,维度:3×4)
inertia = kmeans.inertia_  # 最终惯性值(簇内紧凑度指标)

# 将聚类标签合并到预处理后的DataFrame,便于后续分析
df_iris_scaled['cluster_label'] = cluster_labels
df_iris_scaled.to_csv('标准化后的聚类结果.csv', index=False)
df_iris_new = df_iris.copy()
df_iris_new['cluster_label'] = cluster_labels
df_iris_new.to_csv('原始数据的聚类结果.csv', index=False)

# 输出训练结果摘要
print("=== K-Means模型训练结果 ===")
print(f"最终聚类标签分布:\n{pd.Series(cluster_labels).value_counts().sort_index()}")
print(f"\n模型最终惯性值:{inertia:.2f}")
print(f"\n标准化后的簇中心:")
cluster_centers_scaled = pd.DataFrame(
    cluster_centers,
    columns=df_iris.columns[:-2],  # 特征名称
    index=[f'簇{i + 1}' for i in range(3)]  # 簇名称
)
print(cluster_centers_scaled.round(3))

# 簇中心逆标准化(还原为原始尺度cm,便于业务解读)
cluster_centers_original = scaler.inverse_transform(cluster_centers)
cluster_centers_original = pd.DataFrame(
    cluster_centers_original,
    columns=df_iris.columns[:-2],
    index=[f'簇{i + 1}' for i in range(3)]
)
print(f"\n原始尺度的簇中心(单位:cm):")
print(cluster_centers_original.round(2))

聚类标签分布:最终聚类标签(0/1/2)的样本数量分布相对均衡,簇0包含53个样本、簇1包含50个样本、簇2包含47个样本,无极端不平衡的簇划分,说明聚类结果的样本分布较为合理。

簇中心解读 :簇中心是每个簇的"代表样本",分别提供标准化与原始尺度两种形式(原始尺度更贴合业务认知)。标准化后的簇中心 体现各特征在簇内的相对偏离程度(如簇1的花瓣长度、宽度标准化值均接近-1.3,远低于均值;簇2的花瓣长度、宽度标准化值接近1.0,远高于均值);原始尺度的簇中心(单位:cm) 中,簇1的花萼宽度(3.43)最大,花瓣长度(1.46)、宽度(0.25)最小,对应"山鸢尾"的形态特征;簇2的花萼长度(6.78)、花瓣长度(5.51)、花瓣宽度(1.97)均最大,对应"维吉尼亚鸢尾"的形态特征;簇0的各特征数值介于簇1与簇2之间,对应"变色鸢尾"的形态特征。

这一结果与鸢尾花的实际品种形态规律高度一致,说明K-Means模型成功捕捉到了数据的天然群体划分。

6. 模型评估

K-Means聚类为无监督学习,评估需结合"量化指标(簇内紧凑度+簇间分离度)""真实标签匹配度""可视化验证"三个维度,全面验证聚类效果的合理性。

6.1 核心评估指标

通过惯性值、轮廓系数、DBI指数、CHI指数四个指标,从"簇内紧凑度""综合质量""簇间分离度""方差占比"四个维度全面评估K-Means聚类结果的质量。

  • 惯性值(Inertia):簇内紧凑度,值越小越好;
  • 轮廓系数(Silhouette Score):综合簇内紧凑度与簇间分离度,越接近1越好;
  • 戴维斯-布尔丁指数(DBI):簇间分离度与簇内紧凑度的比值,值越小越好;
  • 卡尔斯基-哈拉巴斯指数(CHI):簇间方差与簇内方差的比值,值越大越好。
python 复制代码
# ====================核心评估指标====================

from sklearn.metrics import silhouette_score, davies_bouldin_score, calinski_harabasz_score

print("=== 核心评估指标 ===")
# 计算核心量化指标
print(f"惯性值(簇内紧凑度): {round(inertia, 2)}")
print(f"轮廓系数(综合质量): {round(silhouette_score(X_scaled, cluster_labels), 3)}")
print(f"DBI指数(簇间分离度): {round(davies_bouldin_score(X_scaled, cluster_labels), 3)}")
print(f"CHI指数(簇间/簇内方差比): {round(calinski_harabasz_score(X_scaled, cluster_labels), 2)}")
  • 惯性值(139.82):代表簇内所有样本到其簇中心的距离平方和,该值处于合理区间(结合k=3的肘部法则结果),说明簇内样本的紧凑度较好;
  • 轮廓系数(0.46):处于[0,1]区间的中等水平,既体现了簇内样本的紧凑性,也保证了簇间的区分度,符合聚类任务的预期质量;
  • DBI指数(0.834):该指数越小代表簇间分离度越好,0.834的数值说明各簇之间的重叠程度较低,划分边界清晰;
  • CHI指数(241.9):代表簇间方差与簇内方差的比值,值越大说明簇间差异越显著,241.9的高数值进一步验证了聚类结果的有效性。

综合来看,这组指标共同表明本次K-Means聚类(k=3)的结果既保证了簇内样本的聚集性,又实现了簇间样本的区分度,整体聚类质量良好。

6.2 聚类结果可视化

6.2.1 聚类标签分布可视化

聚类标签分布可反映各簇的样本数量均衡性,是评估聚类结果合理性的基础指标之一。本次通过柱状图展示3个簇的样本数量分布。

python 复制代码
# ====================聚类标签分布可视化====================

df_cluster_labels = pd.Series(cluster_labels).value_counts().reset_index()
df_cluster_labels.columns = ['类别', '数量']
df_cluster_labels['类别'] = df_cluster_labels['类别'].map({
    0: '簇1',
    1: '簇2',
    2: '簇3'
})
print("\n聚类标签分布:")
print(df_cluster_labels)

plt.figure(figsize=(6, 4))
sns.barplot(x='类别', hue='类别', y='数量', data=df_cluster_labels, palette='Set3')
for x, y in zip(df_cluster_labels['类别'], df_cluster_labels['数量']):
    plt.text(x=x, y=y, s=y, ha='center', va='bottom')
plt.title('聚类标签分布')
plt.xlabel('类别')
plt.ylabel('数量')
plt.savefig('./聚类标签分布柱状图.png')

从图中可见,簇1包含53个样本、簇2包含50个样本、簇3包含47个样本------各簇的样本数量差异较小(最大差值仅6个),分布相对均衡,无"簇样本量极少/极多"的极端情况。这种均衡的分布说明K-Means聚类(k=3)未出现"簇划分偏向某一群体"的问题,进一步验证了聚类结果的稳定性与合理性。

6.2.2 聚类效果可视化

采用散点图矩阵(Pairplot)结合对角线核密度图,从"特征两两组合+单特征分布"双维度直观展示聚类效果,不同颜色代表不同簇。

python 复制代码
# ====================聚类效果可视化散点图====================

plt.figure(figsize=(8, 8))
feature_names = ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)', 'cluster_label']
# 绘制所有特征的散点图矩阵
sns.pairplot(
    data=df_iris_scaled[feature_names],
    hue='cluster_label',
    palette='Set2',
    corner=True,  # 只显示下三角,避免重复
    diag_kind='kde'  # 对角线用核密度图
)
plt.tight_layout()
plt.savefig('KMeans聚类效果散点图.png')
  • 散点图区域(下三角):不同簇的样本在特征组合下呈现明显的聚集性------以"花瓣长度-花瓣宽度"组合为例,橙色样本(簇1)集中在左下角、青色样本(簇0)集中在中间区域、蓝色样本(簇2)集中在右上角,三类几乎无重叠,体现了簇间的强区分度;
  • 核密度图区域(对角线):各簇的单特征分布边界清晰(如花瓣长度的核密度图呈现三个独立峰值),与聚类前的单特征分布规律一致,验证了簇内样本的紧凑性。

这一可视化结果直观印证了K-Means聚类的有效性,样本按形态特征自然划分为三个独立群体,与鸢尾花的实际品种分类高度契合。

6.2.3 原始尺度的簇中心可视化

为更直观地对比不同簇的形态特征差异,将3个簇的原始尺度中心以水平柱状图展示(不同颜色代表不同簇),并标注具体特征数值。

python 复制代码
# ====================原始尺度的簇中心可视化====================

import numpy as np

# 簇中心特征柱状图(原始尺度)
plt.figure(figsize=(12, 8))
ax = cluster_centers_original.T.plot(
    kind='barh',
    color=['#FF6B6B', '#4ECDC4', '#45B7D1'],
    width=0.7,
    figsize=(12, 8)
)
# 添加数值文本注释
for container in ax.containers:
    ax.bar_label(container, fmt='%.2f', padding=3, fontsize=10)

plt.title('3个簇中心特征对比(原始尺度,单位:cm)', fontsize=14, fontweight='bold')
plt.xlabel('特征值(cm)', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.legend(title='聚类簇', loc='upper right')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('簇中心特征对比条形图.png')

从图中可清晰区分各簇的核心形态特征:

  • 簇2(青绿色):花萼宽度(3.43cm)显著大于另外两个簇,花瓣长度(1.46cm)、花瓣宽度(0.25cm)为三者中最小,对应"山鸢尾"的紧凑花瓣特征;
  • 簇3(浅蓝色):花萼长度(6.78cm)、花瓣长度(5.51cm)、花瓣宽度(1.97cm)均为三者中最大,对应"维吉尼亚鸢尾"的舒展花部形态;
  • 簇1(红色):所有特征数值均介于簇2与簇3之间(如花瓣长度4.37cm、花瓣宽度1.41cm),对应"变色鸢尾"的过渡形态特征。

这种特征差异与鸢尾花实际品种的形态规律完全吻合,直观验证了K-Means聚类结果的合理性------簇的划分准确捕捉了不同品种鸢尾花的核心形态差异。

6.2.4 标准化后的簇中心可视化

为消除特征尺度差异的干扰,采用雷达图展示标准化后的簇中心特征,更直观地对比各簇在不同特征维度上的相对差异(雷达图的径向距离代表特征偏离均值的程度)。

python 复制代码
# ====================标准化后的簇中心可视化====================

# 簇中心特征雷达图(标准化尺度,消除量纲影响)
plt.figure(figsize=(10, 8))
# 标准化后的簇中心数据
centers_scaled = cluster_centers
# 特征名称
features = df_iris.columns[:-2]
# 雷达图角度设置
angles = np.linspace(start=0, stop=2 * np.pi, num=len(features), endpoint=False).tolist()
angles += angles[:1]  # 闭合雷达图

# 循环绘制每个簇的雷达图
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
for i, (center, color) in enumerate(zip(centers_scaled, colors)):
    values = center.tolist()
    values += values[:1]  # 闭合数据
    plt.polar(angles, values, label=f'簇{i + 1}', color=color, linewidth=2, marker='o')
    plt.fill(angles, values, color=color, alpha=0.2)

# 调整雷达图样式
plt.xticks(ticks=angles[:-1], labels=features, fontsize=11)
# plt.yticks([])  # 隐藏径向刻度
plt.title('簇中心特征雷达图(标准化尺度)', fontsize=14, pad=20)
plt.legend()
plt.tight_layout()
plt.savefig('簇中心特征雷达图.png')

从图中可观察到各簇的"特征轮廓"差异显著:

  • 簇2(青绿色):在"花萼宽度"维度显著向外延伸(偏离均值最多),但在"花瓣长度""花瓣宽度"维度明显向内收缩(远低于均值),形成了"宽萼片+窄花瓣"的特征轮廓;
  • 簇3(浅蓝色):在"花萼长度""花瓣长度""花瓣宽度"维度均大幅向外延伸,是所有簇中特征值整体偏高的簇,对应"大尺寸花部"的轮廓;
  • 簇1(红色):各特征维度的径向距离均介于簇2与簇3之间,呈现"中等尺寸花部"的均衡轮廓。

这种标准化后的特征轮廓差异,进一步验证了K-Means聚类对不同鸢尾花群体的区分能力------各簇的特征组合模式清晰,聚类结果的簇间区分度较高。


五、完整代码

1. 完整代码

python 复制代码
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.datasets import load_iris

# 设置中文字体正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置Pandas全局选项
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', 50)
pd.set_option('display.expand_frame_repr', False)

# ====================加载数据====================

# 加载鸢尾花数据集
iris = load_iris()
# 转换为DataFrame
df_iris = pd.DataFrame(
    data=iris.data,  # 特征矩阵
    columns=iris.feature_names  # 特征名称
)
# 添加真实品种标签列(用于后续对比聚类结果)
df_iris['species'] = iris.target
# 将数字标签映射为品种名称(提升可读性)
df_iris['species_name'] = (df_iris['species'].map({
    0: '山鸢尾',
    1: '变色鸢尾',
    2: '维吉尼亚鸢尾'
}))

# ====================探索性数据分析====================
# ====================数据基本信息====================

# 基础信息探索
print("=== 鸢尾花数据集基础信息 ===")
print(f"数据集形状:{df_iris.shape}(样本数×特征数)")
print("\n=== 数据前5行预览 ===")
print(df_iris.head())

print("\n=== 数据类型与缺失值检查 ===")
print(df_iris.info())  # 无缺失值,特征均为浮点型

print("\n=== 特征统计描述 ===")
print(df_iris.describe().round(2))  # 查看特征的均值、标准差等

print("\n=== 各品种样本数量分布 ===")
print(df_iris['species_name'].value_counts())

# ====================单特征分布分析====================

import matplotlib.pyplot as plt
import seaborn as sns

# 绘制核密度图,KDE:核密度估计
plt.figure(figsize=(15, 10))
feature = df_iris.columns[:-2]
for i in range(len(feature)):
    plt.subplot(2, 2, i + 1)
    sns.kdeplot(
        data=df_iris,
        x=feature[i],
        fill=True,
        common_norm=False,  # 每个类别单独归一化
        alpha=0.5
    )
    plt.title(f'按类别的 {feature[i]} 核密度估计图')
    plt.xlabel(feature[i])
plt.tight_layout()
plt.savefig('单特征分布核密度估计图.png')

# ===================特征间相关性分析====================

import matplotlib.pyplot as plt
import seaborn as sns

features = df_iris.columns[:-2]
# 全局特征相关性热力图(不区分品种)
plt.figure(figsize=(10, 8))
# 计算特征间的皮尔逊相关系数
corr_matrix = df_iris[features].corr(method='spearman')
# 绘制热力图
mask = np.triu(m=np.ones_like(corr_matrix, dtype=bool), k=1)  # 隐藏上三角(避免重复)
sns.heatmap(data=corr_matrix, mask=mask, annot=True, fmt='.2f', cmap='coolwarm', linewidths=0.5)
plt.title('鸢尾花特征相关性热力图', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig('特征间相关性.png')

# ===================特征变异系数分析====================

# 计算各特征的变异系数(反映离散程度,变异系数=标准差/均值)
cv_data = []
for feat in df_iris.columns[:-2]:
    cv = df_iris[feat].std() / df_iris[feat].mean()
    cv_data.append({'特征': feat, '变异系数': round(cv, 3)})
cv_df = pd.DataFrame(cv_data).sort_values('变异系数', ascending=False)
print("各特征变异系数(离散程度):")
print(cv_df)
plt.figure(figsize=(8, 5))
sns.barplot(data=cv_df, x='变异系数', y='特征', hue='特征', palette='viridis')
# 添加数值标签
for index, value in enumerate(cv_df['变异系数']):
    plt.text(x=value + 0.001, y=index, s=value)

plt.title('各特征的变异系数(CV = 标准差 / 均值)')
plt.xlabel('变异系数')
plt.ylabel('特征')
plt.tight_layout()
plt.savefig('变异系数条形图.png')

# ====================异常值检测与处理====================
import seaborn as sns
import matplotlib.pyplot as plt

# 提取4个形态特征
features = df_iris.columns[:-2]

# 绘制箱线图检测异常值
plt.figure(figsize=(12, 8))
for i, feat in enumerate(features):
    plt.subplot(2, 2, i + 1)
    sns.boxplot(data=df_iris, y=feat, color='lightblue')
    plt.title(f'{feat} 箱线图(异常值检测)')
    plt.xlabel(feat)

plt.tight_layout()
plt.savefig('特征箱线图_异常值检测.png')

# 定量计算异常值数量
outlier_info = {}
for feat in features:
    # 计算四分位数和IQR
    Q1 = df_iris[feat].quantile(0.25)
    Q3 = df_iris[feat].quantile(0.75)
    IQR = Q3 - Q1
    # 异常值判定条件
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    # 统计异常值数量
    outliers = df_iris[(df_iris[feat] < lower_bound) | (df_iris[feat] > upper_bound)]
    outlier_info[feat] = {
        '下界': round(lower_bound, 2),
        '上界': round(upper_bound, 2),
        '异常值数量': len(outliers),
        '异常值占比': f'{round(len(outliers) / len(df_iris) * 100, 2)}%'
    }

# 输出异常值统计结果
outlier_df = pd.DataFrame(outlier_info).T
print("=== 各特征异常值统计 ===")
print(outlier_df)

# ====================特征标准化====================

from sklearn.preprocessing import StandardScaler

# 提取特征矩阵(仅保留4个形态特征)
X = df_iris[features].values

# 初始化标准化器
scaler = StandardScaler()

# 拟合并转换数据
X_scaled = scaler.fit_transform(X)

# 将标准化后的数据转换为DataFrame,便于后续分析
df_iris_scaled = pd.DataFrame(
    X_scaled,
    columns=features
)
# 保留标签列(用于后续对比聚类结果)
df_iris_scaled['species'] = df_iris['species']
df_iris_scaled['species_name'] = df_iris['species_name']

# 验证标准化效果
print("标准化后特征统计:")
print(df_iris_scaled[features].describe().T.round(2))

# ====================肘部法和轮廓系数法则确定最优k值====================

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score

# 测试k值范围:2~10
k_range = range(2, 11)
# 存储不同k值的惯性值
inertias = []
# 存储不同k值的轮廓系数
sil_scores = []

for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    kmeans.fit(X_scaled)
    inertias.append(kmeans.inertia_)
    labels = kmeans.predict(X_scaled)
    sil_score = silhouette_score(X_scaled, labels)
    sil_scores.append(sil_score)

df_k = pd.DataFrame({
    'k': k_range,
    '惯性值': inertias,
    '轮廓系数': sil_scores
})

print(f"各k值对应的惯性值和轮廓系数:\n{df_k}")

# 绘制肘部法则图
plt.figure(figsize=(14, 5))
plt.subplot(1, 2, 1)
plt.plot(k_range, inertias, 'o-', color='#2E86AB', linewidth=2, markersize=8)
for x, y in zip(k_range, inertias):
    plt.text(x=x, y=y + 3, s=f'{y:.2f}')
plt.title('K-Means肘部法则图(惯性值 vs k值)')
plt.xlabel('聚类数k', fontsize=12)
plt.ylabel('惯性值(Inertia)', fontsize=12)
plt.xticks(k_range)
plt.grid(alpha=0.3)

# 绘制轮廓系数曲线
plt.subplot(1, 2, 2)
plt.plot(k_range, sil_scores, 'o-', color='#F18F01', linewidth=2, markersize=8)
for x, y in zip(k_range, sil_scores):
    plt.text(x=x, y=y + 0.01, s=f'{y:.3f}', ha='center')
plt.title('K-Means轮廓系数图(轮廓系数 vs k值)', fontsize=14, fontweight='bold')
plt.xlabel('聚类数k', fontsize=12)
plt.ylabel('轮廓系数(Silhouette Score)', fontsize=12)
plt.xticks(k_range)
plt.grid(alpha=0.3)

plt.savefig('KMeans肘部法则图和轮廓系数图.png')

# ====================K-Means模型训练====================

from sklearn.cluster import KMeans

# 初始化K-Means模型(最优k=3)
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)

# 训练模型(输入标准化后的特征矩阵)
kmeans.fit(X_scaled)

# 获取聚类核心结果
cluster_labels = kmeans.labels_  # 所有样本的聚类标签(0/1/2)
cluster_centers = kmeans.cluster_centers_  # 3个簇的中心(标准化后,维度:3×4)
inertia = kmeans.inertia_  # 最终惯性值(簇内紧凑度指标)

# 将聚类标签合并到预处理后的DataFrame,便于后续分析
df_iris_scaled['cluster_label'] = cluster_labels
df_iris_scaled.to_csv('标准化后的聚类结果.csv', index=False)
df_iris_new = df_iris.copy()
df_iris_new['cluster_label'] = cluster_labels
df_iris_new.to_csv('原始数据的聚类结果.csv', index=False)

# 输出训练结果摘要
print("=== K-Means模型训练结果 ===")
print(f"最终聚类标签分布:\n{pd.Series(cluster_labels).value_counts().sort_index()}")
print(f"\n模型最终惯性值:{inertia:.2f}")
print(f"\n标准化后的簇中心:")
cluster_centers_scaled = pd.DataFrame(
    cluster_centers,
    columns=df_iris.columns[:-2],  # 特征名称
    index=[f'簇{i + 1}' for i in range(3)]  # 簇名称
)
print(cluster_centers_scaled.round(3))

# 簇中心逆标准化(还原为原始尺度cm,便于业务解读)
cluster_centers_original = scaler.inverse_transform(cluster_centers)
cluster_centers_original = pd.DataFrame(
    cluster_centers_original,
    columns=df_iris.columns[:-2],
    index=[f'簇{i + 1}' for i in range(3)]
)
print(f"\n原始尺度的簇中心(单位:cm):")
print(cluster_centers_original.round(2))

# ====================核心评估指标====================

from sklearn.metrics import silhouette_score, davies_bouldin_score, calinski_harabasz_score

print("=== 核心评估指标 ===")
# 计算核心量化指标
print(f"惯性值(簇内紧凑度): {round(inertia, 2)}")
print(f"轮廓系数(综合质量): {round(silhouette_score(X_scaled, cluster_labels), 3)}")
print(f"DBI指数(簇间分离度): {round(davies_bouldin_score(X_scaled, cluster_labels), 3)}")
print(f"CHI指数(簇间/簇内方差比): {round(calinski_harabasz_score(X_scaled, cluster_labels), 2)}")

# ====================聚类标签分布可视化====================

df_cluster_labels = pd.Series(cluster_labels).value_counts().reset_index()
df_cluster_labels.columns = ['类别', '数量']
df_cluster_labels['类别'] = df_cluster_labels['类别'].map({
    0: '簇1',
    1: '簇2',
    2: '簇3'
})
print("\n聚类标签分布:")
print(df_cluster_labels)

plt.figure(figsize=(6, 4))
sns.barplot(x='类别', hue='类别', y='数量', data=df_cluster_labels, palette='Set3')
for x, y in zip(df_cluster_labels['类别'], df_cluster_labels['数量']):
    plt.text(x=x, y=y, s=y, ha='center', va='bottom')
plt.title('聚类标签分布')
plt.xlabel('类别')
plt.ylabel('数量')
plt.savefig('./聚类标签分布柱状图.png')

# ====================聚类效果可视化散点图====================

plt.figure(figsize=(8, 8))
feature_names = ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)', 'cluster_label']
# 绘制所有特征的散点图矩阵(快速查看多特征关系)
sns.pairplot(
    data=df_iris_scaled[feature_names],
    hue='cluster_label',
    palette='Set2',
    corner=True,  # 只显示下三角,避免重复
    diag_kind='kde'  # 对角线用核密度图
)
plt.tight_layout()
plt.savefig('KMeans聚类效果散点图.png')

# ====================原始尺度的簇中心可视化====================

import numpy as np

# 簇中心特征柱状图(原始尺度)
plt.figure(figsize=(12, 8))
ax = cluster_centers_original.T.plot(
    kind='barh',
    color=['#FF6B6B', '#4ECDC4', '#45B7D1'],
    width=0.7,
    figsize=(12, 8)
)
# 添加数值文本注释
for container in ax.containers:
    ax.bar_label(container, fmt='%.2f', padding=3, fontsize=10)

plt.title('3个簇中心特征对比(原始尺度,单位:cm)', fontsize=14, fontweight='bold')
plt.xlabel('特征值(cm)', fontsize=12)
plt.ylabel('特征名称', fontsize=12)
plt.legend(title='聚类簇', loc='upper right')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('簇中心特征对比条形图.png')

# ====================标准化后的簇中心可视化====================

# 簇中心特征雷达图(标准化尺度,消除量纲影响)
plt.figure(figsize=(10, 8))
# 标准化后的簇中心数据
centers_scaled = cluster_centers
# 特征名称
features = df_iris.columns[:-2]
# 雷达图角度设置
angles = np.linspace(start=0, stop=2 * np.pi, num=len(features), endpoint=False).tolist()
angles += angles[:1]  # 闭合雷达图

# 循环绘制每个簇的雷达图
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
for i, (center, color) in enumerate(zip(centers_scaled, colors)):
    values = center.tolist()
    values += values[:1]  # 闭合数据
    plt.polar(angles, values, label=f'簇{i + 1}', color=color, linewidth=2, marker='o')
    plt.fill(angles, values, color=color, alpha=0.2)

# 调整雷达图样式
plt.xticks(ticks=angles[:-1], labels=features, fontsize=11)
# plt.yticks([])  # 隐藏径向刻度
plt.title('簇中心特征雷达图(标准化尺度)', fontsize=14, pad=20)
plt.legend()
plt.tight_layout()
plt.savefig('簇中心特征雷达图.png')
相关推荐
CoderYanger1 小时前
A.每日一题——2141.同时运行N台电脑的最长时间
java·算法·leetcode·职场和发展·1024程序员节
Ayanami_Reii1 小时前
进阶数据结构-线段树
数据结构·算法·线段树
liu****1 小时前
11.字符函数和字符串函数(一)
linux·运维·c语言·开发语言·数据结构·算法
aini_lovee1 小时前
基于UERD算法的JPEG图像隐写MATLAB实现
开发语言·算法·matlab
橘颂TA1 小时前
【剑斩OFFER】算法的暴力美学——Z字行变换
算法·leetcode·职场和发展·结构与算法
minji...1 小时前
linux 进程控制(一) (fork进程创建,exit进程终止)
linux·运维·服务器·c++·git·算法
埃伊蟹黄面1 小时前
双指针算法
数据结构·c++·算法
java修仙传1 小时前
力扣hot100:反转链表
算法·leetcode·链表
Elias不吃糖1 小时前
Leetcode-10.正则表达式匹配(暴力 或 记忆暴力)
数据结构·c++·算法·leetcode·深度优先