Matplotlib 3.11.0 新功能(2026年6月11日)#

有关自上次修订以来的所有问题和拉取请求列表,请参阅 GitHub 3.11.0 版本统计信息 (2026年6月11日)

图形创建/管理#

图形可以附加到 pyplot 或从其移除#

图形现在可以通过 pyplot 附加和移除,这意味着后台与后端的耦合度降低。

特别地,独立图形(使用 Figure 构造函数创建)现在可以通过调用 plt.figure(fig) 注册到 pyplot 模块中。这允许你像处理通过 plt.figure()plt.subplots() 等 pyplot 工厂函数创建的任何图形一样,使用 plt.show() 来展示它们。

当关闭已显示的图形窗口时,相关图形将重置为独立状态,即 pyplot 不再可见。但如果你仍持有对它的引用,则可以继续操作它(例如执行 fig.savefig(),或者通过 plt.figure(fig) 将其重新添加到 pyplot 并再次显示)。

现在可以实现以下操作——尽管该示例为了展示可能性而稍显夸张。在实际操作中,为了更好的连贯性,建议使用更简单的形式。

import matplotlib.pyplot as plt
from matplotlib.figure import Figure

# Create a standalone figure
fig = Figure()
ax = fig.add_subplot()
ax.plot([1, 2, 3], [4, 5, 6])

# Register it with pyplot
plt.figure(fig)

# Modify the figure through pyplot
plt.xlabel("x label")

# Show the figure
plt.show()

# Close the figure window through the GUI

# Continue to work on the figure
fig.savefig("my_figure.png")
ax.set_ylabel("y label")

# Re-register the figure and show it again
plt.figure(fig)
plt.show()
技术细节

独立图形使用 FigureCanvasBase 作为画布。在注册到 pyplot 时,这会被替换为后端相关的子类;当图形关闭时,它会重置为 FigureCanvasBaseFigure.savefig 使用当前画布保存图形(如果可能)。由于 FigureCanvasBase 无法渲染图形,在保存时它会回退到合适的画布子类,例如用于 PNG 等栅格输出的 FigureCanvasAgg

任何基于 Agg 的后端都会创建相同的文件输出。但是,对于非 Agg 后端可能会有细微差异;例如,如果您使用 "GTK4Cairo" 作为交互式后端,fig.savefig("file.png") 可能会根据图形是否注册到 pyplot 而产生略有不同的图像。

通常,您不应存储对画布的引用,而应始终通过 fig.canvas 从图形中获取它。这会返回当前画布,即原始的 FigureCanvasBase 或后端相关的子类(取决于图形是否注册到 pyplot)。

图形尺寸单位#

创建图形时,现在可以定义以厘米或像素为单位的图形大小。

在此之前,图形大小通过 plt.figure(..., figsize=(6, 4)) 指定,且给定的数字被解释为英寸。现在可以向元组添加单位字符串,例如 plt.figure(..., figsize=(600, 400, "px"))。支持的单位字符串包括 "in", "cm", 或 "px"

创建图形时部分 figsize 规范#

图形创建现在接受 figsize 中的单个 None。传入 (None, h) 使用 rcParams["figure.figsize"](默认:[6.4, 4.8])中的默认宽度;传入 (w, None) 使用默认高度。传入 (None, None) 是无效的,会引发 ValueError

例如:

plt.rcParams['figure.figsize'] = (14, 11)
fig = plt.figure(figsize=(None, 4))  # Size will be (14, 4)

Figure.clear 中重置子图参数#

调用 Figure.clear() 时,gridspec.SubplotParams 的设置将恢复为默认值。

SubplotParams.to_dict 是一个获取子图参数字典的新方法,SubplotParams.reset 可将参数重置为默认值。

绘图方法#

分组柱状图#

新的方法 grouped_bar() 极大地简化了分组柱状图的创建。它支持多种输入数据类型(数据集列表、数据集字典、二维数组中的数据、pandas DataFrame),并允许通过控制柱间距和组间距轻松调整布局。

categories = ['A', 'B']
datasets = {
    'dataset 0': [1, 11],
    'dataset 1': [3, 13],
    'dataset 2': [5, 15],
}

fig, ax = plt.subplots()
ax.grouped_bar(datasets, tick_labels=categories)
ax.legend()

(源代码, 2x.png, png)

Diagram of a grouped bar chart of 3 datasets with 2 categories.

broken_barh() 通过 align 参数实现垂直对齐#

broken_barh 现在支持通过 align 参数进行垂直对齐。

fig, ax = plt.subplots()
ax.axhline(0, color='tab:red')
ax.broken_barh([(0, 10)], (0, 2))  # Default is 'bottom'.
ax.axhline(10, color='tab:red')
ax.broken_barh([(0, 10)], (10, 2), align='center')
ax.axhline(20, color='tab:red')
ax.broken_barh([(0, 10)], (20, 2), align='top')

(源代码, 2x.png, png)

A plot with three horizontal bars at 0, 10, and 20. Each is aligned to the bottom, center, and top of those values, respectively.

hist() 支持为多个数据集使用单一颜色#

现在可以向 hist() 传递单一的 color 值。该值将应用于所有数据集。

堆叠图样式#

stackplot 现在接受样式参数 facecolor, edgecolor, linestyle, 和 linewidth 的序列,类似于 hatch 参数的处理方式。

x = np.linspace(0, 10)
y1 = x + np.sin(x)
y2 = x + np.cos(x)

fig, ax = plt.subplots()
ax.stackplot(x, y1, y2, facecolor=['tab:orange', 'tab:green'])

(源代码, 2x.png, png)

A plot of stacked datasets. The bottom area is orange and the top is green.

流线图集成控制#

streamplot 方法中添加了两个新选项,可以更好地控制流线积分过程:

