Matplotlib 中的文本#

Matplotlib 中的绘图和文本操作简介。

Matplotlib 具有广泛的文本支持,包括对数学表达式的支持、对光栅和矢量输出的 truetype 支持、带有任意旋转的换行分隔文本,以及 Unicode 支持。

因为它直接将字体嵌入到输出文档中,例如用于PostScript或PDF,屏幕上看到的就是打印出来的。FreeType 支持生成非常漂亮的、抗锯齿的字体,即使在小的光栅尺寸下看起来也很好。Matplotlib 包含自己的 :mod:`matplotlib.font_manager`(感谢 Paul Barrett),它实现了一个跨平台的、符合 `W3C <https://www.w3.org/>`_ 的字体查找算法。

用户对文本属性(字体大小、字体粗细、文本位置和颜色等)有极大的控制权,这些属性在 rc 文件 中设置了合理的默认值。更重要的是,对于那些对数学或科学图形感兴趣的人来说,Matplotlib 实现了大量的 TeX 数学符号和命令,支持在图形的任何位置使用 数学表达式

基本文本命令#

以下命令用于在隐式和显式接口中创建文本(有关权衡的解释,请参见 Matplotlib 应用程序接口 (APIs)):

隐式 API

显式 API

描述

text

text

Axes 的任意位置添加文本。

annotate

annotate

Axes 的任意位置添加一个注释,可选择是否带有箭头。

xlabel

set_xlabel

Axes 的 x 轴添加标签。

ylabel

set_ylabel

Axes 的 y 轴添加标签。

title

set_title

Axes 添加一个标题。

figtext

text

Figure 的任意位置添加文本。

suptitle

suptitle

Figure 添加标题。

所有这些函数都会创建并返回一个 Text 实例,该实例可以通过各种字体和其他属性进行配置。下面的示例展示了这些命令的实际应用,后续章节中提供了更多详细信息。

import matplotlib.pyplot as plt

import matplotlib

fig = plt.figure()
ax = fig.add_subplot()
fig.subplots_adjust(top=0.85)

# Set titles for the figure and the subplot respectively
fig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')
ax.set_title('axes title')

ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')

# Set both x- and y-axis limits to [0, 10] instead of default [0, 1]
ax.axis([0, 10, 0, 10])

ax.text(3, 8, 'boxed italics text in data coords', style='italic',
        bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})

ax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)

ax.text(3, 2, 'Unicode: Institut für Festkörperphysik')

ax.text(0.95, 0.01, 'colored text in axes coords',
        verticalalignment='bottom', horizontalalignment='right',
        transform=ax.transAxes,
        color='green', fontsize=15)

ax.plot([2], [1], 'o')
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),
            arrowprops=dict(facecolor='black', shrink=0.05))

plt.show()
bold figure suptitle, axes title

x轴和y轴的标签#

通过 set_xlabelset_ylabel 方法,指定 x 轴和 y 轴的标签非常简单。

import matplotlib.pyplot as plt
import numpy as np

x1 = np.linspace(0.0, 5.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.15, left=0.2)
ax.plot(x1, y1)
ax.set_xlabel('Time [s]')
ax.set_ylabel('Damped oscillation [V]')

plt.show()
text intro

x 轴和 y 轴的标签会自动放置,以避免与 x 轴和 y 轴的刻度标签重叠。比较下面的图与上面的图,注意 y 轴标签位于上面图的左侧。

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.15, left=0.2)
ax.plot(x1, y1*10000)
ax.set_xlabel('Time [s]')
ax.set_ylabel('Damped oscillation [V]')

plt.show()
text intro

如果你想移动标签,你可以指定 labelpad 关键字参数,其中值为点(1/72英寸,与指定字体大小的单位相同)。

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.15, left=0.2)
ax.plot(x1, y1*10000)
ax.set_xlabel('Time [s]')
ax.set_ylabel('Damped oscillation [V]', labelpad=18)

plt.show()
text intro

或者,标签接受所有 Text 关键字参数,包括 position,通过它可以手动指定标签位置。这里我们将 xlabel 放在轴的最左边。注意,该位置的 y 坐标没有影响 - 要调整 y 位置,我们需要使用 labelpad 关键字参数。

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.15, left=0.2)
ax.plot(x1, y1)
ax.set_xlabel('Time [s]', position=(0., 1e6), horizontalalignment='left')
ax.set_ylabel('Damped oscillation [V]')

