Skip to content

第 8 章 文本、注释与刻度

学习目标

  • ax.text 在图中任意位置加文字
  • ax.annotate 加带箭头的注释
  • 用 Locator 控制刻度位置、Formatter 控制刻度文本
  • 处理日期刻度
  • 用数学符号(mathtext)美化标题与标签

8.1 图内文字:ax.text

ax.text(x, y, 文本) 在数据坐标 (x, y) 处放一段文字。以 30 天气温图为例,在最高点旁标注数值:

python
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(8)
day30 = np.arange(1, 31)
temp30 = 26 + 6 * np.sin(day30 / 30 * 2 * np.pi) + rng.normal(0, 1.5, 30)
peak_i = np.argmax(temp30)

fig, ax = plt.subplots()
ax.plot(day30, temp30)
ax.text(peak_i, temp30[peak_i] + 0.6, f"最高 {temp30[peak_i]:.1f}°C",
        ha="center", fontsize=11, color="#d33f49")
ax.text(1, 20.5, "整体在 26°C 上下波动", fontsize=10, color="#4b5563")
ax.set_title("30 天气温:用 text 在图内加说明")
ax.set_xlabel("日期")
ax.set_ylabel("气温(°C)")
plt.show()

图 8-1 真实输出:ha="center" 让文字以给定点为中心水平对齐(还有 ha="left"/"right"va="top"/"center"/"bottom" 控制垂直对齐):

图 8-1 在图内任意位置加文字

ax.text 的坐标默认是数据坐标(跟着数据走)。想固定在图的角落,用 transform=ax.transAxes,此时 (0,0) 是左下角、(1,1) 是右上角——第 1 章骨架图里的标注就是这么做的。

8.2 带箭头的注释:ax.annotate

ax.annotate(文本, xy=箭头指向的点, xytext=文字所在位置, arrowprops=箭头样式) 是「文字 + 箭头」的组合,专门用来「指出」图里某个关键位置:

python
fig, ax = plt.subplots()
ax.plot(day30, temp30)
ax.annotate("最热的一天", xy=(peak_i, temp30[peak_i]),
            xytext=(16, 33),
            arrowprops=dict(arrowstyle="->", color="#d33f49", lw=1.5),
            fontsize=11, color="#d33f49")
ax.set_title("30 天气温:用 annotate 加箭头注释")
ax.set_xlabel("日期")
ax.set_ylabel("气温(°C)")
plt.show()

图 8-2 真实输出:箭头从 xytext(文字位置)指向 xy(数据点):

图 8-2 带箭头的注释

arrowprops 是一个字典,常用键:arrowstyle(箭头形状,"->""fancy""->," 等)、colorlw(线宽)、connectionstyle(连接曲线样式)。annotate 是「讲故事的图」里最常用的工具。

8.3 刻度定位器与格式化器:Locator / Formatter

自动刻度通常够用,但想精确控制「刻度放哪里、显示成什么样」,要用到两个对象:

  • Locator(定位器):决定刻度放在哪些数值上,如 MultipleLocator(2) 表示每 2 个单位一个刻度。
  • Formatter(格式化器):决定刻度显示成什么文本,如 PercentFormatterFuncFormatter
python
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 20, 300)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x))
ax.xaxis.set_major_locator(plt.MultipleLocator(2))    # 主刻度:每 2 个单位
ax.xaxis.set_minor_locator(plt.MultipleLocator(0.5))  # 次刻度:每 0.5 个单位
ax.grid(True, which="major", alpha=0.4)
ax.grid(True, which="minor", alpha=0.15)
ax.set_title("MultipleLocator:每 2 个单位一个主刻度")
plt.show()

图 8-3 真实输出:主刻度每 2 单位一格、带较深的网格;次刻度每 0.5 单位、网格更浅:

图 8-3 刻度定位器与主次网格

which="major"/"minor" 用来区分主、次刻度分别应用网格或样式。常用定位器还有 MaxNLocator(自动挑「好看」的个数)、FixedLocator(固定位置)、LogLocator(对数刻度)。

8.4 日期刻度

横轴是日期时,Matplotlib 把日期当作「天数」处理,默认刻度可能显示成一串大数字。用 matplotlib.datesDateFormatter 把刻度格式化成 "07-01" 这样的样子:

python
import datetime
import matplotlib.dates as mdates

dates = [datetime.date(2025, 7, 1) + datetime.timedelta(days=i) for i in range(14)]
sales = rng.integers(80, 150, 14).astype(float)

fig, ax = plt.subplots()
ax.plot(dates, sales, marker="o")
ax.xaxis.set_major_formatter(mdates.DateFormatter("%m-%d"))
ax.set_title("7 月上旬每日销售额")
plt.show()

图 8-4 真实输出:横轴刻度变成 07-0107-03…… %m 月、%d 日、%Y 年、%H:%M 时分,格式串与 C 语言 strftime 一致:

图 8-4 日期刻度格式化