integration_max_step_scale

将积分器计算的默认最大步长乘以该值。

integration_max_error_scale

将积分器设置的默认最大误差乘以该值。

这些参数的值在 0 到 1 之间会减小(收紧)最大步长或误差,从而通过增加计算量来提高流线精度。大于 1 的值会增加(放宽)最大步长或误差,以牺牲流线精度为代价缩短计算时间。

积分器的默认值均经过手工调优,可能不适用于所有情况,因此这些选项允许针对特定用例自定义行为。仅修改 integration_max_step_scale 已被证明有效,但在某些情况下控制误差也很有帮助。

流线上的多个箭头#

streamplot 中添加了新的 num_arrows 参数,允许在每条流线上添加多个箭头。

w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2

fig, ax = plt.subplots()
ax.streamplot(X, Y, U, V, num_arrows=3)

(源代码, 2x.png, png)

One chart showing a streamplot. Each streamline has three arrows.

violinplot 现在接受颜色参数#

violinplotviolin 现在接受 facecolorlinecolor 作为输入参数。这意味着小提琴图的颜色可以在绘制时设置,而无需在之后单独设置每个对象的颜色。可以传入单一颜色应用于所有小提琴图,也可以传入颜色序列。

np.random.seed(19680801)
data = [sorted(np.random.normal(0, std, 100)) for std in range(1, 5)]

fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, sharey=True)

ax1.set_title('Default violin plot')
ax1.set_ylabel('Observed values')
ax1.violinplot(data)

ax2.set_title('Set colors of violins')
ax2.set_ylabel('Observed values')
ax2.violinplot(
    data,
    facecolor=[('yellow', 0.3), ('blue', 0.3), ('red', 0.3), ('green', 0.3)],
    linecolor='black',
)

(源代码, 2x.png, png)

Two violin plots. On the left, all elements are blue. On the right, each is a custom colour: a desaturated yellow, blue, red, and green for each data set, and black for the vertical bars.

注释#

bar_label 支持为每个标签设置单独的内边距#

bar_label 现在接受浮点值或类数组对象作为内边距。类数组对象定义了每个标签各自的内边距。

为饼图扇区添加标签#

新的 pie_label 方法为使用 pie 创建的饼图中的每个扇区添加标签。它可以使用:

  • 字符串列表,类似于 pie 现有的 labels 参数

  • 格式字符串,类似于 pie 现有的 autopct 参数,不同之处在于它使用 str.format 方法,并且可以处理绝对值以及分数/百分比

更多示例,请参见 饼图标签化

data = [36, 24, 8, 12]
labels = ['spam', 'eggs', 'bacon', 'sausage']

fig, ax = plt.subplots()
pie = ax.pie(data)

ax.pie_label(pie, labels, distance=1.1)
ax.pie_label(pie, '{frac:.1%}', distance=0.7)
ax.pie_label(pie, '{absval:d}', distance=0.4)

(源代码, 2x.png, png)

A pie chart with three labels on each wedge, showing a food type, number, and fraction associated with the wedge.

BoxStyle 的箭头样式子类支持调整箭头头部大小#

新增的 head_widthhead_angle 参数允许调整 BoxStyle.LArrow, BoxStyle.RArrowBoxStyle.DArrow 所用箭头头部的大小和宽高比。

为了在所有参数值下保持一致的外观,默认的头部位置(相对于文本的起始位置)与之前的硬编码位置相比略有更改。

通过使用负角度(或对应的反角度)作为 head_angle,可以创建“反向”头部的箭头。

plt.text(0.2, 0.8, "LArrow", ha='center', size=16,
         bbox=dict(boxstyle="larrow, pad=0.3, head_angle=150"))
plt.text(0.2, 0.2, "LArrow", ha='center', size=16,
         bbox=dict(boxstyle="larrow, pad=0.3, head_width=0.5"))
plt.text(0.5, 0.8, "DArrow", ha='center', size=16,
         bbox=dict(boxstyle="darrow, pad=0.3, head_width=3"))
plt.text(0.5, 0.2, "DArrow", ha='center', size=16,
         bbox=dict(boxstyle="darrow, pad=0.3, head_width=1, head_angle=60"))
plt.text(0.8, 0.8, "RArrow", ha='center', size=16,
         bbox=dict(boxstyle="rarrow, pad=0.3, head_angle=30"))
plt.text(0.8, 0.2, "RArrow", ha='center', size=16,
         bbox=dict(boxstyle="rarrow, pad=0.3, head_width=2, head_angle=-90"))
plt.axis("off")

(源代码, 2x.png, png)

Six arrow-shaped text boxes.  The arrows on the left have the shaft on their left; the arrows on the right have the shaft on the right; the arrows in the middle have shafts on both sides.

borderpad 接受元组以进行分别的 x/y 内边距设置#

用于放置锚定绘图元素(例如嵌入坐标轴)的 borderpad 参数现在接受 (x_pad, y_pad) 的元组。

这允许为水平和垂直方向指定单独的内边距值,从而提供对放置位置的精细控制。例如,将嵌入图放置在角落时,可能希望通过水平内边距来避免与主图的轴标签重叠,同时不设垂直内边距以使嵌入图与绘图区域边缘保持齐平。

使用 inset_axes() 的使用示例

ax_inset = inset_axes(
    ax, width="30%", height="30%", loc='upper left',
    borderpad=(4, 0))

坐标轴和绘图元素#

双轴 (Twin Axes) delta_zorder#

twinxtwiny 现在接受 delta_zorder 关键字参数(添加到原始坐标轴 zorder 的相对偏移量),以控制双轴是在原始坐标轴之前还是之后绘制。例如,传入 delta_zorder=-1 可将双轴绘制在主坐标轴之后。

