Data-Science-For-Beginners 第 09 课实战:用 Matplotlib 对数量(Quantity)做线性图、散点图与柱状图可视化
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
本篇指南基于 Data-Science-For-Beginners 仓库第 09 课《Visualizing Quantities》(可视化数量)展开,围绕一份明尼苏达州鸟类数据集 birds.csv,完整演示如何用 Python 的 Matplotlib + Pandas 组合完成从“读入数据—画线性图—标注坐标轴—定位并过滤异常值—做分组柱状图—叠加对比”的全过程。读完后,你将能够独立完成数量类数据的三类核心图表(line / scatter / bar)绘制、用tick_params、plt.text、rcParams等 API 精细控制坐标轴与标签,并掌握“从图中发现异常值、再反向清洗数据”的实战工作流。
课程背景:为什么用鸟类数据集练“数量可视化”
本课程的定位是:当你拿到一份数据集,需要搞清楚“某个东西到底有多少”时,第一任务就是检查它的数值分布。仓库为此提供了一份已清洗过的数据集 birds.csv,共 443 条鸟类记录(不含表头),字段混合了文本与数值:
| 字段 | 类型 | 含义 |
|---|---|---|
Name/ScientificName | 文本 | 鸟类俗名 / 学名 |
Category/Order/Family/Genus | 文本 | 分类学层级(类群/目/科/属) |
ConservationStatus | 文本 | 保护状态(如 LC) |
MinLength/MaxLength | 数值 | 体长范围(厘米) |
MinBodyMass/MaxBodyMass | 数值 | 体重范围(克) |
MinWingspan/MaxWingspan | 数值 | 翼展范围(厘米) |
Matplotlib 的通用绘图流程可以概括为五步:识别 dataframe 中要用的列 → 对数据做必要变换 → 指定 x/y 轴的取值 → 选择图表类型 → 显示图形。本课聚焦最适合表达“数量”的三种图:线性图(line)、散点图(scatter)、柱状图(bar)。
选图原则(直接决定图表能不能讲清故事):
- 分析随时间的趋势:线性图
- 比较数值大小:柱状图、条形图、饼图、散点图
- 展示部分与整体的关系:饼图
- 展示数据分布:散点图、柱状图
- 展示趋势:线性图、条形图
- 展示变量间的关系:线性图、散点图、气泡图
实操时,请先打开本课目录下的 notebook.ipynb 并新建单元格,所有代码都在这份 notebook 中运行。
读入数据并画出第一张线性图
数据文件位于仓库根目录的data文件夹。注意:原文档 notebook 中写的'../../data/birds.csv'是相对于 lesson 文件夹的路径,从仓库根目录看,数据就在 data/birds.csv;若你把 notebook 复制到仓库根目录运行,请相应调整为data/birds.csv。
import pandas as pd import matplotlib.pyplot as plt birds = pd.read_csv('../../data/birds.csv') birds.head()birds.head()输出的前几行(对应 data/birds.csv 前几行)长这样:
| Name | ScientificName | Category | Order | Family | Genus | ConservationStatus | MinLength | MaxLength | MinBodyMass | MaxBodyMass | MinWingspan | MaxWingspan | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Black-bellied whistling-duck | Dendrocygna autumnalis | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Dendrocygna | LC | 47 | 56 | 652 | 1020 | 76 | 94 |
| 1 | Fulvous whistling-duck | Dendrocygna bicolor | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Dendrocygna | LC | 45 | 53 | 712 | 1050 | 85 | 93 |
| 2 | Snow goose | Anser caerulescens | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 64 | 79 | 2050 | 4050 | 135 | 165 |
| 3 | Ross's goose | Anser rossii | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 57.3 | 64 | 1066 | 1567 | 113 | 116 |
| 4 | Greater white-fronted goose | Anser albifrons | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 64 | 81 | 1930 | 3310 | 130 | 165 |
接下来用一条最基础的 Pandas 快捷方法画出最大翼展(MaxWingspan)的线性图:
wingspan = birds['MaxWingspan'] wingspan.plot()Series.plot()是 Pandas 对 Matplotlib 的薄封装:以序列的索引为 x 轴、数值为 y 轴画折线。第一眼看图就会立刻发现一个严重异常的“尖峰”:某个值接近 2300 厘米——也就是 23 米的翼展,明尼苏达难道有翼龙在飞?这正是本课要演示的核心工作流:不离开图表,直接在工作流内部追查异常值。
给 x 轴加标签:先暴露“标签过载”问题
线性图的 x 轴此刻只有索引数字,看不出对应哪种鸟。给坐标轴加上语义化的标题与刻度标签:
plt.title('Max Wingspan in Centimeters') plt.ylabel('Wingspan (CM)') plt.xlabel('Birds') plt.xticks(rotation=45) x = birds['Name'] y = birds['MaxWingspan'] plt.plot(x, y) plt.show()要点解析:
plt.xticks(rotation=45):把 443 个鸟类名称的刻度标签旋转 45 度,缓解横向重叠;plt.plot(x, y)中 x 是类别标签序列(Pandas Series),Matplotlib 会把字符串当作类别轴刻度;- 标题、轴名先设置再
plt.plot,顺序不影响结果,但必须与plt.show()在同一个当前 figure 上执行。
即便旋转 45 度,443 个标签依然密到不可读。这说明当样本过多时,试图给每个点都打标签是错误策略——正确做法是换一种图型加“选择性标注”。
用散点图 +plt.text只标注异常值
策略调整为:改用散点图腾出标注空间,且只给异常值(翼展 > 500 厘米)打标签:
plt.title('Max Wingspan in Centimeters') plt.ylabel('Wingspan (CM)') plt.tick_params(axis='both',which='both',labelbottom=False,bottom=False) for i in range(len(birds)): x = birds['Name'][i] y = birds['MaxWingspan'][i] plt.plot(x, y, 'bo') if birds['MaxWingspan'][i] > 500: plt.text(x, y * (1 - 0.05), birds['Name'][i], fontsize=12) plt.show()这段代码逐行拆解:
| 代码 | 作用 |
|---|---|
plt.tick_params(axis='both', which='both', labelbottom=False, bottom=False) | 同时作用于 x/y 轴的主次刻度(which='both'),labelbottom=False隐藏底部刻度文字、bottom=False移除底部刻度线,彻底腾出标签空间 |
plt.plot(x, y, 'bo') | 'bo'是 Matplotlib 简写格式符:blue + circle,画一个蓝色小圆点,即散点 |
if birds['MaxWingspan'][i] > 500 | 阈值过滤:绝大多数鸟类翼展在几十到两百多厘米,只有超过 500 才值得标注 |
plt.text(x, y * (1 - 0.05), ...) | 在该点上方 5% 处(向下偏移,避免与点重合)写入文字标签 |
fontsize=12 | 保证标签在密集图中仍可读 |
图中浮在高空的两个标签就是异常值:Bald eagle(白头海雕)与 Prairie falcon(草原隼)。
过滤异常值:从“看图”回到“改数据”
结合常识判断:一只翼展 25 米的白头海雕几乎不可能存在——真实翼展约 2 米出头。回查数据源 data/birds.csv 可以确认,这两条记录的数值多写了一个0:Bald eagle 的MaxWingspan为 2300(正常应为 230),Prairie falcon 为 1100(正常应为 110)。这是典型的录入错误(typo)型异常值。
本课给出的处理方式是:先不修改原始数据,而是在绘图时用名称白名单把它们排除掉,观察过滤后的数据形态:
plt.title('Max Wingspan in Centimeters') plt.ylabel('Wingspan (CM)') plt.xlabel('Birds') plt.tick_params(axis='both',which='both',labelbottom=False,bottom=False) for i in range(len(birds)): x = birds['Name'][i] y = birds['MaxWingspan'][i] if birds['Name'][i] not in ['Bald eagle', 'Prairie falcon']: plt.plot(x, y, 'bo') plt.show()过滤掉两个异常点后,整个分布立刻变得紧凑且可理解——这就是“可视化驱动的数据清洗”:图先暴露问题,数据再修正问题。后续练习不依赖翼展数值,你也可以选择:过滤这两条记录、或直接修正 CSV 中的笔误。
从分布走向计数:用柱状图回答“有多少”
线性图与散点图展示的是单个数值的分布;而“数量”问题更常问的是分组计数,例如:
- 有多少个鸟类类别(Category),各自数量是多少?
- 有多少种鸟是灭绝、濒危、罕见还是常见的?
- 按林奈分类学,各属(Genus)、各目(Order)分别有多少?
柱状图正是回答这类问题的工具。
反例:直接对全量数据堆叠
最直接的写法是把整个 dataframe 丢给 Pandas 的绘图接口:
birds.plot(x='Category', kind='bar', stacked=True, title='Birds of Minnesota')这张图不可读:每个类别下又按 443 条记录堆叠,柱子内部毫无意义地拥挤。这个反例本身就是一课:柱状图不能对着原始明细数据画,必须先做聚合。
正例:Pandas 聚合 + Matplotlib 绘图
正确分工是:Pandas 负责管理数据(分组、计数、排序),Matplotlib 只负责画图。统计每个Category出现的次数并按数量排序,再把图拉高以适应类别数量:
category_count = birds.value_counts(birds['Category'].values, sort=True) plt.rcParams['figure.figsize'] = [6, 12] category_count.plot.barh()参数说明:
value_counts(..., sort=True):对类别数组计数并降序排列,结果是一个索引为类别名、值为计数的 Series;plt.rcParams['figure.figsize'] = [6, 12]:把画布从默认约 6.4x4.8 英寸改为 6x12 英寸的竖版,容纳全部类别;plot.barh():横向条形图(bar horizontal),类别名天然适合横向展示长文本标签。
对 data/birds.csv 做分组计数可以验证这一结论:全表共 53 个类别,其中Ducks/Geese/Waterfowl(鸭/雁/水禽)以 45 种居首,其后是New World warblers(41 种)、Sandpipers/Allies(34 种)、Gulls/Terns/Skimmers(28 种)。一眼就能看出该地区鸟类以水禽为主——明尼苏达号称“万湖之州”,完全符合预期。也可以自己换ConservationStatus、Genus、Order等列再做计数,看看还有什么意外发现。
组间比较:新建坐标轴与叠加绘制
比较各类别的 MaxLength
换一个维度:比较每个类别的MaxLength(体长上限):
maxlength = birds['MaxLength'] plt.barh(y=birds['Category'], width=maxlength) plt.rcParams['figure.figsize'] = [6, 12] plt.show()注意这里的写法与前例不同:直接调用 Matplotlib 的plt.barh,用原始明细数据逐条画横条,而不是先聚合。结果:蜂鸟(Hummingbirds)的 MaxLength 最小,鹈鹕、大雁类最大——数据讲出了一个符合常识的故事。
叠加 Min/Max 看区间
更进一步的技巧是在同一坐标轴上叠加两次barh,把同一类别的最小长度和最大长度画在一起,形成区间对比:
minLength = birds['MinLength'] maxLength = birds['MaxLength'] category = birds['Category'] plt.barh(category, maxLength) plt.barh(category, minLength) plt.show()两次调用共享同一个 figure,后画的短条(MinLength)叠在先画的长条(MaxLength)之上,肉眼看即可读出每个类别的长度区间。从图上可以得出一个稳健结论:鸟越大,体长区间的绝对跨度也越大——大鸟的 Min/Max 差值明显宽于小鸟。
小结与延伸练习
本课的完整技能链是:
pd.read_csv读入混合类型数据,head()检查结构;Series.plot()快速出图,用“反直觉的尖峰”定位异常值;plt.xticks(rotation=45)处理刻度旋转,tick_params隐藏刻度、plt.text做选择性标注;- 用
value_counts(sort=True)聚合 +plot.barh()+rcParams['figure.figsize']输出可读的分组计数图; - 用两次
plt.barh叠加展示区间,做组间比较。
课后练习(对应 assignment.md):选一个具体物种(例如 Snow goose 雪雁),围绕它建一个 notebook,综合运用线性图、散点图、柱状图三种图型讲一个完整的数据故事。评分标准看三要素:注释是否到位、故事线是否连贯、图表是否美观。
延伸阅读:本仓库后续课程会继续用同一份鸟类数据集展开分布可视化(直方图、密度图)与比例可视化(饼图、华夫图)等主题;Matplotlib 官方提供 PDF 版 cheatsheet 可作日常速查;若需要交互式图表,可自行研究 Plotly 等替代库。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考