日期坐标与数值坐标一样可以 set_xlim 缩放;大数据量的时间序列,日期刻度几乎必配。

8.5 数学符号:mathtext

标题、标签里想写数学公式,用 mathtext:夹在两个 $ 之间的内容按 LaTeX 语法渲染。字符串前加 r(raw string)避免反斜杠被转义:

python
ax.set_title(r"阻尼振动 $y = \sin(x)\,e^{-x/10}$", fontsize=13)

图 8-5 真实输出:上标、希腊字母、分式都能渲染:

图 8-5 数学符号 mathtext

mathtext 不依赖 LaTeX,Matplotlib 内置渲染,适合在轴标签里写单位($\mathrm{kg/m^2}$)、下标($T_1$)、常用公式。

8.6 读懂报错:arrowprops 传了字符串

arrowprops 必须是字典。传字符串会:

>>> ax.annotate("x", xy=(0.5, 0.5), arrowprops="->")
Traceback (most recent call last):
  ...
  File "matplotlib/text.py", line 2064, in __init__
    arrowprops = arrowprops.copy()
AttributeError: 'str' object has no attribute 'copy'

解读:Matplotlib 拿到 arrowprops 后调用 .copy()——字符串没有这个方法,于是报 AttributeErrorarrowprops 要写成字典:arrowprops=dict(arrowstyle="->")

动手实践

  1. 画一张图,用 ax.text 在最高点旁标注数值,复现图 8-1。
  2. ax.annotate 给最低点加箭头注释,复现图 8-2。
  3. sin(x),用 MultipleLocator(2) 设置主刻度、0.5 设置次刻度,并配主次网格,复现图 8-3。
  4. 画 14 天的销售额折线图,把横轴刻度格式化成 月-日,复现图 8-4。
  5. 把图 8-5 的标题改成 $y = x^2 + 1$,运行验证 mathtext 渲染。

常见错误

错误写法现象原因
arrowprops="->"AttributeError: 'str' object has no attribute 'copy'arrowprops 必须是字典
text 用默认 ha 导致文字跑出图外文字位置不对ha/vaxytext 偏移
忘记字符串前加 r数学符号报错或异常$ 内反斜杠被转义;用 raw string
which= 忘写次刻度网格不生效主/次刻度要 which="major"/"minor" 分别设置
日期没格式化横轴是串大数字mdates.DateFormatter

章末练习

基础

  1. ax.text(5, 2) 处写一行「峰值」,设置字号与颜色。
  2. ax.annotate 指向 sin 的波峰,加箭头。
  3. MultipleLocator(1) 设置主刻度。

提高

  1. 画一条曲线,用 ax.annotate 同时注释两个极值点,并使用 connectionstyle 让箭头弯曲。
  2. 画 30 天的数据(固定种子),用日期刻度并设置 set_xlim 只显示后 15 天。

挑战

  1. FuncFormatter 把 0~100 万的刻度显示成 50 万100 万(提示:定义 def fmt(v, pos): return f"{v/10000:.0f} 万",再 ax.yaxis.set_major_formatter(FuncFormatter(fmt)))。
  2. 给一张图配一个「带公式、带箭头注释、带主次刻度」的完整示例,并说明每个元素的 transform/which 如何配合。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. 在图内任意位置加文字用?
    • A. ax.title()
    • B. ax.text(x, y, s)
    • C. ax.label()
    • D. ax.write()
  2. ax.text 的坐标默认是?
    • A. 像素坐标
    • B. 数据坐标
    • C. 屏幕坐标
    • D. 相对坐标
  3. 带箭头的注释函数是?
    • A. ax.text
    • B. ax.annotate
    • C. ax.arrow
    • D. ax.comment
  4. arrowprops 的正确写法是?
    • A. arrowprops="->"
    • B. arrowprops=dict(arrowstyle="->")
    • C. arrowprops=["->"]
    • D. arrowprops=("->")
  5. MultipleLocator(2) 表示?
    • A. 刻度显示 2 个
    • B. 每 2 个单位一个刻度
    • C. 坐标轴范围是 2
    • D. 刻度字号为 2
  6. 次刻度用 which= 的哪个值?
    • A. "major"
    • B. "minor"
    • C. "all"
    • D. "sub"
  7. 日期刻度格式化成 07-01 用?
    • A. mdates.DateFormatter("%m-%d")
    • B. mdates.DateFormatter("%Y")
    • C. ax.set_datefmt()
    • D. ax.set_xticklabels() 手工写
  8. mathtext 写数学公式,公式部分用?
    • A. 圆括号
    • B. 两个 $ 夹住
    • C. 方括号
    • D. 反引号
  9. 写含 \ 的字符串应加前缀?
    • A. f
    • B. r
    • C. b
    • D. u
  10. ax.annotatexytext 指?
    • A. 箭头指向的点
    • B. 文字所在的位置
    • C. 坐标轴范围
    • D. 刻度位置