plt.show()
text intro

本教程中的所有标签都可以通过操作 matplotlib.font_manager.FontProperties 方法,或通过 set_xlabel 的命名关键字参数来更改。

from matplotlib.font_manager import FontProperties

font = FontProperties()
font.set_family('serif')
font.set_name('Times New Roman')
font.set_style('italic')

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.15, left=0.2)
ax.plot(x1, y1)
ax.set_xlabel('Time [s]', fontsize='large', fontweight='bold')
ax.set_ylabel('Damped oscillation [V]', fontproperties=font)

plt.show()
text intro

最后,我们可以在所有文本对象中使用原生 TeX 渲染,并支持多行:

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(bottom=0.2, left=0.2)
ax.plot(x1, np.cumsum(y1**2))
ax.set_xlabel('Time [s] \n This was a long experiment')
ax.set_ylabel(r'$\int\ Y^2\ dt\ \ [V^2 s]$')
plt.show()
text intro

标题#

子图标题的设置方式与标签非常相似,但有一个 loc 关键字参数可以改变位置和对齐方式,默认值为 loc=center

fig, axs = plt.subplots(3, 1, figsize=(5, 6), tight_layout=True)
locs = ['center', 'left', 'right']
for ax, loc in zip(axs, locs):
    ax.plot(x1, y1)
    ax.set_title('Title with loc at '+loc, loc=loc)
plt.show()
Title with loc at center, Title with loc at left, Title with loc at right

标题的垂直间距通过 rcParams["axes.titlepad"] (default: 6.0) 控制。设置为不同的值会移动标题。

fig, ax = plt.subplots(figsize=(5, 3))
fig.subplots_adjust(top=0.8)
ax.plot(x1, y1)
ax.set_title('Vertically offset title', pad=30)
plt.show()
Vertically offset title

刻度和刻度标签#

放置刻度和刻度标签是制作图表中非常棘手的部分。Matplotlib 会尽力自动完成这项任务,但它也提供了一个非常灵活的框架来确定刻度位置的选择,以及它们如何被标记。

术语#

matplotlib.axis.Axis 对象用于 ax.xaxisax.yaxis,这些对象包含了关于轴上标签如何布局的信息。

轴API在文档中 axis 有详细解释。

一个轴对象有主刻度和次刻度。轴有 Axis.set_major_locatorAxis.set_minor_locator 方法,这些方法使用正在绘制的数据来确定主刻度和次刻度的位置。还有 Axis.set_major_formatterAxis.set_minor_formatter 方法用于格式化刻度标签。

简单标记#

通常只需定义刻度值,有时甚至是刻度标签,覆盖默认的定位器和格式化器,这样做很方便。但不建议这样做,因为它会破坏图形的交互式导航。它还可能重置轴的限制:注意第二个图形的刻度是我们要求的,包括那些远超出自动视图限制的刻度。

fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
axs[1].xaxis.set_ticks(np.arange(0., 8.1, 2.))
plt.show()
text intro

我们当然可以在事后修复这个问题,但这确实突显了硬编码刻度的弱点。这个例子还改变了刻度的格式:

fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
ticks = np.arange(0., 8.1, 2.)
# list comprehension to get all tick labels...
tickla = [f'{tick:1.2f}' for tick in ticks]
axs[1].xaxis.set_ticks(ticks)
axs[1].xaxis.set_ticklabels(tickla)
axs[1].set_xlim(axs[0].get_xlim())
plt.show()
text intro

刻度定位器和格式化器#

与其列出所有的刻度标签,我们本可以使用 matplotlib.ticker.StrMethodFormatter`(新风格的 ``str.format()` 格式字符串)或 matplotlib.ticker.FormatStrFormatter`(旧风格的 '%' 格式字符串),并将其传递给 ``ax.xaxis`。通过传递一个 str,也可以创建一个 matplotlib.ticker.StrMethodFormatter,而无需显式创建格式化器。

fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
ticks = np.arange(0., 8.1, 2.)
axs[1].xaxis.set_ticks(ticks)
axs[1].xaxis.set_major_formatter('{x:1.1f}')
axs[1].set_xlim(axs[0].get_xlim())
plt.show()
text intro

