Python数据分析(十二):Matplotlib 数据可视化
目录
2. 标题与坐标轴标签(title / xlabel / ylabel)
一、pyplot
在 Matplotlib 的体系中,matplotlib.pyplot(通常简写为 plt)是最先接触到的模块。它提供了一套类似于 MATLAB 风格的状态机接口,旨在让用户能以极简的代码量完成数据的图形化展示
1. 什么是状态机接口
所谓状态接口,是指 pyplot 在后台隐式维护着一个全局状态。当你调用 pyplot 的各个函数时,系统会自动跟踪并锁定当前的画布与当前的坐标系
这种设计模式的核心特点是隐式操作:
-
调用 plt.figure() 时,后台会自动创建并激活一个新的画布
-
调用 plt.plot() 时,图形会自动绘制在当前处于激活状态的坐标系上
-
调用 plt.title() 或 plt.xlabel() 时,相关文本标注会直接叠加到当前坐标系中
在此模式下,开发者无需显式地创建图表变量,也不需要将对象作为参数传递,所有绘图指令均按照代码的顺序依次叠加生效
2. pyplot 典型代码示例
以下是一个基于 pyplot 接口绘制基础折线图的典型代码示例:
import matplotlib.pyplot as plt
# 1. 准备数据
x = [1, 2, 3, 4, 5]
y = [2, 5, 7, 8, 11]
# 2. 创建画布与绘制图形(隐式作用于当前全局状态)
plt.figure(figsize=(8, 4))
plt.plot(x, y, color='steelblue', linestyle='--', marker='o')
# 3. 添加图表元数据
plt.title('Monthly Sales Growth Trend')
plt.xlabel('Month')
plt.ylabel('Sales (k USD)')
plt.grid(True)
# 4. 渲染图表
plt.show()
输出结果:

在这个流程中,所有的 plt. 方法都直接作用于全局状态下的图表,逻辑顺畅,阅读门槛低
3. pyplot 接口的优势与局限性
优势
-
代码简洁:省去了对象实例化与引用的代码,适合快速上手
-
适配探索性分析:在 Jupyter Notebook 中进行即时的数据预览或快速验证假设时,能够提供极高的交互效率
局限性
-
状态混淆风险:当脚本中同时存在多个画布或涉及复杂的多子图交替绘制时,仅依赖隐式状态容易导致绘图指令作用错位
-
定制粒度受限:对于复杂的嵌套排版、多轴联动或极深度的细微样式调整,pyplot 的表达能力相对受限
pyplot 接口是 Matplotlib 提供的 "快捷通道",它通过封装底层的对象逻辑,为日常分析提供了直观高效的工具。理解其状态机的运作机制,不仅有助于高效进行基础绘图,也为后续过渡到面向对象接口打下逻辑基础
二、常见图
在实际分析工作中,熟练掌握常用图表的 API 接口签名及其核心参数,是提高绘图效率的关键。本章将详细介绍五种最基础且常用的图表类型,并给出对应的 API 方法签名、核心参数说明及完整代码示例
1. 折线图
折线图主要用于展示数据随连续变量(通常是时间)的变化趋势
plt.plot(x, y, fmt=None, *, lineStyle=None, lineWidth=None,
color=None, marker=None, label=None, **kwargs)
x, y:数据点的横轴与纵轴坐标
fmt:快捷格式化字符串,如 'r--o' 代表红色(r)、虚线(--)、圆点标记(o)
color:指定线条颜色,支持颜色名称(如 'blue')、HEX 码
linestyle:线条样式,常用取值有 '-'(实线)、'--'(虚线)、'-.'(点划线)、':'(点线)
linewidth:线条宽度,数值型(如 2.0)
marker:数据点标记形状,如 'o'(圆点)、's'(方块)、'^'(三角形)
label:数据序列的标签,用于生成图例
代码示例
# 准备数据
months = [1, 2, 3, 4, 5, 6]
sales = [120, 135, 125, 145, 160, 175]
# 绘制折线图
plt.figure(figsize=(7, 4))
plt.plot(months, sales, color='#2b5c8f',
linestyle='-', linewidth=2, marker='o', label='Product A')
plt.title('Monthly Sales Trend')
plt.xlabel('Month')
plt.ylabel('Sales Volume')
plt.legend()
plt.grid(True, linestyle=':', alpha=0.6)
plt.show()
输出结果:

2. 柱状图
柱状图用于比较离散分类数据之间的数值大小差异
plt.bar(x, height, width=0.8, bottom=None, *, align='center',
color=None, edgecolor=None, label=None, **kwargs)
x:分类标签的横坐标
height:柱体的高度(对应数值大小)
width:柱体的宽度
bottom:柱体基底的 Y 轴坐标
align:柱体与 X 轴刻度的对齐方式,可选 'center'(居中)或 'edge'(对齐边沿)
color:柱体的填充颜色
edgecolor:柱体边框线颜色
若需绘制水平条形图,可使用 plt.barh(y, width, height=0.8, left=None, ...) 接口
代码示例
# 准备数据
categories = ['East', 'West', 'South', 'North']
revenue = [450, 320, 280, 410]
# 绘制柱状图
plt.figure(figsize=(7, 4))
plt.bar(categories, revenue, width=0.5, color='#4c8be2',
edgecolor='#1d4e89', label='Revenue')
plt.title('Regional Revenue Comparison')
plt.xlabel('Region')
plt.ylabel('Revenue (k USD)')
plt.legend()
plt.show()
输出结果:

3. 饼图
饼图用于呈现离散分类占总体的比例关系
plt.pie(x, explode=None, labels=None, colors=None,
autopct=None, shadow=False, startangle=0, **kwargs)
x:各部分的数值序列
labels:各扇区的分类文本标签
autopct:控制扇区内百分比数字的格式化字符串
explode:指定各扇区偏离中心的距离列表(例如 [0, 0.1, 0] 用于突出显示第二个扇区)
colors:各个扇区的颜色列表
startangle:饼图绘制的起始角度,默认为 0(从 X 轴正方向逆时针旋转)
代码示例
market_share = [35, 25, 20, 20]
labels = ['Brand A', 'Brand B', 'Brand C', 'Others']
explode = [0.05, 0, 0, 0] # 突出显示第一项
# 绘制饼图
plt.figure(figsize=(6, 6))
plt.pie(
market_share,
labels=labels,
autopct='%.1f%%',
explode=explode,
startangle=140,
colors=['#5b9bd5', '#ed7d31', '#a5a5a5', '#ffc000']
)
plt.title('Market Share Distribution')
plt.show()
输出结果:

4. 散点图
散点图用于探索两个连续变量之间的分布模式、相关性或异类聚集情况
plt.scatter(x, y, s=None, c=None, marker=None,
cmap=None, alpha=None, label=None, **kwargs)
x, y:数据点的二元坐标序列
s:散点的大小
c:散点的颜色
marker:散点的形状符号
cmap:色彩映射表,当 c 为连续数值时生效
alpha:透明度(0.0 全透明至 1.0 不透明)
代码示例
ad_budget = [10, 20, 30, 40, 50, 60, 70, 80]
sales_volume = [15, 28, 36, 45, 51, 68, 74, 89]
conversions = [100, 200, 350, 400, 500, 750, 800, 950] # 映射为点的大小
# 绘制散点图
plt.figure(figsize=(7, 4))
plt.scatter(ad_budget, sales_volume, s=conversions,
c=sales_volume, cmap='Blues', alpha=0.8, edgecolors='black')
plt.title('Ad Budget vs. Sales Volume')
plt.xlabel('Ad Budget (k USD)')
plt.ylabel('Sales Volume (k Units)')
plt.colorbar(label='Sales Scale')
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
输出结果:

5. 箱线图
箱线图主要用于展示数据的五数概括(最小值、下四分位数、中位数、上四分位数、最大值)以及统计异常值
plt.boxplot(x, notch=None, vert=None, patch_artist=None,
labels=None, showmeans=None, **kwargs)
x:输入数据,可以是单个序列,也可以是二维数组
vert:布尔值,True 绘制纵向箱线图,False 绘制横向箱线图
notch:布尔值,是否绘制带 V 型凹口的箱线图(用于展示中位数的置信区间)
patch_artist:布尔值,若为 True,则填充箱体内部颜色;若为 False,仅绘制线框
tick_labels:各个箱线序列的名称标签
showmeans:布尔值,是否额外标出均值点
代码示例
group1 = [65, 72, 75, 80, 82, 88, 95]
group2 = [50, 60, 70, 75, 78, 85, 99]
group3 = [78, 82, 84, 86, 89, 90, 92]
data = [group1, group2, group3]
# 绘制箱线图
plt.figure(figsize=(7, 4))
plt.boxplot(data, tick_labels=['Group A', 'Group B', 'Group C'],
patch_artist=True, showmeans=True)
plt.title('Performance Distribution Across Groups')
plt.ylabel('Scores')
plt.grid(axis='y', linestyle=':', alpha=0.7)
plt.show()
输出结果:

