☰
鸢尾花数据集可视化全攻略:从单变量分析到PCA降维
2026/10/21 7:04:07 网站建设 项目流程

1. 从一份"烂大街"的数据集里能挖出什么

这几年带过不少新人,也在社区里看了大量教程,几乎所有做数据分析的人,第一个正儿八经跑完整的项目,都绕不开同一个名字:鸢尾花数据集。很多人觉得它太基础、太常见,甚至有点"烂大街",随手一个load_iris()就能读出来,画几张散点图就完事了。但我的看法不太一样。就是因为它简单、干净、标注齐全,才最适合拿来练可视化分析的基本功——你不需要花时间清洗脏数据,可以把全部精力放在"怎么把数据看透"这件事上。

我这次做的鸢尾花数据集可视化分析,并不满足于网上那些"画个直方图、散点图就收工"的版本,而是把单变量、双变量、多变量、降维辅助、模型验证几个层面全走了一遍。整套流程走下来,你会发现,可视化分析根本不是"画图"这么简单,它是一套带着问题的观察方法:每个特征长什么样、特征之间什么关系、类别在哪个维度上最容易分开、肉眼看到的结论能不能被模型验证。这篇博文就把我这次完整的分析思路、绘图代码、踩过的坑和判断依据一次说清楚,新手可以照着复现,有基础的朋友也可以看看我在图形选型和结论推论上的一些习惯。

先说背景。鸢尾花数据集包含三个品种:Setosa、Versicolor、Virginica,每个品种50条样本,共150条记录。每条记录有四个数值特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,单位都是厘米。数据最初是由一位植物学家测量收集的,经典程度和知名度在机器学习领域都是教科书级别。正因为它的结构足够规范,我们讨论的重点可以直接放在"可视化怎么帮助我们发现规律"上。

我用的工具是大家最熟悉的Python三件套:pandas处理数据,matplotlib画基础图,seaborn画统计图。这三个库就够用了,不需要引入太偏门的东西。

2. 第一步不是画图,是给数据做"体检"

2.1 先看看数据长什么样

很多人拿到数据的第一反应是直接df.plot(),我建议你忍一下。画图之前,先把数据的基本结构摸清楚,否则你连"该画什么图"都不知道。我在代码里先做了这几件事:读取数据、查看前几行、看列名和类型、看统计摘要。

import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns df = sns.load_dataset('iris') print(df.head()) print(df.info()) print(df.describe().T)

seaborn自带了iris数据集,读取后是一个长表格:每一行是一条样本,species列是类别标签,另外四列是数值特征。用df.info()能看到有没有缺失值,df.describe()能快速看到每个特征的均值、标准差、最小值、四分位数和最大值。

这一步花不了几秒钟,但能建立对数据的"手感"。比如我一眼就看到:

  • 花萼宽度比花瓣宽度整体大一个量级,所以后面画图时必须注意坐标尺度差异
  • 所有特征的均值和中位数都比较接近,没有特别明显的偏态
  • 150条样本四个特征没有缺失值,非常干净

2.2 类别分布必须点名清楚

对于分类问题,类别分布是后续一切分析的地基。我统计了每个类别的数量,并画了一张简单的计数图。

print(df['species'].value_counts()) sns.countplot(data=df, x='species') plt.title('样本类别分布') plt.show()

三个类别各50条,非常均衡。这个结果说明了两个问题:第一,后续如果做分类模型,类别不均衡这个坑直接不存在;第二,可视化分析时,类别间的差异如果明显可见,一定是因为特征本身有区分能力,而不是样本量差异造成的假象。

这类"体检"步骤,放在任何项目里都是通用的。哪怕你面对的是电商用户数据、传感器时序数据,先确认行数、列数、类型、缺失值、类别分布,永远比急着出图稳妥。

3. 单变量可视化:每个特征单独看一遍

3.1 直方图、核密度、还有小提琴图怎么选

单变量分布的可视化,是整套分析的起点。它回答的问题很朴素:这个特征本身是什么形态?取值范围在哪?有没有多峰?有没有极端值?