当然,我们也可以使用非默认的定位器来设置刻度位置。注意,我们仍然传入刻度值,但上面使用的x轴限制修复是不需要的。

fig, axs = plt.subplots(2, 1, figsize=(5, 3), tight_layout=True)
axs[0].plot(x1, y1)
axs[1].plot(x1, y1)
locator = matplotlib.ticker.FixedLocator(ticks)
axs[1].xaxis.set_major_locator(locator)
axs[1].xaxis.set_major_formatter({x}°')
plt.show()
text intro

默认的格式化器是 matplotlib.ticker.MaxNLocator,调用方式为 ticker.MaxNLocator(self, nbins='auto', steps=[1, 2, 2.5, 5, 10])steps 关键字包含一个可以用于刻度值的倍数列表。例如,在这种情况下,2, 4, 6 是可接受的刻度,同样 20, 40, 60 或 0.2, 0.4, 0.6 也是可接受的。然而,3, 6, 9 是不可接受的,因为 3 不在步骤列表中。

nbins=auto 使用一种算法来根据轴的长度确定有多少个刻度是可接受的。刻度标签的字体大小被考虑在内,但刻度字符串的长度没有被考虑(因为尚未确定。)在底部一行中,刻度标签非常大,因此我们将 nbins=4 设置为使标签适合右图。

fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):
    ax.plot(x1*10., y1)

formatter = matplotlib.ticker.FormatStrFormatter('%1.1f')
locator = matplotlib.ticker.MaxNLocator(nbins='auto', steps=[1, 4, 10])
axs[0, 1].xaxis.set_major_locator(locator)
axs[0, 1].xaxis.set_major_formatter(formatter)

formatter = matplotlib.ticker.FormatStrFormatter('%1.5f')
locator = matplotlib.ticker.AutoLocator()
axs[1, 0].xaxis.set_major_formatter(formatter)
axs[1, 0].xaxis.set_major_locator(locator)

formatter = matplotlib.ticker.FormatStrFormatter('%1.5f')
locator = matplotlib.ticker.MaxNLocator(nbins=4)
axs[1, 1].xaxis.set_major_formatter(formatter)
axs[1, 1].xaxis.set_major_locator(locator)

plt.show()
text intro

最后,我们可以使用 matplotlib.ticker.FuncFormatter 为格式化器指定函数。此外,类似于 matplotlib.ticker.StrMethodFormatter,传递一个函数将自动创建一个 matplotlib.ticker.FuncFormatter

def formatoddticks(x, pos):
    """Format odd tick positions."""
    if x % 2:
        return f'{x:1.2f}'
    else:
        return ''


fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.plot(x1, y1)
locator = matplotlib.ticker.MaxNLocator(nbins=6)
ax.xaxis.set_major_formatter(formatoddticks)
ax.xaxis.set_major_locator(locator)

plt.show()
text intro

Dateticks#

Matplotlib 可以接受 datetime.datetimenumpy.datetime64 对象作为绘图参数。日期和时间需要特殊的格式化,这通常可以从手动干预中受益。为了帮助实现这一点,日期有特殊的定位器和格式化器,定义在 matplotlib.dates 模块中。

一个简单的例子如下所示。注意我们如何旋转刻度标签,以避免它们相互重叠。

import datetime

fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
base = datetime.datetime(2017, 1, 1, 0, 0, 1)
time = [base + datetime.timedelta(days=x) for x in range(len(x1))]

ax.plot(time, y1)
ax.tick_params(axis='x', rotation=70)
plt.show()
text intro

我们可以向 matplotlib.dates.DateFormatter 传递一个格式。另请注意,29日和下个月非常接近。我们可以通过使用 dates.DayLocator 类来解决这个问题,该类允许我们指定一个月中要使用的日期列表。类似的格式化器列在 matplotlib.dates 模块中。

import matplotlib.dates as mdates

locator = mdates.DayLocator(bymonthday=[1, 15])
formatter = mdates.DateFormatter('%b %d')

fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
ax.plot(time, y1)
ax.tick_params(axis='x', rotation=70)
plt.show()
text intro

图例和注释#

  • 图例:图例指南

  • 注解: 注解

脚本总运行时间: (0 分钟 1.811 秒)

由 Sphinx-Gallery 生成的图库