Matplotlib样式与定制

专业级图表美化 — 从入门到精通的全面指南

主题: Matplotlib图表样式定制技术

核心内容: 内置样式、rcParams全局配置、自定义样式表、颜色体系、文本注释、刻度格式化、图例定制

适用对象: Python数据分析与可视化从业者,希望提升图表专业度与美观度的学习者

关键词: Matplotlib, 样式, rcParams, colormap, 图例, 刻度, 注释, 颜色, Python, 数据可视化

一、Matplotlib样式体系概述

Matplotlib是Python数据可视化生态中最核心的基础库,几乎所有的数据分析与科学计算项目都离不开它。然而,Matplotlib默认的图表样式在视觉上较为朴素,难以满足出版级、演示级的审美需求。好在Matplotlib提供了三层样式定制体系,让开发者能够从粗到细地控制图表的每一个视觉元素:

  1. 内置样式(Built-in Styles): 通过 plt.style.use() 一行代码即可切换图表的整体视觉风格,适合快速迁移到不同场景。
  2. rcParams全局配置: 通过修改 matplotlib.rcParams 字典,可以精细控制字体、颜色、线宽、刻度、图例、网格等几乎所有元素的默认值,是实现统一风格的基础。
  3. 自定义样式表(.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_8seaborn库经典风格,灰色背景、浅色网格、柔和的配色方案学术论文与报告
classicMatplotlib早期版本经典风格,白色背景无网格向后兼容
dark_background深色背景搭配亮色线条,对比度高、视觉冲击力强演示文稿、暗色模式
fivethirtyeight仿FiveThirtyEight数据新闻网站风格,加粗标题、干净网格数据新闻、博客文章
bmh贝叶斯方法与统计计算风格,类似R包的默认样式贝叶斯统计可视化
grayscale灰度配色方案,去除所有彩色元素黑白印刷出版
tableau-colorblind10Tableau软件的色盲友好型配色方案色盲友好型图表
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) # 在with块内,图表使用dark_background样式 plt.show() # 离开with块后恢复之前的样式 # 方法三:组合多个样式(后面的样式会覆盖前面的冲突项) 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 # 遍历前5种样式查看效果 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 # 查看全部rcParams(约350个参数) print("rcParams总数:", len(mpl.rcParams)) # 查看特定参数 print(mpl.rcParams['figure.figsize']) # (6.4, 4.8) print(mpl.rcParams['font.size']) # 10 print(mpl.rcParams['lines.linewidth']) # 1.5 # 方法一:直接修改字典 plt.rcParams['figure.figsize'] = (12, 8) plt.rcParams['font.size'] = 14 # 方法二:使用plt.rc()批量设置同一组 plt.rc('font', size=14, family='SimHei') # 字体 plt.rc('lines', linewidth=2, linestyle='-') # 线条 plt.rc('grid', color='gray', alpha=0.3) # 网格 # 方法三:使用update()批量更新 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)

# 默认颜色循环(10种颜色,绘制多条线时自动循环) 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 # X轴刻度标签字号 plt.rcParams['xtick.color'] = '#333333' # X轴刻度颜色 plt.rcParams['xtick.direction'] = 'in' # 刻度方向(in/out/inout) 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') # 临时修改(推荐):使用rc_context上下文管理器 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中的键值对应。空行和以 # 开头的行会被忽略。

# 文件: my_style.mplstyle # 自定义图表样式 # 图形设置 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']) # 方式四:在Python代码中动态创建样式表 style_dict = { 'figure.figsize': (12, 8), 'font.size': 14, 'axes.grid': True, } plt.style.use(style_dict)

团队协作中的样式管理

  • 版本控制:.mplstyle 文件纳入Git管理,确保团队成员使用一致的图表风格
  • 按用途分类: 为论文、报告、演示等不同场景分别创建样式文件
  • 名称规范: 使用清晰的文件命名,如 paper_style.mplstyleslide_style.mplstyle
  • 安装路径: 将样式文件放入 ~/.matplotlib/stylelib/ 目录,即可通过 plt.style.use('style_name') 直接引用

五、颜色体系(Named colors / Hex / RGB / Colormap / Cycler)