此外,Matplotlib 现在会自动管理每组双轴的背景补丁可见性,以便该组中最底层的坐标轴具有可见的背景补丁(遵循 frameon)。

BarContainer 属性#

BarContainer 获得了新的属性,可以轻松访问柱状图的坐标:

对数缩放等高线图的最大层级设置现已生效#

使用对数归一化绘制等高线时,传递整数值给 levels 参数以限制最大等高线层级数现在可以如预期工作。

图形补丁的 edgegapcolor#

Patch 现在支持 edgegapcolor 参数,类似于 Line2D 中现有的 gapcolor。这允许具有虚线边缘的补丁在间隙中显示辅助颜色,从而产生“条纹”边缘效果。

这在绘制背景颜色未知的未填充补丁时非常有用,交替的边缘颜色确保了补丁边界保持可见。

from matplotlib.patches import Rectangle

fig, ax = plt.subplots()
rect = Rectangle((0.1, 0.1), 0.6, 0.6, fill=False,
                  edgecolor='orange', edgegapcolor='blue',
                  linestyle='--', linewidth=3)
ax.add_patch(rect)

(源代码, 2x.png, png)

A rectangle with a dashed orange edge and blue gaps, demonstrating the edgegapcolor feature.

hatchcoloredgecolor 分离#

指定 hatchcolor 参数时,它将用于阴影线。如果未指定,它将回退使用 rcParams["hatch.color"](默认:'edge')。特殊值 'edge' 使用补丁边缘颜色,如果补丁边缘颜色为 'none',则回退到 rcParams["patch.edgecolor"](默认:'black')。以前,阴影线颜色与边缘颜色相同,如果补丁没有边缘颜色,则回退到 rcParams["hatch.color"](默认:'edge')。

import matplotlib as mpl
from matplotlib.patches import Rectangle

fig, ax = plt.subplots()

# In this case, hatchcolor is orange
patch1 = Rectangle((0.1, 0.1), 0.3, 0.3, edgecolor='red', linewidth=2,
                   hatch='//', hatchcolor='orange')
ax.add_patch(patch1)

# When hatchcolor is not specified, it matches edgecolor
# In this case, hatchcolor is green
patch2 = Rectangle((0.6, 0.1), 0.3, 0.3, edgecolor='green', linewidth=2,
                   hatch='//', facecolor='none')
ax.add_patch(patch2)

# If both hatchcolor and edgecolor are not specified
# it will default to the 'patch.edgecolor' rcParam, which is black by default
# In this case, hatchcolor is black
patch3 = Rectangle((0.1, 0.6), 0.3, 0.3, hatch='//')
ax.add_patch(patch3)

# When using `hatch.color` in the `rcParams`
# edgecolor will now not overwrite hatchcolor
# In this case, hatchcolor is black
with plt.rc_context({'hatch.color': 'black'}):
    patch4 = Rectangle((0.6, 0.6), 0.3, 0.3, edgecolor='blue', linewidth=2,
                       hatch='//', facecolor='none')

# hatchcolor is black (it uses the `hatch.color` rcParam value)
patch4.set_edgecolor('blue')
# hatchcolor is still black (here, it does not update when edgecolor changes)
ax.add_patch(patch4)

ax.annotate("hatchcolor = 'orange'",
            xy=(.5, 1.03), xycoords=patch1, ha='center', va='bottom')
ax.annotate("hatch color unspecified\nedgecolor='green'",
            xy=(.5, 1.03), xycoords=patch2, ha='center', va='bottom')
ax.annotate("hatch color unspecified\nusing patch.edgecolor",
            xy=(.5, 1.03), xycoords=patch3, ha='center', va='bottom')
ax.annotate("hatch.color='black'",
            xy=(.5, 1.03), xycoords=patch4, ha='center', va='bottom')

(源代码, 2x.png, png)

Four Rectangle patches, each displaying the color of hatches in different specifications of edgecolor and hatchcolor. Top left has hatchcolor='black' representing the default value when both hatchcolor and edgecolor are not set, top right has edgecolor='blue' and hatchcolor='black' which remains when the edgecolor is set again, bottom left has edgecolor='red' and hatchcolor='orange' on explicit specification and bottom right has edgecolor='green' and hatchcolor='green' when the hatchcolor is not set.

对于集合 (collections),可以将颜色序列传递给 hatchcolor 参数,该参数将为每个阴影线循环使用,类似于 facecoloredgecolor

以前,如果未指定 edgecolor,阴影线颜色将回退到 rcParams["patch.edgecolor"](默认:'black'),但 alpha 值默认为 1.0,无论集合的 alpha 值如何。该行为已更改,如果未指定 hatchcoloredgecolor,阴影线颜色将回退到 'patch.edgecolor' 并使用集合的 alpha 值。

np.random.seed(19680801)

fig, ax = plt.subplots()

x = [29, 36, 41, 25, 32, 70, 62, 58, 66, 80, 58, 68, 62, 37, 48]
y = [82, 76, 48, 53, 62, 70, 84, 68, 55, 75, 29, 25, 12, 17, 20]
colors = ['tab:blue'] * 5 + ['tab:orange'] * 5 + ['tab:green'] * 5

ax.scatter(
    x,
    y,
    s=800,
    hatch="xxxx",
    hatchcolor=colors,
    facecolor="none",
    edgecolor="black",
)

(源代码, 2x.png, png)

A random scatter plot with hatches on the markers. The hatches are colored in blue, orange, and green, respectively. After the first three markers, the colors are cycled through again.

坐标轴和刻度#

轴反转状态的标准 Getter/Setter#

坐标轴是否反转现在可以使用 axes.Axes 的 Getter get_xinverted/get_yinverted 查询,并使用 set_xinverted/set_yinverted 设置。