小结
| 图表类型 | 核心 API | 典型应用场景 | 关键参数 |
|---|---|---|---|
| 折线图 | plt.plot() | 时间序列变化趋势 | linestyle, marker |
| 柱状图 | plt.bar() / plt.barh() | 分类数值大小比较 | width, bottom |
| 饼图 | plt.pie() | 构成与占比分析 | autopct, explode |
| 散点图 | plt.scatter() | 双变量相关性与分布 | s, c, cmap, alpha |
| 箱线图 | plt.boxplot() | 分位数分布与异常值识别 | patch_artist, vert |
三、面向对象接口
如果说 pyplot 是 Matplotlib 的快捷模式,那么面向对象接口才是 Matplotlib 的底层完全形态。在企业级工程实践、自动化报表开发以及生产环境中,面向对象接口是更为推荐的代码规范
1. Figure 与 Axes
理解面向对象接口,关键在于理清两个核心概念:Figure(画布) 与 Axes(坐标系/子图)

1. Figure 对象(画布)
-
定位:包含所有图形元素的顶层容器
-
职责:管理整幅图的大小、背景色、分辨率,以及管理画板上的所有子图
2. Axes 对象(坐标系/子图)
-
定位:真正承载数据图形(折线、柱体、散点等)的区域
-
职责:拥有独立的 X/Y 轴、刻度、标签、标题与图例。一张 Figure 上可以包含一个或多个 Axes
2. plt.subplots()
在面向对象风格中,最经典、使用频率最高的初始化方法是调用 plt.subplots()
fig, ax = plt.subplots(nrows=1, ncols=1, *, sharex=False,
sharey=False, figsize=None, dpi=None, **kwargs)
参数说明:
nrows, ncols:子图网格的行数与列数
figsize:画布尺寸,单位为英寸
sharex, sharey:是否共享 X 轴或 Y 轴的刻度与范围,常用于多子图对比分析
dpi:图像分辨率,默认通常为 100
返回值;
fig:返回创建的 figure 对象
ax:若为 1x1 布局,返回单个 axes 对象
若为多子图布局,返回由 axes 对象组成的 NumPy 数组
3. 为什么推荐面向对象接口
在实际项目中,团队通常强制要求采用面向对象风格,其核心逻辑在于以下四个维度:
1. 消除隐式状态的副作用
pyplot 依赖全局状态机,在执行多图绘制、循环画图或并发任务时,很容易因为 "画布/坐标系" 未重置而导致绘制内容错位。面向对象接口中,所有操作都显式绑定在具体的对象变量上,逻辑透明且线程安全
2. 多子图支持
在涉及多张子图排版时,面向对象接口可以通过数组索引精确控制每一个子图:
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# 显式控制各个子图,彼此互不干扰
axes[0, 0].plot(x, y1)
axes[0, 1].bar(x, y2)
axes[1, 0].scatter(x, y3)
axes[1, 1].boxplot(y4)
3. 方法命名更严谨
在 Axes 对象中,属性设置采用了面向对象语言通用的 Getter/Setter 范式。相比于 pyplot 的无规律命名,面向对象 API 更加规范:
| 绘图需求 | pyplot 接口 | 面向对象 API |
|---|---|---|
| 设置标题 | plt.title() | ax.set_title() |
| 设置 X 轴标签 | plt.xlabel() | ax.set_xlabel() |
| 设置 Y 轴范围 | plt.ylim() | ax.set_ylim() |
| 设置 X 轴刻度 | plt.xticks() | ax.set_xticks() |
| 显示网格 | plt.grid() | ax.grid() |
4. 便于封装与集成
当需要把绘图逻辑封装为函数、类或集成到 Web 后端以及 Streamlit 数据大屏中时,面向对象风格能够直接传递 fig 或 ax 句柄,从而实现图表样式的模块化定制与灵活复用
4. 代码案例
以下是用面向对象 API 绘制双子图对比的范例:
# 1. 准备数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 2. 创建画布与 1 行 2 列的子图对象
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5), sharey=True)
# 3. 绘制第一个子图
ax1.plot(x, y1, color='#1f77b4', label='Sin(x)')
ax1.set_title('Sine Wave')
ax1.grid(True, linestyle='--')
# 4. 绘制第二个子图
ax2.plot(x, y2, color='#ff7f0e', linestyle='--', label='Cos(x)')
ax2.set_title('Cosine Wave')
ax2.grid(True, linestyle='--')
plt.show()
输出结果:

四、图形美化
在实际的数据分析开发中,仅绘制出图形的骨架是远远不够的。未经过美化的原始图表往往缺乏上下文、字号过小或缺乏必要的辅助元素,极易造成阅读障碍
在日常开发中,设置画布尺寸、标题与坐标轴标签、图例以及网格辅助线是几乎每天都会用到的配置项。本章将逐一介绍这些元素的配置 API 与美化技巧
1. 画布尺寸设置
画布尺寸决定了图表呈现的比例。合理的尺寸能够避免元素拥挤或布局变形
API 方法与参数
在面向对象接口中,通常在创建 Figure 对象时指定 figsize:
fig, ax = plt.subplots(figsize=(float, float), dpi=int)
figsize:以元组形式传入 (宽度, 高度),单位为英寸。常见比例有:
(8, 5) 或 (8, 4.5):标准横屏比例(16:9 或 16:10)
(12, 6):宽屏比例
(6, 6):等宽正方形比例
dpi:每英寸点数。开发展示通常设为 100,若用于高清印刷或导出可设置为 300 及以上
2. 标题与坐标轴标签(title / xlabel / ylabel)
文本标注是让图表具备自我解释能力的基础
API 方法签名
# 面向对象 API
ax.set_title(label, fontsize=12, fontweight='bold', pad=15, color='black')
ax.set_xlabel(xlabel, fontsize=10, labelpad=10, color='gray')
ax.set_ylabel(ylabel, fontsize=10, labelpad=10, color='gray')
fontsize:字号大小,可用数值或预设字符串(如 'large')
fontweight:字体粗细,如 'normal'、'bold'
pad / labelpad:坐标轴的边距像素值
color:文本颜色
3. 图例配置(legend)
当图表中包含多条数据序列或多个对比分类时,图例(Legend)是识别不同数据的桥梁
API 方法签名
ax.legend(loc='best', frameon=True, fontsize=10, ncol=1, bbox_to_anchor=None)
loc:图例在子图区域内的摆放位置。常用取值包括:
'best':系统自动选择覆盖数据最少的最佳位置
'upper right', 'upper left', 'lower right', 'lower left', 'center'。
bbox_to_anchor:用于将图例定位到子图区域之外
frameon:布尔值,是否保留图例的外框线
ncol:图例的列数。当类别较多且采用水平排版时,可设为 ncol=2 或 ncol=3
只有在调用绘图方法(如 ax.plot() 或 ax.bar())时显式指定了 label='标签名' 参数,调用 ax.legend() 才能正确渲染图例
4. 网格辅助线(grid)
合理的网格线能提升读者对数据点具体数值的读数精度
API 方法签名
ax.grid(visible=True, which='major', axis='both',
linestyle='--', alpha=0.5, color='gray')
visible:是否显示网格线
axis:控制网格线的方向,可选 'both'(双向)、'x'(仅竖线)、'y'(仅横线)
linestyle:网格线的线型,通常建议使用虚线 ':' 或 '--'
alpha:透明度(0.0 至 1.0)
总结
本章围绕 Matplotlib 展开,学习了状态接口和面向对象接口两种绘图方式,并掌握了折线图、柱状图、饼图、散点图、箱线图等常见图表的绘制方法。同时,我们还学习了标题、坐标轴、图例、网格等图形美化技巧,能够根据不同的数据特点绘制更加清晰、美观的数据图表
下一篇将学习 Seaborn 统计可视化,进一步了解 Seaborn 在统计图表绘制方面的优势,并掌握单变量、双变量和多变量数据的可视化方法,使数据分析结果更加直观、易于理解

更多推荐


所有评论(0)