颜色是数据可视化中最具表现力的视觉通道。Matplotlib提供了极为丰富的颜色系统,从简单的命名颜色到复杂的颜色映射(Colormap),再到专门的颜色循环机制(Cycler),覆盖了所有可视化场景的需求。合理运用颜色不仅能提升图表的美观度,更能有效地传递数据信息。

5.1 颜色的表示方法

Matplotlib支持多种颜色表示方式,灵活适配不同场景:

# 1. Named colors(命名颜色,140+种) plt.plot(x, y, color='red') plt.plot(x, y, color='steelblue') plt.plot(x, y, color='lightcoral') # 2. Hex code(十六进制,最常用) plt.plot(x, y, color='#1f77b4') plt.plot(x, y, color='#ff7f0e') plt.plot(x, y, color='#2ca02c') # 3. RGB/RGBA元组(值范围0-1) plt.plot(x, y, color=(0.121, 0.466, 0.705)) # RGB plt.plot(x, y, color=(0.121, 0.466, 0.705, 0.6)) # RGBA(带透明度) # 4. 灰度字符串 plt.plot(x, y, color='0.5') # 50%灰度 plt.plot(x, y, color='gray') # 灰色 plt.plot(x, y, color='0.2') # 深灰色 # 5. 简写字符(单字符颜色代码) # 'b'=blue, 'g'=green, 'r'=red, 'c'=cyan, 'm'=magenta, 'y'=yellow, 'k'=black, 'w'=white plt.plot(x, y, color='b') # 查看所有Named colors from matplotlib import colors as mcolors print(list(mcolors.CSS4_COLORS.keys())[:10]) # CSS4颜色 print(len(mcolors.XKCD_COLORS)) # XKCD颜色(949种) print(len(mcolors.TABLEAU_COLORS)) # Tableau颜色(10种)

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不推荐使用)
# 查看所有Colormap from matplotlib import cm print(list(cm._cmap_registry.keys())[:20]) # 使用Colormap import numpy as np data = np.random.randn(10, 10) # 方式一:在绘图函数中指定cmap plt.imshow(data, cmap='viridis') plt.colorbar(label='值') # 方式二:获取Colormap对象后使用 cmap = plt.get_cmap('coolwarm') colors_for_values = cmap(np.linspace(0, 1, 10)) # 获取10个色值 # 方式三:Colormap的分段使用 cmap = plt.get_cmap('Blues') for i in range(5): color = cmap(0.3 + i * 0.15) # 取Blues色带中30%~90%区间的颜色 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是控制颜色循环的关键。

# 默认Cycler(10种Tableau颜色) # 查看当前颜色循环 print(plt.rcParams['axes.prop_cycle']) # 方法一:使用cycler模块自定义 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) # 使用plt.text()在任意位置添加文本 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数学表达式,只需将表达式放在一对美元符号 $...$ 中即可。在科学计算和学术图表中,这一功能对于显示公式、希腊字母、数学符号极为有用。

# 行内LaTeX(使用文本模式LaTeX) ax.set_title('$\\sin(x)$ 与 $\\cos(x)$ 函数图像', fontsize=16) ax.set_xlabel('$x$ (rad)') ax.set_ylabel('$f(x)$') # 在text()中使用LaTeX 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}$') # 使用完整LaTeX渲染引擎(需要安装LaTeX,默认使用mathtext) plt.rcParams['text.usetex'] = True # 启用完整LaTeX引擎 ax.set_title(r'\textbf{正弦函数} $y = \sin(x)$')

6.4 文本属性综合控制

# 使用fontdict统一设置文本样式 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) # 主刻度:每10个单位一个 ax.xaxis.set_major_locator(ticker.MultipleLocator(10)) # 次刻度:每2个单位一个 ax.xaxis.set_minor_locator(ticker.MultipleLocator(2)) # Y轴:MaxNLocator(自动选择最优刻度数) ax.yaxis.set_major_locator(ticker.MaxNLocator(nbins=8)) # 最多8个主刻度 # 其他常用Locator示例 # ax.xaxis.set_major_locator(ticker.AutoLocator()) # 自动定位(默认) # ax.xaxis.set_major_locator(ticker.FixedLocator([0,25,50,75,100])) # 固定位置 # ax.xaxis.set_major_locator(ticker.LinearLocator(11)) # 均匀放置11个刻度 # ax.xaxis.set_major_locator(ticker.LogLocator()) # 对数刻度 # ax.xaxis.set_major_locator(ticker.NullLocator()) # 隐藏所有刻度 # ax.xaxis.set_major_locator(ticker.IndexLocator(base=5, offset=0)) # 索引刻度