之前存在的方法(Axes.xaxis_inverted, Axes.invert_xaxis)由于其非标准的命名和行为,现在不建议使用(但尚未弃用)。

xtickytick 旋转模式#

新增了一项功能,用于更直观地处理 xtick 和 ytick 标签的旋转。新的 旋转模式 "xtick" 和 "ytick" 会自动调整旋转刻度标签的对齐方式,使文本指向它们的锚点,即刻度。这适用于绘图的所有四个侧面(底、顶、左、右),减少了旋转标签时进行手动调整的需要。

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 3.5), layout='constrained')

pos = range(5)
labels = ['label'] * 5
ax1.set_xticks(pos, labels, rotation=-45, rotation_mode='xtick')
ax1.set_yticks(pos, labels, rotation=45, rotation_mode='ytick')
ax2.xaxis.tick_top()
ax2.set_xticks(pos, labels, rotation=-45, rotation_mode='xtick')
ax2.yaxis.tick_right()
ax2.set_yticks(pos, labels, rotation=45, rotation_mode='ytick')

(源代码, 2x.png, png)

Example of rotated xtick and ytick labels.

改进的对数刻度选择#

改进了对数刻度(以 10 的幂为单位)刻度的选择算法。具体来说,它现在总是尽可能多地绘制刻度(例如,如果可以放置两个刻度,它就不会只绘制一个);如果对刻度进行降采样,在最终刻度数量相同的情况下,它更倾向于将刻度放置在降采样步长的整数倍上(例如,倾向于将刻度放置在 100, 103, 106,而不是 101, 104, 107);并且它现在对浮点计算错误更加稳健。

颜色和颜色映射#

Okabe-Ito 无障碍色板#

Matplotlib 现在包含 Okabe-Ito 色板。对于常见的色觉缺陷,其颜色仍然可区分,且在打印时效果良好。

例如,将其设置为绘图和类似图像绘图元素的默认颜色映射,请使用

import matplotlib.pyplot as plt
from cycler import cycler

plt.rcParams['axes.prop_cycle'] = cycler(color='okabe_ito')
plt.rcParams['image.cmap'] = 'okabe_ito'

或者,在创建图表时,您可以显式传递它

(源代码, 2x.png, png)

A plot of 8 lines, with colors of the Okabe-Ito sequence, from bottom to top: black, orange, sky blue, bluish green, yellow, blue, vermilion, and reddish purple.

六色和八色 Petroff 循环色板#

六色和八色无障碍 Petroff 循环色板分别命名为 'petroff6''petroff8'。它们补充了现有的 'petroff10' 循环色板,该色板在 Matplotlib 3.10.0 中添加。

更多详细信息,请参阅 Petroff, M. A.: "Accessible Color Sequences for Data Visualization"。若要加载 'petroff6' 循环色板以替代默认值

import matplotlib.pyplot as plt
plt.style.use('petroff6')

(源代码, 2x.png, png)

A plot of 6 lines, with colors of the Petroff six-color cycle, from bottom to top: blue, orange, red, dark purple, grey, light purple.

或加载 'petroff8' 循环色板

import matplotlib.pyplot as plt
plt.style.use('petroff8')

(源代码, 2x.png, png)

A plot of 8 lines, with colors of the Petroff eight-color cycle, from bottom to top: blue, orange, red, purple, grey, light blue, blue, dark grey.

设置默认颜色循环为命名颜色序列#

默认颜色循环现在可以在 matplotlibrc 文件或样式文件中配置,以使用任何 命名颜色序列。例如

axes.prop_cycle : cycler(color='Accent')

颜色映射支持在创建时指定“坏值”、“低于”和“高于”的颜色#

颜色映射增加了关键字参数 bad, underover,以在创建时指定这些值。以前,这些值必须在之后使用 set_bad, set_under, set_bad, set_extremes, with_extremes 中的一种来设置。

建议使用新功能,例如

cmap = ListedColormap(colors, bad="red", under="darkblue", over="purple")

代替

cmap = ListedColormap(colors).with_extremes(
    bad="red", under="darkblue", over="purple")

cmap = ListedColormap(colors)
cmap.set_bad("red")
cmap.set_under("darkblue")
cmap.set_over("purple")

调整颜色映射的透明度#

新方法 Colormap.with_alpha 允许创建一个具有相同颜色值但具有新的均匀 alpha 值的新颜色映射。如果您只想修改用于 Artist 的映射颜色的透明度,这很方便。

字体和文本#

重要

此版本的 Matplotlib 对文本处理和渲染进行了重大更新。在发布的 wheel 文件中,捆绑的 FreeType 版本已更新,渲染管道本身也进行了彻底检查,以支持下述现代功能和字体。遗憾的是,无法像以前版本那样为渲染的文本重现完全相同的像素值。

如果您依赖于版本之间像素级的完美一致性,那么在此版本中该一致性将会被破坏。对于测试图表的下游软件包,我们建议采取以下几种方案:

  1. 直接更新测试图像;如果您只要求支持 3.11 及以上版本,这是最简单的选项。但是,这意味着放弃对许多使用旧版 Matplotlib 的用户的支持。或者,提供两套测试图像(更新前和更新后)。这会增加兼容性,但需要占用更多磁盘空间。

  2. 放宽对文本测试的容差。请注意,这可能会掩盖非预期的差异,因此在设置过高的容差时请务必小心。如果您正在使用 Matplotlib 的 image_comparison 装饰器,可以传递 tol 参数

    @image_comparison(['plot.png'], tol=5, style='mpl20')
    def test_plot():
        ...
    

    如果您正在使用 pytest-mpl,则可以传递 tolerance 参数

    @pytest.mark.mpl_image_compare(tolerance=5)
    def test_plot():
        ...
    
  3. 删除非必要的文本元素。避免此问题的最简单方法是不要包含任何需要担心的文本。对于 image_comparison 装饰器和 pytest-mpl,您可以传递 remove_text 参数

    @image_comparison(['plot.png'], remove_text=True, style='mpl20')
    def test_plot():
        ...
    
    @pytest.mark.mpl_image_compare(remove_text=True)
    def test_plot():
        ...
    

    以删除标题和刻度文本;请注意,其他故意添加的文本不会被删除。

  4. 用占位符替换文本。如果您确实需要存在文本,但不需要检查确切的像素(例如,确认布局正确),则可以用固定大小的占位符替换文本。如果您正在使用 pytest,可以使用 text_placeholders 固件来用固定大小的框替换文本。如有必要,考虑在您自己的测试中嵌入类似的固件。

  5. 使用不同的图像比较算法。虽然 Matplotlib 的测试框架中不可用,但如果您希望避免依赖确切的像素值,使用 感知哈希算法 (perceptual hashing algorithm) 可能更合适。