每个人都会画直方图,但直方图的组距选择会直接影响你对分布的判断。组距太小,图形锯齿感严重,噪声大;组距太大,细节被抹平,可能看不见分布内部的子结构。我习惯先把自动组距的图跑一遍,再手动调一两个组距对比。

fig, axes = plt.subplots(2, 2, figsize=(12, 8)) features = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'] for i, feat in enumerate(features): ax = axes[i//2][i%2] sns.histplot(data=df, x=feat, bins=20, kde=True, ax=ax) ax.set_title(f'{feat} 分布直方图') plt.tight_layout() plt.show()

四个特征的直方图放在一起,对比就出来了。花萼长度近似单峰但略微右偏,花萼宽度也是单峰,花瓣长度和花瓣宽度则明显呈现双峰或长尾形态。双峰的含义很明确:这个特征背后可能隐藏着两个不同的群体,结合类别标签去看,十有八九是Setosa和另外两个品种分布位置差异造成的。

核密度曲线(KDE)对直方图是个很好的补充,它不依赖组距,能画出更平滑的分布轮廓。但要注意,它也会在数据稀疏区域产生"虚胖",所以单看KDE也别全信。

小提琴图是我做单变量分析时最常用的一种图,因为它等于把箱线图的统计特征和核密度图的形态特征合并到了一起。我把三个类别分别的小提琴图叠在一张图上:

fig, axes = plt.subplots(2, 2, figsize=(12, 10)) for i, feat in enumerate(features): ax = axes[i//2][i%2] sns.violinplot(data=df, x='species', y=feat, ax=ax, palette='Set2') ax.set_title(f'{feat} 分品种小提琴图') plt.tight_layout() plt.show()

这张图信息量非常足。花瓣长度和花瓣宽度的小提琴图,三个类别的位置明显错开,尤其是Setosa,和其他两个类别几乎没有重叠区域。而花萼宽度这个特征,三个类别的小提琴形状重叠得非常厉害,属于区分能力很弱的特征。

3.2 箱线图里的离群点不是终点

小提琴图看形态,箱线图看统计。我用箱线图把每个特征按类别分开画了一遍。

fig, axes = plt.subplots(2, 2, figsize=(12, 8)) for i, feat in enumerate(features): ax = axes[i//2][i%2] sns.boxplot(data=df, x='species', y=feat, ax=ax, palette='Pastel1') ax.set_title(f'{feat} 分品种箱线图') plt.tight_layout() plt.show()

箱线图最直观的价值是离群点检测。花萼宽度这个特征里,有两个点被标成了离群点(在箱须之外)。很多人看到离群点就想去掉,但这里的经验是:先别急着删。

我查了一下这两条样本对应的品种,发现是Versicolor和Virginica,数值虽然离群,但没有超出合理生物学范围。花萼宽度是个天然存在波动的测量值,这两条样本可能是正常变异。先保留,后续建模时再观察它们是否影响结果。真正该警惕的是那种因为录入错误导致的离群点,比如负值、单位错乱等,那种才需要处理。

3.3 用正态性检验给分布一个量化答案

图形只能给"感觉",不能给"结论"。所以我在单变量阶段补了一个步骤:正态性检验。

from scipy.stats import shapiro for feat in features: stat, p = shapiro(df[feat]) print(f'{feat}: W={stat:.4f}, p={p:.4f}')

Shapiro-Wilk检验的原假设是数据服从正态分布。如果p值小于0.05,就拒绝原假设,认为数据不服从正态。结果也很直观:花萼长度和花萼宽度p值较大,接近正态;花瓣长度和花瓣宽度p值非常小,明显偏离正态。

这个结论有什么用?如果你后续要做参数检验(比如t检验、ANOVA),正态性假设可能不满足,那你就要考虑非参数检验方法,或者做数据变换。可视化分析的价值在这里就不只是"看看图"了,它直接影响了后面的统计方法选择。

4. 双变量关系:两两特征的合谋与对抗

4.1 散点图矩阵,一次看尽所有组合

单变量看完了,很多人直接开始看相关性矩阵。但二维空间里,相关性和视觉关系常常有出入,所以我的习惯是先画散点图矩阵。

sns.pairplot(df, hue='species', palette='bright') plt.show()

pairplot是三列以上数值数据最值得优先尝试的图形,它自动生成所有特征两两组合的散点图,并且按类别着色,对角线位置还能自动放上核密度曲线或直方图。

从这张矩阵图里能读到的信息,比任何单独的图都丰富。最核心的结论是:

  • Setosa这个品种,在任意包含花瓣特征的组合里,都能被一个清晰的簇分出来
  • petal_length和petal_width之间几乎是完美的正线性关系,所有样本排列成一条直线
  • sepal_width和sepal_length之间的点云比较散,看不出清晰的类别分界
  • Versicolor和Virginica在有的组合里重叠严重,但在petal_length与petal_width组合里,重叠区域很小,只是边界仍有混合

这个分析一下子就把分析的重心带到了"花瓣特征"上。当某个结论在不同图上反复出现时,那基本就是真的了。

4.2 相关系数矩阵:数字不怕你看错

散点图给的是直觉,相关系数给的是定量。我分别计算了皮尔逊相关系数,并画了热力图。

corr = df.drop(columns='species').corr(method='pearson') sns.heatmap(corr, annot=True, fmt='.2f', cmap='coolwarm', square=True) plt.title('特征相关系数热力图') plt.show()

花瓣长度和花瓣宽度相关系数高达0.96,这是极强的正相关。花萼长度和花瓣特征的相关系数也在0.87左右,较强。花萼宽度和其他特征基本上是负相关,数值也小。

这里想提醒一点:相关系数说的是线性关系,它捕捉不到非线性关系。散点图里如果看到明显的U形或者曲线关系,而相关系数很低,那不代表没关系,只是关系不是线性的。所以我从来都是"先看图,后看数",图在前,数在后,交叉验证。

4.3 类别标签带来的视角反转

如果只看总体相关系数矩阵,其实是不够的。因为横跨三个类别总体计算的相关性,可能被类别差异主导。比如某个特征在总体里相关系数高,只是因为三个类别各自都很独立,并不是真实的相关模式。

所以我进一步按类别分别算了相关系数,保证分析的完整性。结果发现,在单个类别内部,特征间的相关系数普遍比总体低。这说明我们在pairplot里看到的强相关性,有很大一部分是"类别间差异"贡献的,而非类别内部规律。这个细节在真实的业务数据分析里尤其重要——混合人群算出来的相关性,可能掩盖掉细分群体内部完全不同的模式。

5. 多变量探索:二维图如何表现三维信息

5.1 hue、size和style,三个维度塞进一张图

平面图只能表达两个坐标轴,但我们可以用颜色、大小、形状来扩展维度。在鸢尾花数据集里,最自然的操作是:用x轴、y轴表达两个特征,用颜色区分品种,用点的大小表达第三个特征。我画了这样一张图:

plt.figure(figsize=(8, 6)) sns.scatterplot(data=df, x='petal_length', y='petal_width', hue='species', size='sepal_length', sizes=(40, 200), palette='deep', alpha=0.7) plt.title('花瓣特征散点图(点大小=花萼长度)') plt.show()

这张图看起来只是把点的大小变了,但信息量增加了:花萼长度作为第三个维度,通过大小变化参与到散点图中。可以看到Setosa点的普遍偏小,而Virginica点的普遍偏大,进一步说明不同品种在花萼长度上也有差异。多变量可视化原则就是这样:一图一个重点,用hue表达分类,用size表达连续变量,用style表达不同分组,能在一张图里讲清楚的故事,就不拆成两张。

5.2 分面图:把类别拆开对比

另一种多变量分析的好办法是分面,按类别各画一张子图,每个子图里只展示该类样本的关系结构。我用FacetGrid做了每个品种的双变量关系图。

g = sns.FacetGrid(df, col='species', hue='species', palette='Set1') g.map(plt.scatter, 'petal_length', 'petal_width', alpha=0.7) g.add_legend() plt.show()

分面之后,每个子图内只有一类样本,观察的是同一类别内特征的相关性。结果和前面分析的互相印证:Virginica内部的petal_length和petal_width依然有明显的正相关趋势,Setosa内部的整体分布相对紧凑。分面图最好的使用场景就是"控制变量":当你怀疑某个分组的内部规律和全局规律不一样时,直接按分组分面看,一目了然。

5.3 平行坐标图:被低估的替代视角

在只有四五个特征的场景里,平行坐标图其实很值得一试。它的思路是用水平方向排列多个坐标轴,每个样本就是一条穿越这些轴的折线,类别不同颜色不同。

from pandas.plotting import parallel_coordinates plt.figure(figsize=(12, 6)) parallel_coordinates(df, class_column='species', colormap=plt.cm.Set1) plt.xticks(rotation=45) plt.title('平行坐标图') plt.show()

平行坐标图最经典的应用就是找类别分离。因为每一维都对应一条竖轴,你可以直接看到在哪些轴上不同类别的折线分离得最开。这张图里能明显看到Setosa在花瓣长度、花瓣宽度两条轴上和其他类别分离得很干净,折线颜色区域几乎没有交叉;而花萼宽度轴上则缠成一团,没有区分力度。

平行坐标图不适合数据维度特别大的场景,几十个维度画出来就像一团乱麻。但在四维、五维这种低维场景,反而是个非常直观的"高维体检工具",建议每个人都上手试一次。

6. 降维辅助验证:人眼的极限在哪里

6.1 PCA让高维数据投射回二维

人眼能直观体会的维度极限是三维,但鸢尾花数据有四维特征。虽然我们知道可以两两组合画散点图,但四维特征的全部结构,其实被切碎到了很多张二维图里。这时候主成分分析(PCA)就派上了用场。

PCA的本质是把原有的多个特征,通过线性组合压缩成几个互不相关的新特征,这些新特征按"方差大小"排序,也就是按信息量排序。前两个主成分通常能保留大部分的信息。我做了这样几步:

from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA X = df[features].values X_std = StandardScaler().fit_transform(X) pca = PCA(n_components=2) X_pca = pca.fit_transform(X_std) pca_df = pd.DataFrame(X_pca, columns=['PC1', 'PC2']) pca_df['species'] = df['species'].values plt.figure(figsize=(8, 6)) sns.scatterplot(data=pca_df, x='PC1', y='PC2', hue='species', palette='bright') plt.title('PCA降维后的散点图') plt.show() print('解释方差比:', pca.explained_variance_ratio_)

必须先标准化再PCA,否则花萼宽度量级小、花瓣长度量级大,量级大的特征会在主成分里占绝对的权重,得到的降维结果会被单位尺度带偏。标准化之后,每个特征权重才是平等的。

降维之后的结果很有说服力:前两个主成分解释了约95%以上的总方差,基本实现了信息的无损压缩。散点图上,三个类别被一条很干净的边界分开,Setosa在左下、Virginica在右上、Versicolor在中间,几乎没有重叠。这和pairplot里肉眼看到的趋势高度一致,算是用降维方法给前面的可视化结论做了一次官方认证。

6.2 可视化结论和模型判断的一致性

做完PCA,我又顺手用最近邻分类做了个快速验证。按7:3划分训练集和测试集,只用前两个主成分作为特征,准确率竟然还能维持在95%上下。如果把四个原始特征全部喂进去,准确率更是接近完美。这个实验的用意不是吹模型,而是验证一个朴素的事实:从可视化里观察到的类间距和分离度,和模型的实际表现是对得上的。

很多分析场景里,可视化和模型之间存在"两张皮"的问题:图上看起来分得开,模型一跑就不对劲;或者模型得分很高,图上却看不出规律。鸢尾花数据集在这方面非常友好,它的可视化模式和模型结论高度统一。对新手来说,这种直观一致性是理解"特征决定上限"的最佳教材;对有经验的人来说,哪怕数据集换成了高维业务数据,先可视化、后建模、最后回来检查可视化的一致性,也是永远不会过时的思路。

7. 一张图怎么选、一版图怎么迭代:实操经验沉淀

7.1 绘图前先问三个问题

我读过的分析报告很多,很多图的问题不是画得不好看,而是没有回答任何问题。所以我才反复强调一个习惯:动笔之前先问自己三个问题——我要通过这张图回答什么问题?哪些维度是关键?观众能不能在五秒内 get 到我的点?

选图类型也是取舍。举个例子,单变量看分布,优先级从高到低是:直方图加核密度、小提琴图、箱线图、经验累积分布图。双变量看关系,首推散点图加类别色调,其次才是hexbin这类用于大样本的图。类别比较多的时候别硬往散点图里塞,分面可能更清爽。三变量以上,再考虑色调、大小、分面组合以及降维。

这些本质上不是技术问题,是表达问题。数据可视化,先是从数据到图表的映射,再从图表到人脑的映射。中间任何一步出错,都可能产生误导。

7.2 配色、透明度与细节:偷懒不得

还有一个容易被低估的细节:配色和叠印。鸢尾花数据集只有150个点,点与点重叠还不算严重,但如果你把样本量放大到几千几万,点与点重叠之后,散点图会变成一整块黑疙瘩,什么都看不清。这时就要调透明度、调整点的大小、或者用六边形分箱图。

plt.figure(figsize=(8, 6)) sns.scatterplot(data=df, x='sepal_length', y='sepal_width', hue='species', alpha=0.7, s=60) plt.legend(title='品种') plt.show()

透明度设为0.6到0.8是大多数场景下比较稳妥的选择,既能保留重叠部分的痕迹,又不至于让单点失去辨识度。

另外,默认的matplotlib配色在部分人群眼里区分度不够,尤其涉及打印成黑白文档时,不同类别只能靠灰度区分,如果颜色又接近,图就直接废掉。我一般都会显式设置调色板,或者至少看一眼图在灰度模式下的可读性。

7.3 这次分析踩过的坑和我的处理方式

做这个项目的过程中,有几个问题值得单独记下来。

第一个坑是直方图的组距。最初我用了系统默认组距,画出来的花萼宽度直方图有好几个奇怪的小尖峰,容易误读成多峰分布。手动调整组距并叠加核密度曲线后,才发现那只是组距造成的假象。组距应该根据样本量和分布范围动态调节,不是永远默认20个箱子。

第二个坑是热力图的色彩映射。coolwarm在中间值用浅色、两端用深色的设计,比较适合相关系数这种有正有负、有中间0值的数;如果用viridis这种顺序映射,相关系数从0到1的视觉差异会被压缩。我一开始用错了映射,把弱相关看成中等相关,换回coolwarm再对比才纠正认知。相关系数热力图不应该用彩虹色或者顺序色,原因就在这里——它容易提醒自己,可视化不只是画出来,画图的每一个参数都在影响你得出结论。

第三个坑是PCA标准化。我最早没有标准化,直接对原始四列做PCA,得到的第一主成分几乎完全被花瓣长度和花瓣宽度主导,花萼宽度的贡献几乎为零。这不是PCA错了,而是量纲掩盖了变量真实的贡献。标准化之后,每个特征在单位尺度上平等竞争,主成分更能反映数据结构本身,而不是被单位差异绑架。

最后想多说一句。很多人做可视化分析,做到"画出几张漂亮的图"就停了。但一次"比较全面"的鸢尾花数据分析,应该是从数据体检开始,经过单变量、双变量、多变量,再到降维验证、模型佐证,环环相扣,每一步都在回答前面的问题,也在给下一步制造新的问题。自己动手把整套流程跑一遍之后,你对"特征""类别可分性""降维"这些抽象概念的理解,会比单纯看教程深刻得多。

这个项目后续的扩展方向其实也不少,比如尝试不同的降维算法对比,或者用聚类把样本按特征自动分组,再和真实标签比对。我个人建议初学者在复现完这套分析之后,挑一个自己熟悉的领域,构造一组真实数据,用同样的流程走一遍,那种"知识变成能力"的感觉,和刷十篇教程完全不一样。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询