Matplotlib样式与定制
专业级图表美化 — 从入门到精通的全面指南
一、Matplotlib样式体系概述
Matplotlib是Python数据可视化生态中最核心的基础库,几乎所有的数据分析与科学计算项目都离不开它。然而,Matplotlib默认的图表样式在视觉上较为朴素,难以满足出版级、演示级的审美需求。好在Matplotlib提供了三层样式定制体系,让开发者能够从粗到细地控制图表的每一个视觉元素:
- 内置样式(Built-in Styles): 通过
plt.style.use() 一行代码即可切换图表的整体视觉风格,适合快速迁移到不同场景。
- rcParams全局配置: 通过修改
matplotlib.rcParams 字典,可以精细控制字体、颜色、线宽、刻度、图例、网格等几乎所有元素的默认值,是实现统一风格的基础。
- 自定义样式表(.mplstyle文件): 将rcParams配置保存为独立的样式文件,便于复用、共享和版本管理,适合团队内部或项目中统一图表风格。
本文将从这三个层面入手,结合丰富的代码示例,系统讲解Matplotlib样式定制的每一个关键技术点,帮助你快速构建专业级的可视化图表。
核心原则
- 分层控制: 快速切换用内置样式,精细控制用rcParams,复用分发用样式表
- 优先级: 显式调用(如
plt.plot(color='red'))> 样式表设置 > rcParams默认值
- 一致性: 同一项目中尽量保持统一的样式风格,避免混用
二、内置样式(Built-in Styles)
Matplotlib自3.0版本起内置了丰富的样式表,只需一行代码即可彻底改变图表的视觉呈现。这是最快速的图表美化方式,特别适合在探索性数据分析阶段快速切换不同风格进行预览。
查看与应用内置样式
使用 plt.style.available 可以查看当前版本所有可用的内置样式列表,然后通过 plt.style.use() 应用指定样式。
import matplotlib.pyplot as plt
import numpy as np
print("可用样式数量:", len(plt.style.available))
print(plt.style.available)
主要内置样式详解
以下是最常用和最具代表性的内置样式:
| 样式名称 | 特点描述 | 适用场景 |
ggplot | 仿R语言ggplot2风格,浅灰背景、白色网格线、色彩鲜明 | 数据探索与统计分析 |
seaborn-v0_8 | seaborn库经典风格,灰色背景、浅色网格、柔和的配色方案 | 学术论文与报告 |
classic | Matplotlib早期版本经典风格,白色背景无网格 | 向后兼容 |
dark_background | 深色背景搭配亮色线条,对比度高、视觉冲击力强 | 演示文稿、暗色模式 |
fivethirtyeight | 仿FiveThirtyEight数据新闻网站风格,加粗标题、干净网格 | 数据新闻、博客文章 |
bmh | 贝叶斯方法与统计计算风格,类似R包的默认样式 | 贝叶斯统计可视化 |
grayscale | 灰度配色方案,去除所有彩色元素 | 黑白印刷出版 |
tableau-colorblind10 | Tableau软件的色盲友好型配色方案 | 色盲友好型图表 |
Solarize_Light2 | 暖色调浅色主题,护眼舒适 | 长时间数据工作 |
fast | 简化渲染风格,减少细节以提升绘制速度 | 大数据量快速预览 |
应用样式的方法
plt.style.use('ggplot')
with plt.style.context('dark_background'):
fig, ax = plt.subplots()
ax.plot(np.sin(x), color='#00ff88', lw=2)
plt.show()
plt.style.use(['seaborn-v0_8', 'grayscale'])
最佳实践
- 全局切换: 在脚本开头调用
plt.style.use(),确保全文风格统一
- 局部覆盖: 使用
plt.style.context() 为特定图表应用不同风格,不影响全局
- 组合策略: 先应用一个基础风格(如seaborn-v0_8),再用自定义样式覆盖部分参数
- 预览技巧: 使用
plt.style.available 遍历所有风格,快速找到最适合的样式
def plot_with_style(style_name):
with plt.style.context(style_name):
fig, ax = plt.subplots(figsize=(8, 4))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), label='sin(x)')
ax.plot(x, np.cos(x), label='cos(x)')
ax.set_title(f'样式: {style_name}')
ax.legend()
return fig
for style in plt.style.available[:5]:
plot_with_style(style)
三、rcParams全局配置
rcParams 是Matplotlib的核心配置字典,几乎控制着图表中所有元素的默认行为。深入理解rcParams是实现精细化定制的关键。通过修改rcParams,可以一次性地为所有图表设定统一的字体、字号、线宽、颜色循环、网格样式、刻度属性等,无需在每个绘图函数中重复设置。
查看与修改rcParams
import matplotlib as mpl
import matplotlib.pyplot as plt
print("rcParams总数:", len(mpl.rcParams))
print(mpl.rcParams['figure.figsize'])
print(mpl.rcParams['font.size'])
print(mpl.rcParams['lines.linewidth'])
plt.rcParams['figure.figsize'] = (12, 8)
plt.rcParams['font.size'] = 14
plt.rc('font', size=14, family='SimHei')
plt.rc('lines', linewidth=2, linestyle='-')
plt.rc('grid', color='gray', alpha=0.3)
plt.rcParams.update({
'axes.facecolor': '#f5f5f5',
'axes.edgecolor': '#333333',
'axes.grid': True,
'grid.alpha': 0.3,
'grid.linestyle': '--',
})
核心rcParams分类详解
下面按照功能模块,逐一介绍最常用和最重要的rcParams参数:
(1)图形与画布(Figure)
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['figure.dpi'] = 100
plt.rcParams['figure.facecolor'] = 'white'
plt.rcParams['figure.edgecolor'] = 'white'
plt.rcParams['figure.titlesize'] = 'large'
(2)字体(Font)
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['font.size'] = 12
plt.rcParams['axes.unicode_minus'] = False
(3)坐标轴(Axes)
plt.rcParams['axes.facecolor'] = '#f0f0f0'
plt.rcParams['axes.edgecolor'] = '#333333'
plt.rcParams['axes.linewidth'] = 1.0
plt.rcParams['axes.grid'] = True
plt.rcParams['axes.axisbelow'] = True
plt.rcParams['axes.labelcolor'] = '#333333'
plt.rcParams['axes.labelsize'] = 13
plt.rcParams['axes.titlesize'] = 16
plt.rcParams['axes.titleweight'] = 'bold'
plt.rcParams['axes.spines.top'] = False
plt.rcParams['axes.spines.right'] = False
(4)颜色循环(Prop Cycle)
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=[
'#1f77b4', '#ff7f0e', '#2ca02c', '#d62728',
'#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
'#bcbd22', '#17becf'
])
(5)线条与标记(Lines)
plt.rcParams['lines.linewidth'] = 2.0
plt.rcParams['lines.linestyle'] = '-'
plt.rcParams['lines.color'] = '#1f77b4'
plt.rcParams['lines.marker'] = 'o'
plt.rcParams['lines.markersize'] = 6
plt.rcParams['lines.markeredgewidth'] = 1.0
(6)网格(Grid)
plt.rcParams['grid.color'] = 'gray'
plt.rcParams['grid.alpha'] = 0.3
plt.rcParams['grid.linestyle'] = '-'
plt.rcParams['grid.linewidth'] = 0.8
(7)刻度(Ticks)
plt.rcParams['xtick.labelsize'] = 11
plt.rcParams['xtick.color'] = '#333333'
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['xtick.major.width'] = 1.0
plt.rcParams['xtick.minor.visible'] = True
plt.rcParams['ytick.labelsize'] = 11
plt.rcParams['ytick.color'] = '#333333'
plt.rcParams['ytick.direction'] = 'in'
(8)图例(Legend)
plt.rcParams['legend.loc'] = 'best'
plt.rcParams['legend.fontsize'] = 11
plt.rcParams['legend.frameon'] = True
plt.rcParams['legend.fancybox'] = True
plt.rcParams['legend.shadow'] = False
plt.rcParams['legend.facecolor'] = 'white'
plt.rcParams['legend.edgecolor'] = '#cccccc'
plt.rcParams['legend.handlelength'] = 2.0
plt.rcParams['legend.handletextpad'] = 0.8
(9)保存(Savefig)
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['savefig.bbox'] = 'tight'
plt.rcParams['savefig.transparent'] = False
plt.rcParams['savefig.format'] = 'png'
实用技巧:重置与恢复
如果修改了rcParams后想恢复默认值,可以使用以下方法:
mpl.rcdefaults()
plt.style.use('default')
with mpl.rc_context({'font.size': 14, 'lines.linewidth': 3}):
fig, ax = plt.subplots()
ax.plot(x, y)
四、自定义样式表(.mplstyle文件与动态修改)
当内置样式无法满足需求,而每次启动都手动设置大量rcParams又显得繁琐时,自定义样式表就是最佳选择。Matplotlib支持从 .mplstyle 文件中加载样式配置,将rcParams的设置持久化为可复用的文件。
创建.mplstyle样式文件
样式文件采用纯文本格式,每行一个配置项,格式为 参数名: 值,与rcParams中的键值对应。空行和以 # 开头的行会被忽略。
figure.figsize: 12, 8
figure.dpi: 120
figure.facecolor: white
font.family: sans-serif
font.sans-serif: SimHei, Microsoft YaHei, DejaVu Sans
font.size: 12
axes.unicode_minus: False
axes.facecolor: #f8f9fa
axes.edgecolor: #333333
axes.linewidth: 1.2
axes.grid: True
axes.axisbelow: True
axes.spines.top: False
axes.spines.right: False
grid.color: #cccccc
grid.alpha: 0.4
grid.linestyle: --
grid.linewidth: 0.6
lines.linewidth: 2.0
lines.linestyle: -
legend.loc: best
legend.fontsize: 11
legend.frameon: True
legend.fancybox: True
xtick.labelsize: 11
xtick.direction: in
ytick.labelsize: 11
ytick.direction: in
savefig.dpi: 300
savefig.bbox: tight
savefig.transparent: False
加载与使用自定义样式
将样式文件放置在当前工作目录下,或者Matplotlib配置目录中的 stylelib/ 文件夹下,即可通过名称直接引用。
plt.style.use('my_style.mplstyle')
plt.style.use('/path/to/my_style.mplstyle')
plt.style.use(['seaborn-v0_8', 'my_style.mplstyle'])
style_dict = {
'figure.figsize': (12, 8),
'font.size': 14,
'axes.grid': True,
}
plt.style.use(style_dict)
团队协作中的样式管理
- 版本控制: 将
.mplstyle 文件纳入Git管理,确保团队成员使用一致的图表风格
- 按用途分类: 为论文、报告、演示等不同场景分别创建样式文件
- 名称规范: 使用清晰的文件命名,如
paper_style.mplstyle、slide_style.mplstyle
- 安装路径: 将样式文件放入
~/.matplotlib/stylelib/ 目录,即可通过 plt.style.use('style_name') 直接引用
五、颜色体系(Named colors / Hex / RGB / Colormap / Cycler)
颜色是数据可视化中最具表现力的视觉通道。Matplotlib提供了极为丰富的颜色系统,从简单的命名颜色到复杂的颜色映射(Colormap),再到专门的颜色循环机制(Cycler),覆盖了所有可视化场景的需求。合理运用颜色不仅能提升图表的美观度,更能有效地传递数据信息。
5.1 颜色的表示方法
Matplotlib支持多种颜色表示方式,灵活适配不同场景:
plt.plot(x, y, color='red')
plt.plot(x, y, color='steelblue')
plt.plot(x, y, color='lightcoral')
plt.plot(x, y, color='#1f77b4')
plt.plot(x, y, color='#ff7f0e')
plt.plot(x, y, color='#2ca02c')
plt.plot(x, y, color=(0.121, 0.466, 0.705))
plt.plot(x, y, color=(0.121, 0.466, 0.705, 0.6))
plt.plot(x, y, color='0.5')
plt.plot(x, y, color='gray')
plt.plot(x, y, color='0.2')
plt.plot(x, y, color='b')
from matplotlib import colors as mcolors
print(list(mcolors.CSS4_COLORS.keys())[:10])
print(len(mcolors.XKCD_COLORS))
print(len(mcolors.TABLEAU_COLORS))
5.2 Colormap颜色映射
Colormap是将数值映射到颜色的重要机制,在热力图、等高线图、伪彩图等场景中不可或缺。Matplotlib根据用途将Colormap分为五大类:
| 分类 | 特点 | 常用Colormap | 适用场景 |
| Sequential(连续色) | 从浅到深的单一色相渐变 | viridis, plasma, inferno, magma, Blues, Greens, YlOrRd, Purples | 连续数值映射,如温度、密度、海拔 |
| Diverging(发散色) | 从中间向两端发散的双色渐变 | coolwarm, RdBu, seismic, PiYG, PuOr, RdYlBu, BrBG | 有正负分界的数据,如变化率、偏差值 |
| Cyclic(循环色) | 首尾相接的循环渐变 | twilight, twilight_shifted, hsv | 角度、相位等循环数据 |
| Qualitative(分类型) | 离散的、高区分度的颜色集合 | Set1, Set2, Set3, Pastel1, Pastel2, tab10, tab20, Accent, Dark2, Paired | 类别型数据,如不同组别的分类 |
| Miscellaneous(杂项) | 特殊效果 | flag, prism, gist_rainbow, rainbow, jet | 特殊视觉效果(jet不推荐使用) |
from matplotlib import cm
print(list(cm._cmap_registry.keys())[:20])
import numpy as np
data = np.random.randn(10, 10)
plt.imshow(data, cmap='viridis')
plt.colorbar(label='值')
cmap = plt.get_cmap('coolwarm')
colors_for_values = cmap(np.linspace(0, 1, 10))
cmap = plt.get_cmap('Blues')
for i in range(5):
color = cmap(0.3 + i * 0.15)
plt.plot(x, y + i, color=color, lw=2, label=f'系列 {i+1}')
5.3 离散色与连续色的选择原则
选色黄金法则
- 类别型数据(性别、地区、产品类型):使用 Qualitative 分类色板,如 tab10、Set1、Pastel1,确保各类之间视觉差异明显
- 有序型数据(低中高、小中大):使用 Sequential 连续色板,如 Blues、viridis,体现数据的递进关系
- 偏差型数据(正负变化、高于/低于基准):使用 Diverging 发散色板,如 coolwarm、RdBu,中间色代表基准值
- 循环型数据(角度、方向、时间周期):使用 Cyclic 循环色板,如 twilight
- 色盲友好: 优先使用 viridis、plasma、inferno、magma 等感知均匀的色板
- 避免jet: jet色板存在视觉失真问题,不推荐用于科学数据可视化
5.4 Cycler颜色循环
当在一张图上绘制多条线或柱状图有多个类别时,Matplotlib使用Cycler机制自动为每一组数据分配不同的颜色。理解和自定义Cycler是控制颜色循环的关键。
print(plt.rcParams['axes.prop_cycle'])
from cycler import cycler
custom_cycle = cycler(color=[
'#e6194b', '#3cb44b', '#ffe119', '#4363d8',
'#f58231', '#911eb4', '#42d4f4', '#f032e6',
])
plt.rcParams['axes.prop_cycle'] = custom_cycle
multi_cycle = (cycler(color=['#1f77b4', '#ff7f0e', '#2ca02c']) *
cycler(linestyle=['-', '--', ':']) *
cycler(marker=['o', 's', '^']))
plt.rcParams['axes.prop_cycle'] = multi_cycle
plt.rcParams['axes.prop_cycle'] = cycler(color=plt.cm.viridis(np.linspace(0, 1, 10)))
颜色工具推荐
- ColorBrewer (colorbrewer2.org): 专业的选色工具,提供色盲友好型配色方案
- Matplotlib Colormap参考: 官方文档提供了所有Colormap的视觉对照表
- palettable库: 提供更多高质量配色方案的Python包
六、文本与注释(annotate / arrowprops / text / LaTeX)
在数据可视化中,恰到好处的文本注释能极大地提升图表的信息传递效率。Matplotlib提供了丰富的文本和注释功能,从简单的坐标轴标签到复杂的带箭头标注,配合数学表达式支持,可以满足几乎所有场景的需求。
6.1 基础文本元素
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 2 * np.pi, 100)
ax.plot(x, np.sin(x), label='sin(x)', color='#e74c3c', lw=2)
ax.set_xlabel('角度 (弧度)', fontsize=13, fontweight='bold')
ax.set_ylabel('函数值', fontsize=13, fontweight='bold')
ax.set_title('正弦函数图像', fontsize=16, fontweight='bold', pad=15)
ax.text(np.pi / 2, 0.2, '波峰位置',
fontsize=12, color='#e74c3c',
bbox=dict(boxstyle='round', facecolor='#ffeaa7', alpha=0.8))
6.2 annotate()高级注释
annotate() 是Matplotlib中最强大的注释工具,它允许在指定数据点添加带有箭头指向的文本注释。其核心参数包括:xy(箭头指向的数据点)、xytext(文本放置位置)、arrowprops(箭头样式配置)。
ax.annotate('最大值',
xy=(np.pi/2, 1),
xytext=(np.pi/2 + 0.5, 1.2),
fontsize=13, color='#c0392b',
arrowprops=dict(
arrowstyle='->',
color='#c0392b',
lw=1.5
))
ax.annotate('最小值',
xy=(3*np.pi/2, -1),
xytext=(3*np.pi/2, -1.5),
fontsize=13, color='#2980b9',
arrowprops=dict(
arrowstyle='fancy',
connectionstyle='arc3,rad=0.3',
color='#2980b9',
lw=2,
facecolor='#3498db',
))
ax.annotate('零点',
xy=(np.pi, 0),
xytext=(np.pi + 0.3, -0.3),
fontsize=12,
bbox=dict(boxstyle='round,pad=0.3',
facecolor='#ecf0f1',
edgecolor='#95a5a6',
alpha=0.9),
arrowprops=dict(
arrowstyle='->',
connectionstyle='angle,angleA=0,angleB=90',
color='#7f8c8d'
))
6.3 数学表达式(LaTeX)
Matplotlib原生支持LaTeX数学表达式,只需将表达式放在一对美元符号 $...$ 中即可。在科学计算和学术图表中,这一功能对于显示公式、希腊字母、数学符号极为有用。
ax.set_title('$\\sin(x)$ 与 $\\cos(x)$ 函数图像', fontsize=16)
ax.set_xlabel('$x$ (rad)')
ax.set_ylabel('$f(x)$')
ax.text(0.5, 0.5, '$\\sum_{n=1}^{\\infty} \\frac{1}{n^2} = \\frac{\\pi^2}{6}$',
fontsize=16, transform=ax.transAxes)
ax.text(0.1, 0.3, '$\\alpha \\beta \\gamma \\delta \\epsilon$')
ax.text(0.1, 0.2, '$\\sigma = \\sqrt{\\frac{1}{N}\\sum_{i=1}^N (x_i - \\mu)^2}$')
plt.rcParams['text.usetex'] = True
ax.set_title(r'\textbf{正弦函数} $y = \sin(x)$')
6.4 文本属性综合控制
font_dict = {
'fontsize': 14,
'fontweight': 'bold',
'color': '#2c3e50',
'family': 'serif',
'style': 'italic',
}
ax.text(0.5, 0.95, '关键注释', fontdict=font_dict,
transform=ax.transAxes, ha='center', va='top')
ax.text(0.1, 0.5, '左对齐', ha='left', transform=ax.transAxes)
ax.text(0.5, 0.5, '居中', ha='center', transform=ax.transAxes)
ax.text(0.9, 0.5, '右对齐', ha='right', transform=ax.transAxes)
七、刻度格式化(Formatter / Locator / 日期刻度 / 科学计数法)
刻度是坐标轴的核心组成部分,直接决定了图表的可读性和信息密度。Matplotlib通过 Locator(定位器) 和 Formatter(格式化器) 两个组件实现对刻度的精细控制。Locator决定刻度线的数量和位置,Formatter决定刻度标签的文本格式。熟练掌握这两个组件,可以应对任何复杂的刻度需求。
7.1 Locator刻度定位器
Locator负责决定在坐标轴的哪些位置放置刻度线。Matplotlib提供了多种定位器以满足不同场景的需求。
import matplotlib.ticker as ticker
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 100, 200)
ax.plot(x, np.sin(x * 0.1) * np.exp(-x * 0.02) * 10)
ax.xaxis.set_major_locator(ticker.MultipleLocator(10))
ax.xaxis.set_minor_locator(ticker.MultipleLocator(2))
ax.yaxis.set_major_locator(ticker.MaxNLocator(nbins=8))
7.2 Formatter刻度格式化器
Formatter控制刻度标签的显示格式,可以将数值转化为更易读的文本形式。
ax.yaxis.set_major_formatter(ticker.StrMethodFormatter('{x:.2f}'))
def millions_formatter(x, pos):
return f'${x/1e6:.1f}M'
ax.xaxis.set_major_formatter(ticker.FuncFormatter(millions_formatter))
ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1.0))
ax.yaxis.set_major_formatter(ticker.NullFormatter())
7.3 日期刻度
日期类型的刻度是最常见的需求之一。Matplotlib通过 matplotlib.dates 模块提供了专门的日期定位器和格式化器,用于处理时间序列数据的刻度显示。
import matplotlib.dates as mdates
import datetime
dates = [datetime.datetime(2024, 1, 1) + datetime.timedelta(days=i) for i in range(365)]
values = np.random.randn(365).cumsum()
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(dates, values)
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_minor_locator(mdates.WeekdayLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(ax.xaxis))
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
locator = mdates.AutoDateLocator()
ax.xaxis.set_major_formatter(mdates.ConciseDateFormatter(locator))
7.4 科学计数法与大数值刻度
ax.yaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
ax.ticklabel_format(style='sci', axis='y', scilimits=(0, 0))
ax.yaxis.set_major_formatter(ticker.EngFormatter())
7.5 tick_params()精细控制
ax.tick_params(
axis='both',
which='major',
direction='in',
length=6,
width=1.2,
colors='#333333',
labelsize=11,
labelcolor='#555555',
pad=8,
top=False,
right=False,
)
八、图例定制(loc / bbox_to_anchor / 柱状图图例 / 多列图例)
图例(Legend)是图表中标识数据系列的说明框,良好的图例设计能大幅提升图表的可读性。Matplotlib的图例系统极为灵活,从位置控制到样式定制,再到自定义句柄,几乎无所不能。
8.1 图例位置控制
图例位置由 loc 和 bbox_to_anchor 两个参数共同控制。loc 指定图例的对齐锚点,bbox_to_anchor 指定该锚点的放置位置。
plt.legend(loc='best')
plt.legend(loc='upper right')
plt.legend(loc='upper left')
plt.legend(loc='lower right')
plt.legend(loc='lower left')
plt.legend(loc='upper center')
plt.legend(loc='lower center')
plt.legend(loc='center')
plt.legend(loc='center left')
plt.legend(loc='center right')
ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1))
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
ax.legend(loc='upper left', bbox_to_anchor=(0.5, -0.15))
8.2 图例外观定制
ax.legend(
loc='upper left',
bbox_to_anchor=(1.02, 1),
fontsize=12,
title='数据系列',
title_fontsize=13,
frameon=True,
fancybox=True,
shadow=True,
facecolor='#f8f9fa',
edgecolor='#cccccc',
framealpha=0.9,
borderpad=0.8,
labelspacing=0.8,
handlelength=2.0,
handletextpad=0.8,
columnspacing=1.0,
)
8.3 多列图例
当数据系列较多时,使用多列图例可以有效节省纵向空间,让图表布局更紧凑。
for i in range(10):
ax.plot(x, y + i, label=f'系列 {i+1}')
ax.legend(ncol=2, loc='upper center', bbox_to_anchor=(0.5, -0.1))
ax.legend(ncol=3, fontsize=10, loc='lower center', bbox_to_anchor=(0.5, -0.25))
8.4 柱状图图例与自定义句柄
categories = ['A', 'B', 'C', 'D']
values_a = [23, 45, 56, 78]
values_b = [34, 29, 67, 45]
x_pos = np.arange(len(categories))
width = 0.35
bars1 = ax.bar(x_pos - width/2, values_a, width, label='2023年', color='#3498db')
bars2 = ax.bar(x_pos + width/2, values_b, width, label='2024年', color='#e67e22')
ax.legend(loc='upper right', fontsize=12)
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
custom_handles = [
Patch(facecolor='#e74c3c', label='红色区域'),
Patch(facecolor='#3498db', label='蓝色区域'),
Line2D([0], [0], color='#2ecc71', lw=3, label='趋势线'),
Line2D([0], [0], marker='o', color='w', markerfacecolor='#9b59b6',
markersize=10, label='数据点'),
]
ax.legend(handles=custom_handles, loc='upper right')
ax.plot(x, y1, label='显示')
ax.plot(x, y2, label='_隐藏')
line1, = ax.plot(x, y1, label='曲线1')
line2, = ax.plot(x, y2, label='曲线2')
line3, = ax.plot(x, y3, label='曲线3')
ax.legend(handles=[line1, line3])
图例布局综合示例
当图例很多且图表空间有限时,推荐将图例放置在图表外部,使用多列布局:
fig, ax = plt.subplots(figsize=(10, 6))
ax.legend(
loc='upper left',
bbox_to_anchor=(1.02, 1),
ncol=1,
fontsize=11,
title='系列名称',
frameon=True,
fancybox=True,
edgecolor='#cccccc',
)
plt.tight_layout()
plt.savefig('chart_with_legend.png', dpi=300, bbox_inches='tight')
九、核心要点总结
- 三层样式体系: 内置样式(快速切换)> 自定义样式表(.mplstyle复用)> rcParams(精细化控制),按需选择,分层使用
- rcParams是根本: 掌握rcParams = 掌握Matplotlib的"默认值系统",一次设置,全局生效。重点掌握 font、axes、lines、grid、xtick/ytick、legend 六大模块
- 颜色选择有方法: 连续数据用 Sequential(如 viridis),偏差数据用 Diverging(如 coolwarm),分类数据用 Qualitative(如 tab10),循环数据用 Cyclic(如 twilight)。避免使用 jet 色板
- 颜色循环用Cycler: 使用
cycler 模块可同时循环颜色、线型、标记等多个属性,避免多系列数据的视觉混淆
- 注释提升信息密度:
annotate() 配合 arrowprops 的 arrowstyle 和 connectionstyle 参数,可以从简单箭头到复杂弧形箭头全掌控
- LaTeX表达数学: 在
$...$ 中使用LaTeX语法可渲染希腊字母、积分符号、公式等,是学术图表的标准配置
- 刻度随心控: Locator 控制刻度的"位置和密度",Formatter 控制刻度的"显示格式"。日期刻度用
mdates 模块专门处理
- 图例外置的技巧:
bbox_to_anchor=(1.02, 1) + loc='upper left' 是最常用的外部图例放置方式。多列用 ncol,自定义句柄用 Patch 和 Line2D
- 一行代码快速美化: 在脚本开头添加
plt.style.use('seaborn-v0_8') 即可秒杀默认样式。如需中文支持加 plt.rcParams['font.sans-serif'] = ['SimHei'] 和 plt.rcParams['axes.unicode_minus'] = False
- 上下文管理安全修改: 使用
plt.style.context() 或 mpl.rc_context() 实现临时样式修改,确保不影响全局配置
十、完整实战示例:综合运用所有技巧
下面的综合示例将前面所有知识点融会贯通,展示如何创建一张出版级的专业图表:
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.ticker as ticker
import numpy as np
from cycler import cycler
plt.style.use('seaborn-v0_8')
plt.rcParams.update({
'font.family': 'sans-serif',
'font.sans-serif': ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'],
'font.size': 12,
'axes.unicode_minus': False,
'figure.figsize': (14, 7),
'figure.dpi': 120,
'axes.facecolor': '#f8f9fa',
'axes.grid': True,
'axes.axisbelow': True,
'axes.spines.top': False,
'axes.spines.right': False,
'grid.color': '#cccccc',
'grid.alpha': 0.3,
'grid.linestyle': '--',
'axes.prop_cycle': cycler(color=[
'#e6194b', '#3cb44b', '#4363d8', '#f58231',
'#911eb4', '#42d4f4', '#f032e6', '#bfef45'
]),
'legend.fontsize': 11,
'legend.frameon': True,
'legend.fancybox': True,
'xtick.direction': 'in',
'ytick.direction': 'in',
'xtick.labelsize': 11,
'ytick.labelsize': 11,
'savefig.dpi': 300,
'savefig.bbox': 'tight',
})
x = np.linspace(0, 4 * np.pi, 200)
fig, ax = plt.subplots()
for i, amplitude in enumerate(np.linspace(0.5, 2.0, 6)):
ax.plot(x, amplitude * np.sin(x) * np.exp(-x * 0.05),
lw=2, alpha=0.85, label=f'A={amplitude:.1f}')
ax.annotate('衰减振荡', xy=(np.pi, 1.8),
xytext=(np.pi * 0.6, 2.5),
fontsize=13, color='#c0392b',
arrowprops=dict(arrowstyle='->', color='#c0392b', lw=1.5))
ax.set_title('不同振幅下阻尼正弦振荡 $y = A \\cdot \\sin(x) \\cdot e^{-0.05x}$',
fontsize=15, fontweight='bold', pad=15)
ax.set_xlabel('时间 $t$ (秒)', fontsize=13)
ax.set_ylabel('振幅 $y$', fontsize=13)
ax.xaxis.set_major_locator(ticker.MultipleLocator(np.pi / 2))
ax.xaxis.set_major_formatter(ticker.FuncFormatter(
lambda val, pos: f'${val/np.pi:.1f}\\pi$' if val != 0 else '0'
))
ax.legend(loc='upper right', title='振幅参数', fontsize=11,
title_fontsize=12, ncol=2, frameon=True, fancybox=True)
plt.tight_layout()
plt.savefig('damped_oscillation.png')
plt.show()