使用 libraqm 进行复杂的文本布局#

文本支持已扩展为包括复杂的文本布局。此支持包括

  1. 需要高级布局的语言,如阿拉伯语或希伯来语。

  2. 混合使用从左到右和从右到左语言的文本。

    (2x.png, png)

    The mixed-language text 'Here is some رَقْم in اَلْعَرَبِيَّةُ'.
  3. 结合多个相邻字符以提高可读性的连字 (Ligatures)。

    (2x.png, png)

    A rightwards arrow pointing from the individual letters 'f', 'f', and 'i', to the 'ffi' ligature.
  4. 结合多个或双宽度的变音符号。

    (2x.png, png)

    An "equation" showing the letter 'a' plus a circumflex accent plus a tilde plus the letter 'c' plus a diaeresis, producing a single glyph of all of them.

注意,所有高级功能都需要相应的字体支持,并且可能需要除内置 DejaVu Sans 之外的其他字体。

指定字体特性标签#

OpenType 字体可能支持特性标签,指定可选的替代字形形状或替换。文本 API 现在支持设置一组要与关联字体一起使用的特性标签。特性标签可以通过以下方式获取/设置:

字体特性字符串最终会被传递给 HarfBuzz,因此支持所有 hb_feature_from_string() 支持的字符串格式。但请注意,子范围 (subranges) 不被明确支持,未来行为可能会发生变化。

例如,默认字体 DejaVu Sans 默认启用了标准连字('liga' 标签),并提供了可选的自由连字(dlig 标签)。这些可以使用 +- 进行切换。

fig = plt.figure(figsize=(7, 3))

fig.text(0.5, 0.85, 'Ligatures', fontsize=40, horizontalalignment='center')

# Default has Standard Ligatures (liga).
fig.text(0, 0.6, 'Default: fi ffi fl st', fontsize=40)

# Disable Standard Ligatures with -liga.
fig.text(0, 0.35, 'Disabled: fi ffi fl st', fontsize=40,
         fontfeatures=['-liga'])

# Enable Discretionary Ligatures with dlig.
fig.text(0, 0.1, 'Discretionary: fi ffi fl st', fontsize=40,
         fontfeatures=['dlig'])

(源代码, 2x.png, png)

An example of ligatures affecting text, in four lines. The first line is the title "Ligatures". Each subsequent line shows the style, followed by the examples "fi", "ffi", "fl", and "st". The second line is the default, where all but "st" use ligatures. The third line has disabled ligatures and all examples are drawn as individual glyphs. The fourth line has enabled discretionary ligatures and all examples, including the "st", use ligatures.

可用的字体特性标签可在 https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist 找到。

指定文本语言#

OpenType 字体可能支持语言系统,用于选择不同的排版惯例,例如共享单个 Unicode 代码点的字母的本地化变体,或不同的默认字体特性。文本 API 现在支持设置要使用的语言,可通过以下方式获取/设置:

文本的语言必须是 libraqm 接受的格式,即 BCP47 语言代码。如果为 None 或未设置,则不暗示任何特定语言,将使用默认字体设置。

例如,Matplotlib 的默认字体 DejaVu Sans 支持西里尔字母中的塞尔维亚语和马其顿语(与俄语对比),或拉丁字母中的萨米语系(与英语对比)的特定语言字形。

fig = plt.figure(figsize=(7, 3))

char = '\U00000431'
fig.text(0.5, 0.8, f'\\U{ord(char):08x}', fontsize=40, horizontalalignment='center')
fig.text(0, 0.6, f'Serbian: {char}', fontsize=40, language='sr')
fig.text(1, 0.6, f'Russian: {char}', fontsize=40, language='ru',
         horizontalalignment='right')

char = '\U0000014a'
fig.text(0.5, 0.3, f'\\U{ord(char):08x}', fontsize=40, horizontalalignment='center')
fig.text(0, 0.1, f'Inari Sámi: {char}', fontsize=40, language='smn')
fig.text(1, 0.1, f'English: {char}', fontsize=40, language='en',
         horizontalalignment='right')

(源代码, 2x.png, png)

