Python数据分析实战:Iris鸢尾花数据集从加载到可视化全流程

发布时间:2026/9/18 2:59:39
Python数据分析实战:Iris鸢尾花数据集从加载到可视化全流程 很多学Python数据分析的人第一眼见到Iris鸢尾花数据集时都会有点困惑这么一个小小的、只有150行数据的表格凭什么成了无数教程和书籍的常客等你自己把数据加载、数据体检、可视化分析这套流程完整跑下来你才会意识到它之所以经典是因为它替你屏蔽了所有“脏数据”带来的干扰让你能把全部注意力放在工具链本身。这篇文章我就用Python把Iris数据集从加载到可视化分析完整走一遍把我实际用下来最顺手的姿势、最常踩的坑以及每一步背后的判断依据都摊开讲清楚。1. Iris数据集为什么能成为新手第一课1.1 数据集本身的“教科书级”设计Iris数据集源自统计学家R.A. Fisher在1936年发表的经典论文收集了3种鸢尾花各50条样本总共150条记录。每条记录包含4个数值型特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位都是厘米最后还有一个品种标签分别是Setosa、Versicolor、Virginica。这个数据集的巧妙之处在于它天生就是为教学设计的。4个特征全部是连续数值没有文本、时间、地理位置这些需要额外处理的数据类型150条记录不多不少跑任何算法都是一瞬间的事类别完全平衡三个品种各占50条不存在类别倾斜的问题。更妙的是它的分类难度梯度Setosa这个品种在花瓣特征上和另外两个品种完全分离几乎用一刀就能切开而Versicolor和Virginica之间存在明显重叠单纯看某一个维度很难区分必须综合多个特征才能分得比较干净。这种“有简单、有模糊”的结构能让你在一张图上同时感受到线性可分的爽快和分类边界的模糊感。1.2 它背后站着的是Python数据生态的完整链条很多新手误以为学Iris就是学一个数据集其实完全不是。Iris更像是串起整个Python数据分析工具链的那根线。用pandas加载表格、用matplotlib和seaborn画统计图、用scikit-learn做机器学习建模、用plotly做交互式可视化这串流程正是日常数据分析工作最扎实的地基。把这套链路在Iris上跑通一遍之后换任何业务数据你只需要替换数据源和字段名核心思路完全一致。这也是我看重它的另一个原因Iris足够小小到你可以在几秒内完成一次完整分析迭代。当我接触一个新的可视化库或者想验证一个新配置是否生效时我都会先拿Iris当“冒烟测试”数据跑通了再上真实业务数据。这个习惯帮我省掉了大量排查环境问题的时间。2. 数据加载的几种姿势选对了能省一半时间2.1 先搭一个干净可复用的Python环境在加载数据之前先确认环境是干净的。我推荐用Python 3.8以上版本创建虚拟环境避免把包装进系统环境里导致版本互相污染。执行下面这组命令就够了python -m venv iris_env source iris_env/bin/activate # Windows下是 iris_env\Scripts\activate pip install pandas numpy matplotlib seaborn scikit-learn plotly有人会问为什么不直接用conda或全局环境。我的建议是常规项目一律用venv虚拟环境因为依赖隔离能让你在升级某个库版本时不影响其他项目。Iris这个项目本身依赖不多但matplotlib、seaborn、pandas这三个库之间存在版本联动装在虚拟环境里更省心。2.2 姿势一从CSV文件读取贴近真实工作场景大部分真实业务数据都是以CSV或Excel文件形式存在的所以pd.read_csv是必须掌握的技能。假设你已经把iris.csv下载到本地最简单的加载方式是这样import pandas as pd df pd.read_csv(iris.csv) print(df.head())这里有一个我经常提醒初学者的细节很多从网上下载的Iris数据文件并没有列名或者列名是自定义的。如果直接read_csv第一行会被当成列名。遇到这种情况就需要显式指定列名columns [sepal_length, sepal_width, petal_length, petal_width, species] df pd.read_csv(iris.csv, headerNone, namescolumns)我见过不少人在这一步卡住原因是他们下载的CSV文件恰好没有表头而网上的教程都是按有表头写的。判断方式其实很简单加载后打印df.columns如果列名不是你预期的英文特征名而是数字0、1、2、3那就是缺表头了按headerNone的方式重新读取即可。2.3 姿势二scikit-learn内置加载最省事的离线方式如果你装了scikit-learnIris数据其实已经内置了不需要下载任何文件。我觉得这是初学者最推荐的入口from sklearn.datasets import load_iris import pandas as pd bunch load_iris() df pd.DataFrame(bunch.data, columnsbunch.feature_names) df[species] pd.Categorical.from_codes(bunch.target, bunch.target_names) print(df.head())这里稍微解释一下bunch这个对象。scikit-learn的load_iris()返回的是一个类似字典的Bunch对象其中data是150行4列的二维数组target是0、1、2的整数标签feature_names是特征名列表target_names是三类鸢尾花的品种名。用pd.Categorical.from_codes把整数标签转换成真正的品种名后续画图时图例就会显示Setosa、Versicolor、Virginica而不是一堆数字。这种加载方式的好处是彻底摆脱了网络和文件路径的依赖只要环境里有scikit-learn就能加载。而且Bunch对象里还附带完整的字段说明想看数据集的背景信息可以直接print(bunch.DESCR)懒人福音。2.4 姿势三seaborn内置加载画图前的首选入口seaborn内置了一个load_dataset函数可以加载包括Iris在内的多个示例数据集加载后直接返回一个整理好的DataFrameimport seaborn as sns df sns.load_dataset(iris) print(df.head()) print(df.dtypes)load_dataset(iris)返回的DataFrame列名是sepal_length、sepal_width、petal_length、petal_width、species其中species已经是字符串类型。这种格式和seaborn的绘图API配合得非常好因为seaborn大部分绘图函数都接受dataDataFrame然后用x、y指定列名、用hue指定分组列。如果你想快速画一组统计图表来观察数据这是最快的一条路。需要提醒的是load_dataset默认是从seaborn的在线仓库拉取数据网络不稳定的时候会失败。如果报错可以先用脚本把这个CSV缓存到本地之后改用pd.read_csv读取或者直接用scikit-learn的内置版本。2.5 姿势四Plotly Express内置加载交互式可视化的快捷方式如果你打算做交互式图表plotly.express也内置了Iris数据import plotly.express as px df px.data.iris() print(df.head())px.data.iris()同样返回规范的DataFrame列名和seaborn版本略有区别比如species列里有的是字符串。这个入口的好处是零配置加载完直接就能传给px.scatter_3d这类函数做3D交互图不用关心数据格式转换。2.6 五种加载方式的横向对比我把上面几种方式整理成一个对比表方便你按需选择加载方式返回类型是否依赖网络典型使用场景pd.read_csv(iris.csv)DataFrame本地文件模拟真实业务数据处理流程sklearn.datasets.load_iris()Bunch对象否机器学习建模前加载数据sns.load_dataset(iris)DataFrame是可缓存快速绘制统计图表px.data.iris()DataFrame否快速做交互式图表pd.read_csv(url)DataFrame是直接读取在线公开数据集如果只是入门学习我建议从CSV方式开始因为真实工作里遇到最多的就是文件读取。等熟悉了DataFrame的基本操作再体验另外几种方式也不迟。3. 加载完数据先别急按这个顺序做数据体检很多教程教你加载完数据立刻画图我建议忍住。数据加载只是起点真正决定后面图表是否有说服力的是加载之后那几分钟的数据体检。这一步能帮你确认数据是不是符合预期避免画出来的图带着隐藏的错误。3.1 用shape、info和head快速确认结构拿到DataFrame的第一件事先看它的整体结构print(df.shape) print(df.head()) print(df.info())df.shape会输出(150, 5)意思是150行5列和Iris数据集的标准结构一致。df.head()让你快速预览前5行确认列名和内容是否正常。df.info()则会输出每一列的数据类型和非空值数量从中可以一眼看出是否有类型异常。这一步虽然基础但它是一个很好的“结构确认”习惯。我在处理真实数据时经常遇到名义上是CSV、实际混入了分隔符异常、BOM头、空行这类问题Iris虽然不会出现这些情况但保持这个习惯能避免以后在真实数据上栽跟头。3.2 用describe看数值分布先建立直觉接下来执行df.describe()这是最值回票价的单行代码print(df.describe().round(2))输出结果对每个数值特征给出计数、均值、标准差、最小值、25%分位数、中位数、75%分位数和最大值。拿Iris来说你能看到花萼长度在4.3到7.9厘米之间均值约5.84厘米花瓣长度在1.0到6.9厘米之间均值约3.76厘米。这些数值会在你心里形成一个大致轮廓花萼长度的整体数值比花瓣长度大一个量级。我强调这一步的原因是它能在你动手画图之前就建立对数据的直觉。比如当你发现有某个特征的均值远大于中位数你就能预判这个特征可能是右偏分布。带着这种预期去看图比毫无准备地扫一眼要深刻得多。3.3 检查缺失值、重复值和类别平衡Iris数据集本身质量很高但你在不同渠道下载的版本可能并不完全相同所以缺失值和重复值的检查不能省print(df.isnull().sum()) print(df.duplicated().sum()) print(df[species].value_counts())isnull().sum()逐列统计缺失值数量标准Iris数据应该全为0。duplicated().sum()统计完全重复的行数这个值不为0时就要小心了——重复行会让后续模型评估结果虚高因为模型可能在训练时已经“见过”这些样本。value_counts()输出三个类别的样本数量理想情况下三个品种各50条。类别平衡为什么重要如果某个类别占了90%那么即使模型把所有样本都预测成那个类别准确率也能有90%这显然不能说明模型学到了有效规律。Iris的平衡设计就是为了让你在建模时有底气相信准确率是有意义的。3.4 用相关性矩阵预览特征间的关系最后再看一眼相关性矩阵corr df.select_dtypes(includenumber).corr() print(corr.round(2))你会看到花瓣长度和花瓣宽度的相关系数高达0.96花萼长度和花瓣长度的相关系数也有0.87而花萼宽度和另外几个特征要么接近0、要么是负数。这组数字说明什么说明花瓣相关的两个特征之间信息高度冗余后面画热力图时你会更直观地看到这一点。这一步与可视化的关系很紧密。如果你提前知道哪几个特征高度相关再看散点图矩阵时就会特别留意那几组图的形状。4. 单变量到多变量可视化把数据结构看出层次数据体检做完终于到了标题里“可视化分析”的重头戏。我的建议是不要一上来就画复杂的pairplot而是从单个特征开始逐步叠加维度这样每一步你都知道这个图在回答什么问题。4.1 直方图与KDE看清单个特征的分布形态先拿花瓣长度开刀用seaborn的histplot同时叠加KDE曲线import matplotlib.pyplot as plt import seaborn as sns sns.set_theme(stylewhitegrid) sns.histplot(datadf, xpetal_length, huespecies, kdeTrue) plt.show()这张图一出来你会立刻看到花瓣长度呈双峰分布左侧的峰集中在1到2厘米之间右侧的峰集中在4到6厘米之间。加上hue参数后左侧峰基本全是Setosa右侧峰则是Versicolor和Virginica混在一起。这个视觉信息极其关键——它直接告诉你单靠花瓣长度这个特征Setosa能被轻松分离出来但Versicolor和Virginica还需要更多信息。同样的逻辑可以迁移到生产环境里你在做业务分析时画直方图不是为了好看而是为了确认特征分布是否满足后续建模的假设。比如逻辑回归对特征尺度敏感看到偏态分布时就该考虑做变换。4.2 箱线图用统计量判断类别差异和离群点接下来用箱线图观察每个特征在不同品种间的分布差异fig, axes plt.subplots(2, 2, figsize(12, 10)) features [sepal_length, sepal_width, petal_length, petal_width] for ax, feature in zip(axes.ravel(), features): sns.boxplot(datadf, xspecies, yfeature, axax) ax.set_title(feature) plt.tight_layout() plt.show()箱线图的核心是五数概括箱体上下边缘分别是25%和75%分位数箱体中间的线是中位数上下须延伸到非离群点的边界。从这四张小图能明显看出花瓣长度和花瓣宽度在三个品种间的箱体错位很大几乎不重叠而萼片宽度的三个箱体高度重叠区分度很差。这个观察在后续特征选择时非常直接地告诉你花瓣特征更有价值。顺带说箱线图也是排查离群点的利器。标准Iris数据没有明显的离群点但它在真实数据上的用法是通用的一旦发现某个点超出Q1 - 1.5*IQR或Q3 1.5*IQR的范围就需要判断它是真实业务波动还是数据录入错误。4.3 散点图矩阵查看特征两两组合的分类边界单变量看完后就轮到Iris最著名的“招牌图”了sns.pairplot(df, huespecies, height2.5) plt.show()pairplot会把每两个特征两两组合生成散点图对角线位置是每个特征的直方图。这张图上你能观察到一个关键规律无论看花瓣长度和花瓣宽度的组合还是花萼长度和花瓣长度的组合Setosa都形成一个独立的点簇和另外两个品种完全分开而Versicolor和Virginica在大多数组合里都存在一定程度的交错只有某些角度能看到相对清晰的边界。说到这里我想强调一个看散点图矩阵的方法不要逐张图扫视而是先盯住对角线上的直方图找到单变量区分度最好的特征再看非对角线散点图里“点簇分离是否明显”。Iris里区分度最好的是花瓣长度和花瓣宽度这两个特征的组合它们画出来的散点图几乎可以用一条直线把Setosa切开这正是视觉化的线性可分性。4.4 热力图把相关性从数字变成颜色最后用热力图把相关性矩阵变成更易读的图形corr df.select_dtypes(includenumber).corr() sns.heatmap(corr, annotTrue, cmapcoolwarm, fmt.2f) plt.show()热力图的核心价值是让你一眼看出颜色深的区域。花瓣长度和花瓣宽度那条格子是深红色数值0.96说明这两个特征几乎在讲同一件事花萼宽度和其他特征的格子颜色偏浅数值普遍较低说明它携带的独立信息比较多。这个认知对后续建模有指导意义如果做特征选择你可以考虑保留花萼宽度因为它和别的特征相关性低能带来新的角度。5. 进阶玩法降维、3D交互和极简分类模型“可视化分析”这个词如果只停在画静态图其实是不够的。当你处理的真实数据特征数量超过三维时就需要借助降维手段把高维结构压到平面上看这也是Iris数据能教给你的第二个核心技能。5.1 PCA降维把4个特征压缩成2个主成分PCA主成分分析的原理是找到数据方差最大的几个方向把高维数据投影到这些方向上从而用更少的维度保留尽可能多的信息。在Iris上的实现很短from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA features df[[sepal_length, sepal_width, petal_length, petal_width]] scaled StandardScaler().fit_transform(features) pca PCA(n_components2) pca_result pca.fit_transform(scaled) pca_df pd.DataFrame(pca_result, columns[PC1, PC2]) pca_df[species] df[species] print(pca.explained_variance_ratio_) print(pca.explained_variance_ratio_.sum())这里一定要先做StandardScaler()标准化这一步很多人会漏掉。原因是PCA计算的是方差最大的方向如果特征的量纲不同数值大的特征会主导主成分方向。比如萼片长度数值普遍在5到8之间而花瓣宽度只有0.1到2.5不标准化的话PCA会天然偏向萼片长度标准化之后四个特征被拉到同一尺度PCA才能公平提取结构信息。运行结果里第一个主成分通常能解释约73%的方差第二个约22.9%两个加起来约95.8%。这意味着把4个特征压成2个主成分后仍然保留了绝大部分信息画在二维图上几乎没有太多信息损失sns.scatterplot(datapca_df, xPC1, yPC2, huespecies, s80) plt.show()在这张降维图里你能看到三个品种形成三个相对清晰的簇Setosa在左侧完全分离Versicolor和Virginica在右侧虽有部分接近但整体边界比单看任何两个原始特征的散点图都要清晰。5.2 用Plotly做3D交互图旋转起来看聚类结构静态图看够了可以用plotly.express直接生成一个可旋转的3D散点图import plotly.express as px fig px.scatter_3d( df, xpetal_length, ypetal_width, zsepal_length, colorspecies, sizesepal_width, opacity0.7, titleIris 3D Scatter ) fig.show()这种图特别适合演示场景因为你可以拖着旋转视角从不同方向观察点簇的重叠关系。旋转时会发现Setosa始终单独聚在一处而Versicolor和Virginica在某些视角下靠得很近这比静态图多了一个“感知三维结构”的维度。如果是写报告我一般会在静态图里用PCA降维图而在现场演示时用3D交互图两者各有不可替代的场景。5.3 用KNN验证可视化中观察到的“类间可分性”可视化看得再多也只是“感觉可以分”要量化这个结论需要落到一个简单分类模型上。我在这里选择KNN是因为它的思想和可视化的“点簇”直觉一脉相承——看谁的邻居多就归到哪一类。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import classification_report, ConfusionMatrixDisplay X df[[sepal_length, sepal_width, petal_length, petal_width]] y df[species] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) scaler StandardScaler().fit(X_train) X_train_scaled scaler.transform(X_train) X_test_scaled scaler.transform(X_test) model KNeighborsClassifier(n_neighbors3) model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) print(classification_report(y_test, y_pred)) ConfusionMatrixDisplay.from_estimator(model, X_test_scaled, y_test) plt.show()注意几点。第一train_test_split里我加了stratifyy作用是让训练集和测试集里的类别比例和原始数据一致避免某类样本全落到测试集里造成评估偏差。第二fit之前只对训练集做StandardScaler的fit测试集用同一个transform不能先对整个数据集标准化再切分否则会把测试集的信息提前泄漏给训练环节。第三KNN是距离类算法特征必须标准化否则量纲大的特征会主导距离计算。实测下来测试集准确率大概在97%左右个别随机种子下会有1到2个样本预测错误。混淆矩阵能告诉你错在哪里错误基本集中在Versicolor和Virginica之间。这和你在pairplot里看到的现象完全吻合——可视化看到的边界模糊最后都体现在分类错误上了。6. 我在实际操作中踩过的坑和总结下来的避坑建议这一节的内容大多来自我反复跑Iris和各种数据时积累的真实教训每一条都对应一次排查经历。6.1 中文乱码matplotlib绘图时最头疼的问题如果你给图表加了中文标题或坐标轴标签很可能跑出来变成一堆方框。matplotlib默认字体不包含中文字符需要手动指定字体plt.rcParams[font.sans-serif] [SimHei, PingFang SC, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False第一行设置中文字体第二行解决负号显示成方块的问题。这里有一个容易忽略的细节不同操作系统可用的中文字体不一样Windows通常用SimHei或Microsoft YaHeimacOS用PingFang SCLinux可能需要额外安装中文字体。配置前先确认你的系统里有什么字体不然设了也不生效。6.2 列名和索引问题从Bunch转DataFrame时最容易翻车从load_iris()转DataFrame的时候如果忘了指定columnsbunch.feature_names列名就会变成0到3的数字后续用df[sepal_length]取列就会报错。另一个常见问题是读取CSV后如果不显式设置索引行号是默认的0到149这在和sklearn切分数据时没有太大影响但一旦你做了drop_duplicates或reset_index索引会变得不连续可能在某些合并场景下引发对齐问题。建议养成习惯对行号无意义的数据直接reset_index(dropTrue)。6.3 忽略标准化就做PCA或KNN结果会被量纲带偏我在第5章反复强调标准化这里再补充一个具体例子。如果跳过StandardScaler直接对原始Iris数据做PCA第一主成分的载荷会几乎集中在萼片长度方向上因为它的数值范围最大标准化之后四个特征的贡献才会相对均衡。对KNN来说不标准化会导致萼片特征在欧氏距离计算中权重过大花瓣特征的区分能力被压制分类准确率可能不升反降。处理新数据集时先看特征量纲是否一致再做算法选择这个顺序不能乱。6.4 只用准确率评估模型远远不够在Iris这种类别均衡的小数据集上准确率看起来很不错但在类别不均衡的真实数据上准确率会严重失真。我自己的习惯是至少同时看精确率precision、召回率recall和F1-score最好再输出混淆矩阵确认每个类别的表现。举个例子如果某个类别在测试集里完全没预测对准确率可能还是90%以上但混淆矩阵会立刻暴露问题。很多教学资料用准确率作为唯一指标是因为Iris数据集本身很干净。如果你想训练自己的评估能力可以手动把训练集切得很不平衡或者构造一个多数类占95%的模拟数据你会发现准确率这个数字几乎没有任何指导意义。6.5 库版本差异带来的坑seaborn的load_dataset(iris)依赖网络下载而且不同seaborn版本对默认绘图风格的处理略有差异。如果pip install seaborn装出来的版本和你手上的教程不一样图表样式可能对不上。我建议在博客或笔记里记录自己使用的库版本比如seaborn 0.13.2、pandas 2.1.4这样复现时才有据可查。另外安装包时偶尔会遇到权限或PATH问题这时用python -m pip install 包名会比直接敲pip install 包名更可靠因为它明确使用了当前Python解释器对应的pip。最后分享一个我个人的使用习惯每次接触一个新的Python库我都会拿Iris先跑一遍“加载、体检、画图、建模”四条流程。它足够小跑得快结果可预期当某个库的行为出现异常时我能立刻判断是库本身的问题还是我的用法有问题。这种把已知数据当作测试用例的“冒烟测试”思路比每次都拿陌生业务数据去试新工具要高效得多。希望这篇文章能帮你把这套流程和判断标准也内化成自己的习惯。