7.2 Formatter刻度格式化器

Formatter控制刻度标签的显示格式,可以将数值转化为更易读的文本形式。

# StrMethodFormatter:使用Python格式化字符串 ax.yaxis.set_major_formatter(ticker.StrMethodFormatter('{x:.2f}')) # 保留2位小数 # FuncFormatter:使用自定义函数 def millions_formatter(x, pos): return f'${x/1e6:.1f}M' ax.xaxis.set_major_formatter(ticker.FuncFormatter(millions_formatter)) # FormatStrFormatter(旧API,仍兼容) # ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.1f')) # PercentFormatter:百分比格式 ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1.0)) # 0.5 -> 50% # NullFormatter:隐藏刻度标签 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()) # 每周一个次刻度 # 日期格式化器:显示为"Jan 2024"格式 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()) # 其他常用日期格式 # '%Y-%m-%d' -> 2024-01-15 # '%b %d' -> Jan 15 # '%m/%d' -> 01/15 # '%H:%M' -> 14:30 # '%Y-Q%q' -> 2024-Q1(需要自定义Formatter) # 其他日期定位器 # mdates.YearLocator() # 每年 # mdates.MonthLocator() # 每月 # mdates.WeekdayLocator() # 每周 # mdates.DayLocator() # 每天 # mdates.HourLocator() # 每小时 # ConciseDateFormatter(简洁日期格式,Matplotlib 3.1+) locator = mdates.AutoDateLocator() ax.xaxis.set_major_formatter(mdates.ConciseDateFormatter(locator))

7.4 科学计数法与大数值刻度

# 使用ScalarFormatter的科学计数法 ax.yaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True)) ax.ticklabel_format(style='sci', axis='y', scilimits=(0, 0)) # scilimits=(m, n) 表示当数值在 10^m ~ 10^n 范围外时启用科学计数法 # scilimits=(0, 0) 表示始终使用科学计数法 # 使用EngFormatter(工程计数法,以10^3为步长) ax.yaxis.set_major_formatter(ticker.EngFormatter()) # 输出结果会显示为 1k, 10k, 100k, 1M, 10M 等

7.5 tick_params()精细控制

# tick_params可以一次性设置刻度的多个属性 ax.tick_params( axis='both', # 'x', 'y', 或 'both' which='major', # 'major', 'minor', 或 'both' direction='in', # 刻度方向: 'in', 'out', 'inout' 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 图例位置控制

图例位置由 locbbox_to_anchor 两个参数共同控制。loc 指定图例的对齐锚点,bbox_to_anchor 指定该锚点的放置位置。

# loc参数的预定义位置 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') # 右侧居中 # loc也可以使用数字编码 # 0:'best' 1:'upper right' 2:'upper left' 3:'lower left' # 4:'lower right' 5:'right' 6:'center left' 7:'center right' # 8:'lower center' 9:'upper center' 10:'center' # bbox_to_anchor精确定位(使用坐标轴坐标系,0~1范围) ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1)) # 将图例的左上角锚定在坐标轴右侧外部 # 将图例完全置于坐标轴外部(右侧) ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) # bbox_to_anchor也可以使用绝对坐标 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)) # ncol=2 表示2列排列,图例置于图表下方 # 三列图例 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) # 自定义图例句柄(使用Patch创建颜色块) 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='_隐藏') # 以下划线开头的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]) # 只显示曲线1和曲线3的图例

图例布局综合示例

当图例很多且图表空间有限时,推荐将图例放置在图表外部,使用多列布局:

# 图例的最佳实践:置于图表右侧外部 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')

九、核心要点总结

十、完整实战示例:综合运用所有技巧

下面的综合示例将前面所有知识点融会贯通,展示如何创建一张出版级的专业图表:

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()