An example of how text language affects rendering, in four lines. The first line lists the Unicode code point '\U00000431`, which is the Cyrillic small letter BE. The second line then shows the rendering when the language is set to Serbian vs set to Russian. The third line lists the Unicode code point '\U0000014a`, which is the Latin capital letter ENG. The fourth line then shows the rendering when the language is set to Inari Sámi vs set to English.

缺失字形使用 Last Resort 字体#

大多数字体没有 100% 的字符覆盖率,对于未提供的字符将回退到“未找到”字形。通常,此字形会非常简单(例如,默认的 DejaVu Sans“未找到”字形只是一个矩形)。这种最小化的字形无法提供关于缺失字符的任何上下文。

现在,缺失字形将回退到 Unicode 联盟制作的 Last Resort 字体。这种特殊用途的字体提供表示 Unicode 字符类型的字形。这些字形显示了缺失 Unicode 块中的代表字符,在较大尺寸下,还可以提供更多上下文,以帮助确定需要哪个字符和字体。

要禁用此回退行为,请将 rcParams["font.enable_last_resort"](默认:True)设置为 False

(源代码, 2x.png, png)

An example of missing glyph behaviour, the first glyph from Bengali script, second glyph from Hiragana, and the last glyph from the Unicode Private Use Area. Multiple lines repeat the text with increasing font size from top to bottom.

可通过所有 SFNT 字体族名称访问字体#

字体现在可以通过它们在 OpenType 名称表中声明的任何族名称进行选择,而不仅仅是 FreeType 报告的主字体族名称。

某些字体在不同平台上或名称表的不同条目中存储不同的族名称。例如,Ubuntu Light 在 Macintosh 平台 Name ID 1 插槽(FreeType 用作主名称)中存储 "Ubuntu",而在 Microsoft 平台 Name ID 1 插槽中存储 "Ubuntu Light"。以前仅注册了基于 FreeType 的名称,需要一种晦涩的基于权重的变通方法。

# Previously required
matplotlib.rcParams['font.family'] = 'Ubuntu'
matplotlib.rcParams['font.weight'] = 300

所有描述族的名称表条目(两个平台上的 Name ID 1、排版族名称 Name ID 16 和 WWS 族名称 Name ID 21)现在都作为单独条目注册在 FontManager 中,因此可以直接使用其中任何一个名称。

matplotlib.rcParams['font.family'] = 'Ubuntu Light'

支持加载 TrueType 集合字体#

TrueType 集合字体(通常以 .ttc 为扩展名)现在受支持。即,Matplotlib 将在扫描系统字体时包含这些文件扩展名,并将所有子字体添加到可用字体列表中(即 get_font_names 的列表)。

从大多数高级 API 来看,这意味着您应该能够像指定任何其他字体一样指定集合中任何子字体的名称。请注意,目前无法以任何自动化选择内部子字体的形式指定整个集合。

在底层 API 中,为了确保向后兼容性同时促进这一新支持,可以传递一个 FontPath 实例(由字体路径和子字体索引组成,行为类似于 str)到字体管理 API 中,以代替简单的 os.PathLike 路径。任何之前返回字符串路径的字体管理 API 现在改为返回 FontPath 实例。

忽略系统字体的新环境变量#

可以通过设置 MPL_IGNORE_SYSTEM_FONTS 来忽略系统字体;这会禁止搜索系统字体(在已知目录中或通过某些特定于平台的子进程),并限制 FontManager.findfont 的结果。

Mathtext 区分 斜体 (italic)正常 (normal) 字体#

Matplotlib 的轻量级 TeX 表达式解析器 (usetex=False) 现在可以区分 斜体正常 数学字体,以更接近 LaTeX 的行为。默认情况下,数学环境会选择正常数学字体(除非 rcParam mathtext.default 被覆盖),也可以通过新的 \mathnormal 命令显式设置。斜体则通过 \mathit 选择。主要区别在于:斜体 产生斜体数字,而 正常 产生正体数字。此前,无法排版斜体数字。请注意,normal 现在对应于以前的 it,而 it 现在会将所有字符渲染为斜体。重要提示:如果通过在 matplotlibrc 中设置 mathtext.default: it 覆盖了默认数学字体,则必须将其注释掉或更改为 mathtext.default: normal 以保持其行为。否则,所有字母数字字符(包括数字)都将呈现为斜体。

与传统 LaTeX 的一个区别在于,LaTeX 进一步区分了 正常 (\mathnormal) 和 默认数学,其中默认值使用罗马数字,而正常模式使用老式数字 (oldstyle digits)。现代 LaTeX 引擎、unicode-math 以及 Matplotlib 中已不再有这种区分。

Mathtext 对 \phantom, \llap, \rlap 的支持#

Mathtext 增加了对 TeX 宏 \phantom, \llap\rlap 的支持。\phantom 允许在画布上占用一定的空间,就像渲染了某些文本一样,但实际上并不渲染该文本;而 \llap\rlap 允许在画布上渲染文本,同时假装它不占用任何空间。这些宏总体上可以实现更精细的文本对齐控制。

关于这些宏的详细描述,请参阅 https://www.tug.org/TUGboat/tb22-4/tb72perlS.pdf

例如,在下方的第一个图例中使用这些宏,可以预留空间,使其尺寸与包含较长文本的第二个图例相同。

fig = plt.figure(layout="constrained")
sfs = fig.subfigures(2)

ax0 = sfs[0].add_subplot()
ax0.plot([1, 2], label=r"$\rlap{\text{foo}}\phantom{\text{a longer label}}$")
sfs[0].legend(loc="outside right upper")

ax1 = sfs[1].add_subplot()
ax1.plot([1, 2], label="foo")
ax1.plot([2, 1], label="a longer label")
sfs[1].legend(loc="outside right upper")

(源代码, 2x.png, png)

Two plots of diagonal lines. The first Axes has a legend with a single entry labelled "foo". The second Axes has a legend with two entries labelled "foo" and "a longer label". Both legends are the same width despite the former containing a shorter label.

使用 Mathtext 时为文本添加下划线#

Mathtext 现在支持 \underline 命令。

plt.figure(figsize=(6, 2))
plt.text(0.05, 0.7, r'This is $\underline{underlined}$ text.', fontsize=24)
plt.text(0.05, 0.2, r'So is $\underline{\mathrm{this}}$.', fontsize=24)
plt.axis('off')

(源代码, 2x.png, png)

Two lines of text. The first says "This is underlined text." and the word "underlined" is italic and has a line under it. The second line says "So us this." and the word "this" has a line under it.

改进 PDF 中的字体嵌入#

Type 3 和 Type 42 字体(详见 Matplotlib 中的字体)现在已无限制地嵌入 PDF 中。为了满足格式限制,字体可能会被拆分为多个嵌入子集。此外,每个字体都添加了修正后的 Unicode 映射。

这意味着在支持该功能的 PDF 查看器中,所有文本现在都应该可以被选中和复制。

当使用 usetex 功能时,Matplotlib 会调用 TeX 来渲染图中的文本和公式。使用的字体通常是 "Type 1" 字体。它们过去是完整嵌入的,现在则限制为仅嵌入图中实际使用的字形。这减少了生成的 PDF 文件的大小。

rcParams 改进#

rcParams 中用于主/次网格线的独立样式选项#

使用 rcParams["grid.major.*"]rcParams["grid.minor.*"] 将分别覆盖 rcParams["grid.*"] 中对应主网格线和次网格线的设置。

import matplotlib as mpl

# Set visibility for major and minor gridlines
mpl.rcParams["axes.grid"] = True
mpl.rcParams["ytick.minor.visible"] = True
mpl.rcParams["xtick.minor.visible"] = True
mpl.rcParams["axes.grid.which"] = "both"

# Using grid.* to set both major and minor properties
mpl.rcParams["grid.color"] = "lightgrey"

# Overwrite some values for major and minor separately
mpl.rcParams["grid.major.linewidth"] = 1.2
mpl.rcParams["grid.minor.color"] = "tab:blue"
mpl.rcParams["grid.minor.linestyle"] = ":"

plt.plot([0, 1], [0, 1])

(源代码, 2x.png, png)

Modifying the gridlines using the new options `rcParams`

axes.prop_cycle rcParam 安全性改进#

axes.prop_cycle rcParam 的解析方式变得更安全、更受限。现在仅允许使用字面量、cycler()concat() 调用、运算符 +*,以及切片操作。所有之前记录在 https://matplotlib.org.cn/cycler/ 的有效 cycler 字符串仍然受支持,例如

axes.prop_cycle : cycler('color', ['r', 'g', 'b']) + cycler('linewidth', [1, 2, 3])
axes.prop_cycle : 2 * cycler('color', 'rgb')
axes.prop_cycle : concat(cycler('color', 'rgb'), cycler('color', 'cmk'))
axes.prop_cycle : cycler('color', 'rgbcmk')[:3]

图例 (Legends)#

legend.linewidth rcParam 和参数#

新增了一个 rcParam legend.linewidth,用于控制图例边框的线条宽度。当设置为 None(默认值)时,它会继承 patch.linewidth 的值。这允许在不影响其他元素的情况下独立控制图例边框的线条宽度。

Legend 构造函数现在也接受一个名为 linewidth 的新参数,用于直接设置图例边框的线条宽度,该参数会覆盖 rcParam 的值。

fig, ax = plt.subplots()
ax.plot([1, 2, 3], label='data')
ax.legend(linewidth=2.0)  # Thick legend box edge

(源代码, 2x.png, png)

A line plot with a legend showing a thick border around the legend box.

现在支持 PatchCollection 图例#

PatchCollection 实例在指定标签时,现在可以正确显示在图例中。此前,PatchCollection 对象上的标签会被图例系统忽略,要求用户手动创建图例条目。

import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection

fig, ax = plt.subplots()
patches = [mpatches.Circle((0, 0.5), 0.1), mpatches.Rectangle((0.5, 0), 0.2, 0.3)]
pc = PatchCollection(patches, facecolor='blue', edgecolor='black', label='My patches')
ax.add_collection(pc)
ax.legend()  # Now displays the label "My patches"

(源代码, 2x.png, png)

The legend entry displays a rectangle matching the visual properties (colors, line styles, line widths) of the first patch in the collection.

小部件与交互性 (Widgets and Interactivity)#

使用鼠标滚轮缩放#

Control+鼠标滚轮 可用于在绘图窗口中进行缩放。此外,x+鼠标滚轮 仅缩放 x 轴,而 y+鼠标滚轮 仅缩放 y 轴。

缩放操作以鼠标指针为中心。按下 Control 时,保持轴的纵横比;按下 xy 时,仅缩放相应的轴。

目前缩放功能仅在直角坐标轴 (rectilinear Axes) 上受支持。

一致的缩放框#

缩放功能现在在所有后端中都具有一致的虚线框样式。

RadioButtons 和 CheckButtons 小部件支持灵活的布局#

widgets.RadioButtonswidgets.CheckButtons 小部件现在通过新的 layout 参数支持不同的按钮排列方式。您可以通过传递 (rows, cols) 元组将按钮垂直(默认)、水平排列或放置在二维网格中。

有关 (rows, cols) 的示例,请参阅 单选按钮网格 (Radio Buttons Grid)

from matplotlib.widgets import CheckButtons

t = np.arange(0.0, 2.0, 0.01)
s0 = np.sin(2*np.pi*t)
s1 = np.sin(4*np.pi*t)
s2 = np.sin(6*np.pi*t)
s3 = np.sin(8*np.pi*t)

fig, axes = plt.subplot_mosaic(
    [['main'], ['buttons']],
    height_ratios=[8, 1],
    layout="constrained",
)

l0, = axes['main'].plot(t, s0, lw=2, label='2 Hz')
l1, = axes['main'].plot(t, s1, lw=2, label='4 Hz')
l2, = axes['main'].plot(t, s2, lw=2, label='6 Hz')
l3, = axes['main'].plot(t, s3, lw=2, label='8 Hz')
axes['main'].set_xlabel('Time (s)')
axes['main'].set_ylabel('Amplitude')

lines_by_label = {l.get_label(): l for l in [l0, l1, l2, l3]}

axes['buttons'].set_facecolor('0.95')
check = CheckButtons(
    axes['buttons'],
    labels=lines_by_label.keys(),
    actives=[l.get_visible() for l in lines_by_label.values()],
    layout='horizontal'
)

def callback(label):
    ln = lines_by_label[label]
    ln.set_visible(not ln.get_visible())
    fig.canvas.draw_idle()

check.on_clicked(callback)

(源代码, 2x.png, png)

Multiple sine waves with checkboxes to toggle their visibility.

SliderRangeSlider 的可调用 valfmt#

除了现有的 %-格式化字符串外,SliderRangeSlidervalfmt 参数现在也接受形式为 valfmt(val: float) -> str 的可调用对象。

WebAgg 滚动捕获控制#

WebAgg 后端现在提供了捕获滚动事件的能力,以防止在与绘图交互时触发页面滚动。这可以通过新的 FigureCanvasWebAggCore.set_capture_scrollFigureCanvasWebAggCore.get_capture_scroll 方法来启用或禁用。

3D 绘图改进#

3D 坐标轴上的非线性标尺 (Non-linear scales)#

解决了长期存在的问题,3D 轴现在像 2D 轴一样,支持诸如 'log''symlog''logit''asinh' 以及自定义 'function' 标尺等非线性轴标尺。请使用 set_xscaleset_yscaleset_zscale 来分别设置每个轴的标尺。

# A sine chirp with increasing frequency and amplitude
x = np.linspace(0, 1, 400)  # time
y = 10 ** (2 * x)  # frequency, growing exponentially from 1 to 100 Hz
phase = 2 * np.pi * (10 ** (2 * x) - 1) / (2 * np.log(10))
z = np.sin(phase) * x ** 2 * 10  # amplitude, growing quadratically

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot(x, y, z)

ax.set_xlabel('Time (linear)')
ax.set_ylabel('Frequency, Hz (log)')
ax.set_zlabel('Amplitude (symlog)')

ax.set_yscale('log')
ax.set_zscale('symlog')

(源代码, 2x.png, png)

A 3D plot with a linear x-axis, logarithmic y-axis, and symlog z-axis.

有关所有可用标尺及其参数的详细信息,请参阅 matplotlib.scale

使用 Control 键对齐 3D 旋转角度#

现在,在鼠标旋转 3D 轴时按住 Control 键,支持将旋转角度对齐到固定的角度增量。

对齐步长由新的 rcParams["axes3d.snap_rotation"](默认:5.0)rcParam 控制。将其设置为 0 可禁用对齐。

例如:

mpl.rcParams["axes3d.snap_rotation"] = 10

在用鼠标旋转时,会将仰角、方位角和滚动角对齐到 10 度的倍数。

3D 深度着色修复#

此前,一种评估 3D 项目视觉“深度”的方法存在微小缺陷,可能导致图表方向改变时透明度发生突然且意外的变化。

现在,这种行为变得平滑且可预测。此外,新增了 depthshade_minalpha 参数,允许用户设置最小透明度水平。深度着色是 Patch3DCollectionPath3DCollection(包括 3D 散点图)的一个选项。

depthshadedepthshade_minalpha 的默认值现在分别由 rcParams["axes3d.depthshade"](默认:True)和 rcParams["axes3d.depthshade_minalpha"](默认:0.3)控制。

一个简单示例

fig = plt.figure()
ax = fig.add_subplot(projection="3d")

X = [i for i in range(10)]
Y = [i for i in range(10)]
Z = [i for i in range(10)]
S = [(i + 1) * 400 for i in range(10)]

ax.scatter(
    xs=X, ys=Y, zs=Z, s=S,
    depthshade=True,
    depthshade_minalpha=0.3,
)
ax.view_init(elev=10, azim=-150, roll=0)

(源代码, 2x.png, png)

A 3D scatter plot with depth-shading enabled.

3D 性能改进#

3D 绘图的绘制时间得到了改进,特别是对于曲面和线框图。在某些情况下,用户将看到高达 10 倍的速度提升。这将使与 3D 图表的交互响应更加灵敏。

其他改进#

保存为 GIF 格式功能已恢复#

根据图表文档,savefig 方法支持文件扩展名为 .gif 的 GIF 格式。然而,GIF 支持自 Matplotlib 2.0.0 以来一直处于损坏状态,现已修复。

CallbackRegistry.disconnect 允许直接通过函数断开回调#

CallbackRegistry 现在允许在 disconnect 中直接传入函数(以及可选的信号),而无需跟踪由 connect 返回的回调 ID。

from matplotlib.cbook import CallbackRegistry

def my_callback(event):
    print(event)

callbacks = CallbackRegistry()
callbacks.connect('my_signal', my_callback)

# Disconnect by function reference instead of callback ID
callbacks.disconnect('my_signal', my_callback)

violin_stats 简化的 method 参数#

violin_statsmethod 参数现在可以指定为字符串元组,并有了新的默认值 ("GaussianKDE", "scott")。因此,调用 violin_stats 后跟 violin,现在等同于调用 violinplot

from matplotlib.cbook import violin_stats

rng = np.random.default_rng(19680801)
data = rng.normal(size=(10, 3))

fig, (ax1, ax2) = plt.subplots(ncols=2, layout='constrained', figsize=(6.4, 3.5))

# Create the violin plot in one step
ax1.violinplot(data)
ax1.set_title('One Step')

# Process the data and then create the violin plot
vstats = violin_stats(data)
ax2.violin(vstats)
ax2.set_title('Two Steps')

(源代码, 2x.png, png)

Example showing violin_stats followed by violin gives the same result as violinplot.