ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

K-Means聚类鸢尾花:数据预处理到评估可视化完整实践

K-Means聚类鸢尾花:数据预处理到评估可视化完整实践 简介这是一份基于K-Means聚类算法处理经典鸢尾花Iris数据集的Python实践项目适合机器学习入门者理解无监督学习与聚类分析流程。压缩包内共2个文件iris_all.csv提供三种鸢尾花共150条样本的四个特征测量值IrisCluster.py则实现数据预处理、K-Means聚类、SSE等指标评估及散点图可视化整体体积仅2KB轻量易用。通过运行脚本可观察不同簇的划分效果并借助肘部法则或轮廓系数调整K值掌握从数据加载到聚类结果解读的完整链路。项目虽小但结构清晰既可作为课堂实验模板也能为后续学习更复杂聚类算法打下基础。已有156人浏览学习适合初学者快速上手。1. 用K-Means跑通鸢尾花聚类一份能直接改的代码比教程更值钱Iris数据集搭配K-Means聚类几乎是每个学机器学习的人都绕不过去的组合。这份资源解压后就是两个文件iris_all.csv和IrisCluster.py前者是经典的鸢尾花测量数据后者是用Python实现K-Means聚类的完整脚本。你拿来就能跑跑完能出聚类结果、能算评估指标、能画散点图整个过程不需要额外搭建环境装好pandas、scikit-learn、matplotlib就能直接运行。我拆这份资源时最大的感受是它不像很多教程只贴几段代码让人自己拼而是把加载数据、预处理、聚类、评估、可视化串成了一条完整的链路。对于刚开始接触无监督学习的开发者这是一个能让你看到聚类全流程的活样本对于有经验的数据分析师它也能当作快速验证K-Means参数行为的脚手架。这篇笔记我会把文件里涉及的每个环节拆开讲从数据格式到聚类原理再到代码逐段解读和踩坑记录你跟着走一遍就能完全掌握之后换成自己的数据也只是改改路径和参数的事。2. Iris数据集与K-Means选型为什么这个组合适合用来建立聚类直觉2.1 数据集结构与文件格式先搞清楚iris_all.csv里装了什么iris_all.csv是本次聚类的原始数据。Iris数据集由英国统计学家R.A. Fisher在1936年提出包含三种鸢尾花——Setosa、Versicolour、Virginica每种50个样本每个样本记录四个特征花萼长度sepal length、花萼宽度sepal width、花瓣长度petal length、花瓣宽度petal width单位是厘米。这是机器学习领域最经典的数据集之一特点在于三类样本中有一类线性可分另外两类存在部分重叠恰好适合用来观察聚类算法的边界表现。拿到CSV文件后我建议先用下面这段代码确认数据结构和基本统计信息import pandas as pd df pd.read_csv(iris_all.csv) print(df.head()) print(df.info()) print(df.describe())提示如果打印df.head()发现列名不是约定俗成的sepal_length、sepal_width、petal_length、petal_width大概率是原文件用了缩写或带表头格式差异。用df.columns查看实际列名后续代码里按实际列名引用即可。逻辑说明read_csv()加载数据后head()看前几行确认列名和数值格式info()检查各列是否有缺失值以及数据类型describe()输出每列的均值、标准差、最小最大值帮助你快速判断是否需要做标准化。这个数据集的样本量只有150条不需要考虑抽样和分批直接全量读入内存即可。2.2 K-Means算法原理最小化簇内平方和与初始化策略K-Means的目标是把n个样本划分到K个簇中使得每个样本到其所属簇中心的距离平方和最小。算法流程分四步随机初始化K个簇中心centroid计算每个样本到各簇中心的距离把它归到最近的簇重新计算每个簇内所有样本的均值作为新的簇中心重复第二步和第三步直到簇中心不再变化或达到最大迭代次数。距离度量通常采用欧氏距离这也是K-Means的标准配置。需要注意的是K-Means对初始中心的选择比较敏感不同的初始点可能收敛到不同的局部最优解。scikit-learn的KMeans类在n_init参数上默认执行多次独立初始化最终返回簇内平方和inertia最低的那次结果这能显著降低随机初始化带来的影响。K值的选择是这个算法里最需要人为干预的地方。Iris数据集真实标签是3类但无监督场景下你并不知道这一点。常见做法是画肘部图横轴是K值纵轴是各K值对应的簇内平方和曲线拐弯处的K值就是相对合理的簇数量。我在后面会给出具体实现代码用inertia_属性批量计算K从1到10的簇内平方和。2.3 特征标准化量纲不统一会让距离计算失真四个特征的量纲并不一致花萼长度在4.3到7.9厘米之间波动而花瓣宽度只有0.1到2.5厘米。直接用原始数值计算欧氏距离花瓣宽度对距离的贡献会被花萼长度远远压过等于变相给花萼长度加了更高的权重这显然不是我们想要的结果。标准做法是使用StandardScaler做Z-score标准化让每个特征变成均值0、标准差1的分布。实现代码也很简单from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(df.iloc[:, :4]) print(X_scaled[:5])逻辑说明fit_transform先计算每列的均值和标准差再执行(x - mean) / std的变换返回标准化后的NumPy数组。这里我取df.iloc[:, :4]是因为iris_all.csv可能带有最后一列品种标签K-Means是无监督算法训练时不能把标签传进去。如果文件只有四列特征整份DataFrame直接传也行但保留标签列用于后续对比会更方便。提示标准化不是可选步骤是必须步骤。很多初学者第一次跑K-Means发现聚类结果和真实标签对不上排查半天最后发现只是忘了标准化。3. 跑通IrisCluster.py完整代码逐段拆解与参数调整方法3.1 数据加载与预处理从CSV到可聚类矩阵的完整链路解压后的IrisCluster.py会完成从读取文件到输出可视化结果的整套流程。我把它按功能拆成几个段落解读方便你对照自己手头的代码检查每一步是否一致。第一步是加载数据和特征分离。无论文件里有没有标签列统一用如下方式处理import pandas as pd from sklearn.preprocessing import StandardScaler df pd.read_csv(iris_all.csv) print(数据集形状:, df.shape) # 分离特征和标签如果文件最后一列是品种名 if df.shape[1] 5: X df.iloc[:, :4].values y_true df.iloc[:, 4].values else: X df.values y_true None print(特征矩阵形状:, X.shape)逻辑说明先读取数据再按列数判断是否包含标签列。Iris数据集在多数CSV版本中都是5列最后一列是品种名string类型此时只取前四列作为聚类输入。y_true保存真实标签用于后续的聚类效果对比但在K-Means训练过程中不会用到它。如果你的文件是4列整份矩阵就是特征真实标签无从谈起只能通过轮廓系数这类内部指标评估效果。第二步是标准化。直接使用StandardScaler注意必须在加载数据之后、聚类之前执行from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) print(标准化后特征均值近似0:, X_scaled.mean(axis0).round(6)) print(标准化后特征标准差近似1:, X_scaled.std(axis0).round(6))逻辑说明标准化后的数据每个特征均值接近0、标准差接近1。这两条打印语句是验证标准化是否生效的快速手段如果打印结果离0和1偏差很大可能是原始数据里存在非数值列被fit_transform报错或强转导致的异常。标准化这一步做完K-Means的输入就准备好了。3.2 训练K-Means模型参数逐个解读与K值选择训练模型前先用肘部法则确定合理的K值。Iris数据集的真实类别数是3但你不能在无监督任务里直接拿这个数当答案因为真实业务场景下你通常没有标签可参考。肘部法则的代码实现如下from sklearn.cluster import KMeans import matplotlib.pyplot as plt inertia_list [] for k in range(1, 11): km KMeans(n_clustersk, random_state42, n_init10) km.fit(X_scaled) inertia_list.append(km.inertia_) plt.plot(range(1, 11), inertia_list, markero) plt.xlabel(K值) plt.ylabel(簇内平方和 (Inertia)) plt.title(肘部法则确定最佳K值) plt.grid(True) plt.savefig(elbow.png, dpi120)逻辑说明inertia_是KMeans训练完成后模型自带的属性表示所有样本到所属簇中心的距离平方和。K值越大每个簇内的样本越少inertia自然越小但这个下降幅度会在某个K值后明显变缓这个转折点就是所谓的“肘部”。把这10个点画成折线图保存为elbow.png你就能直观看到曲线在K3附近出现明显拐弯。random_state42固定随机种子保证每次运行结果可复现。K值确定后正式训练K-Means模型的方式如下# 根据肘部图选择K3 k 3 km KMeans(n_clustersk, random_state42, n_init10, max_iter300) km.fit(X_scaled) print(聚类标签:, km.labels_[:10]) print(簇中心:, km.cluster_centers_) print(簇内平方和:, km.inertia_) print(迭代次数:, km.n_iter_)逻辑说明n_init10表示算法会从10个不同的随机初始中心出发各跑一遍最终返回inertia最小的那个结果。random_state42让这10次初始化的随机序列固定下来避免每次运行结果都存在随机偏差。max_iter300限定单次初始化的最大迭代轮次正常情况下Iris这样的小数据集几十轮内就能收敛300轮已经留了充足余量。打印的labels_是每个样本被分配到的簇编号范围是0到K-1cluster_centers_是每个簇中心在标准化空间内的坐标如果你想看它在原始特征空间里的值需要调用scaler.inverse_transform()还原。提示n_clusters设置成几打印出的labels_最大值就是几减1。如果你看到训练警告提示某次初始化收敛失败多半是数据里有异常值或K值选择不合理换成更大的n_init或调整random_state即可。3.3 结果评估与可视化轮廓系数加散点图双验证聚类完成后需要回答两个问题簇内是否紧凑、簇间是否分离。最常用的内部评估指标是轮廓系数Silhouette Coefficient取值范围从-1到1越接近1说明聚类效果越好。计算逻辑是对每个样本计算它到同簇其他样本的平均距离作为簇内不相似度a计算它到最近邻簇样本的平均距离作为簇间不相似度b轮廓系数等于(b - a) / max(a, b)最终取所有样本的平均值。from sklearn.metrics import silhouette_score sil_score silhouette_score(X_scaled, km.labels_) print(f轮廓系数: {sil_score:.4f})逻辑说明silhouette_score接收特征矩阵和聚类标签直接输出整体轮廓系数。Iris数据集标准化后K3的轮廓系数通常在0.55到0.65之间这是一个相当不错的聚类结构。如果系数低于0.3说明簇之间重叠严重可能是K值选多了或者特征选择有问题。可视化方面最直观的方式是选择两个特征画二维散点图。Iris数据集中花瓣长度和花瓣宽度两个特征的区分度最高用它们画图能看到清晰的簇结构import matplotlib.pyplot as plt plt.figure(figsize(8, 6)) colors [red, blue, green] for cluster_id in range(k): cluster_points X_scaled[km.labels_ cluster_id] plt.scatter(cluster_points[:, 2], cluster_points[:, 3], ccolors[cluster_id], labelf簇 {cluster_id}) # 画出簇中心 centers scaler.inverse_transform(km.cluster_centers_) plt.scatter(centers[:, 2], centers[:, 3], markerX, s200, cblack, label簇中心) plt.xlabel(花瓣长度 (标准化后)) plt.ylabel(花瓣宽度 (标准化后)) plt.title(K-Means聚类结果 (K3)) plt.legend() plt.grid(True) plt.savefig(cluster_result.png, dpi120)逻辑说明km.labels_是每个样本的簇编号代码按簇编号分组后分别绘制散点同一簇的样本用同一种颜色。要注意的是这里画图用的是标准化后的特征所以横纵坐标名称标注为“标准化后”如果希望坐标轴显示原始数值就把X_scaled替换成X原始矩阵但那样画出来的点间距会被花萼长度这类大量纲特征主导图形区分度通常不如标准化后清晰。簇中心需要先通过scaler.inverse_transform()还原回原始量纲再画不然中心点位置和原始数据不在一个尺度上。最后一部分评估是和真实标签对比。如果你加载数据时保留了y_true可以把聚类标签和真实类别做交叉对比直观看到每个簇里主要包含哪一类鸢尾花import numpy as np # 如果原始文件有标签列 if y_true is not None: cross_tab pd.crosstab(y_true, km.labels_) print(cross_tab)逻辑说明pd.crosstab生成行是真实品种、列是聚类簇编号的交叉表。以Iris数据集为例Setosa通常会被完整分到同一个簇交叉表某一行只有一个非零值而Versicolour和Virginica可能在两个簇之间有交叉分布这正是数据集本身特征的体现。这里要特别提醒聚类簇编号和真实类别编号没有一一对应关系km.labels_里的0、1、2只是簇的标识不代表Setosa、Versicolour、Virginica的顺序。4. 避坑K-Means跑Iris数据集最常见的五个坑及排查方法4.1 数据里有非数值列KMeans.fit()直接报错现象运行km.fit(X_scaled)时抛出ValueError提示输入包含非数值类型。原因iris_all.csv如果包含ID列、品名字符串列或日期列直接用df.values会把整列转成字符串类型StandardScaler无法对字符串执行均值方差计算错误在标准化阶段就可能爆发。更隐蔽的情况是某列混入了?或na之类的占位符read_csv会将整列解析为object类型。解决加载数据后立即执行df.dtypes检查每列类型。发现非数值列就按列名排除X df[[sepal_length, sepal_width, petal_length, petal_width]].values。如果列值里混入了缺失标记先执行df df.replace(?, np.nan).dropna()再做类型转换。4.2 忘了标准化聚类结果和真实标签对不上现象聚类完成后画散点图发现Setosa这组能分开但Versicolour和Virginica混成一团轮廓系数只有0.3左右。原因花瓣宽度和花萼长度的数值范围差异太大距离计算被大量纲特征主导。这不是K-Means算法的问题而是特征没有处于同一尺度导致的高维空间距离失真。解决强制在聚类前执行StandardScaler().fit_transform(X)不要跳过这一步。跑完用silhouette_score对比标准化前后的数值通常标准化后能提升0.1到0.2。这也是我把标准化写进每个示例代码里的原因——它在Iris数据集上是MVP级别的关键步骤。4.3 不设random_state每次跑出来的聚类结果都不一样现象同一份代码连着跑两次轮廓系数在小数点后几位有差异部分样本的簇归属发生变动。原因K-Means的初始簇中心是随机选择的不同初始点可能收敛到不同局部最优解。虽然没有random_state时的多次初始化也会选出较优的结果但每次运行的随机种子不同结果就存在波动。解决在KMeans()构造函数里固定random_state42。做实验对比时凡是涉及K-Means的代码都强制设置这个参数否则你无法判断结果差异是来自参数调整还是随机种子变动。对Iris数据集来说固定种子后结果完全可复现这是项目规范化的重要一环。4.4 用聚类标签和真实标签直接算准确率得出极低的分数现象有人用accuracy_score(y_true, km.labels_)评估聚类效果发现准确率只有0.3左右甚至更低觉得K-Means算法很垃圾。原因聚类的簇编号只是任意分配的标识编号0不代表Setosa编号1不代表Versicolour编号2也不代表Virginica。拿无序的簇编号和有序的真实类别直接算准确率本身就是一个统计对齐问题分数低不代表聚类效果差。解决改用调整兰德指数Adjusted Rand Index, ARI或归一化互信息NMI这两个指标会自动寻找聚类标签和真实标签之间的最佳匹配关系不受编号顺序影响。用from sklearn.metrics import adjusted_rand_score计算Iris数据集上K3的ARI通常在0.7以上。4.5 高维空间的可视化误导判断现象聚类完成后用花萼长度和花萼宽度两个特征画散点图发现三个簇严重重叠觉得K-Means聚类失败了换用花瓣长度和花瓣宽度重画又发现效果很好。原因Iris数据集有四个特征任何二维投影都只是部分视角。花萼长度和花萼宽度对Setosa有区分度但对Versicolour和Virginica几乎无法区分而花瓣特征在区分后两类时更有优势。单看一张散点图就下结论容易误判聚类效果。解决可视化时要么选择区分度最高的特征组合这里推荐花瓣长度和花瓣宽度要么用PCA或t-SNE把数据降到二维再看整体结构。PCA是线性降维能最大程度保留全局方差t-SNE更擅长展示局部结构但计算开销大150条样本两种方法都很快。我一般优先PCA因为它结果稳定且可解释性强。5. 进阶用法用PCA降维验证聚类结构让一维决策边界直观可见Iris数据集只有四维特征不降维也能通过两两组合画图观察。但当你想从整体上判断K3是不是最优选择、或者换一份高维数据集时PCA降维加聚类结果叠加可视化是更通用的方案。PCA的原理是把原始特征做线性变换生成一组互不相关的主成分其中第一主成分保留最大方差第二主成分保留次大方差以此类推。把数据投影到前两个主成分上就能在二维平面上观察高维数据的整体分布结构。在IrisCluster.py现有代码基础上追加PCA降维与可视化from sklearn.decomposition import PCA # 对标准化后的数据做PCA降维 pca PCA(n_components2) X_pca pca.fit_transform(X_scaled) print(主成分解释方差比例:, pca.explained_variance_ratio_) print(累计解释方差比例:, pca.explained_variance_ratio_.sum().round(4)) # 用聚类标签给PCA降维结果上色 plt.figure(figsize(8, 6)) for cluster_id in range(k): cluster_points X_pca[km.labels_ cluster_id] plt.scatter(cluster_points[:, 0], cluster_points[:, 1], ccolors[cluster_id], labelf簇 {cluster_id}, alpha0.7) centers_pca pca.transform(km.cluster_centers_) plt.scatter(centers_pca[:, 0], centers_pca[:, 1], markerX, s200, cblack, label簇中心) plt.xlabel(第一主成分) plt.ylabel(第二主成分) plt.title(PCA降维后的K-Means聚类结果 (K3)) plt.legend() plt.grid(True) plt.savefig(pca_cluster_result.png, dpi120)逻辑说明fit_transform(X_scaled)对标准化后的数据执行PCA变换输出每个样本在二维主成分空间中的坐标。explained_variance_ratio_表示每个主成分保留的方差比例Iris数据集典型情况是第一主成分保留约72%的方差第二主成分约22%两者合计超过95%意味着投影到二维平面后信息损失非常小。画图时用聚类标签着色能直观看到三个簇在PCA空间中的分布是否紧凑、彼此是否分离。pca.transform(km.cluster_centers_)把簇中心的坐标从四维特征空间映射到二维主成分空间这样簇中心能和样本点画在同一张图上。这里有一个值得花时间验证的做法对比原始两特征散点图和PCA降维散点图你会发现PCA视图下的簇边界通常更清晰这是因为PCA综合了四个特征的信息而两特征图只用了其中一小部分。写完这段代码后我习惯性做两件事来验证聚类结果是否可靠一是对比km.labels_和y_true做交叉表确认每个簇的主导品种二是把PCA的累计解释方差比例打印出来确认丢弃的信息没有超过5%。如果新数据集上PCA累计方差低于85%二维可视化就会丢失大量信息此时建议把n_components提高到3用三维散点图观察。对这份Iris聚类项目本身我最后的建议是你跑完代码后把n_clusters分别改成2和4再跑一遍对比三组结果的轮廓系数。这个过程能帮你建立对K值敏感度的直觉——K2时轮廓系数可能不低但结构过粗K4时轮廓系数开始下降说明引入了过度切分。从那以后我每次用K-Means处理新数据都会把肘部图、轮廓系数、PCA可视化这三维验证走一遍缺一不可算是我在这个经典项目上收获的最大教训。希望帮到你。本文还有配套的精品资源点击获取
返回列表