常见问题解答#

我没有看到图形窗口#

请参阅 调试图形窗口不显示的问题

为什么我有这么多刻度,和/或为什么它们是乱序的?#

刻度表现异常的一个常见原因是传入了字符串列表而非数字或日期时间对象。在读取逗号分隔的文本文件时,这种情况很容易无意中发生。Matplotlib 将字符串列表视为分类变量(绘制分类变量),默认情况下每个类别对应一个刻度,并按照提供的顺序进行绘制。

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(1, 2, layout='constrained', figsize=(6, 2))

ax[0].set_title('Ticks seem out of order / misplaced')
x = ['5', '20', '1', '9']  # strings
y = [5, 20, 1, 9]
ax[0].plot(x, y, 'd')
ax[0].tick_params(axis='x', labelcolor='red', labelsize=14)

ax[1].set_title('Many ticks')
x = [str(xx) for xx in np.arange(100)]  # strings
y = np.arange(100)
ax[1].plot(x, y)
ax[1].tick_params(axis='x', labelcolor='red', labelsize=14)

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

解决方法是将字符串列表转换为数字或日期时间对象(通常使用 np.asarray(numeric_strings, dtype='float')np.asarray(datetime_strings, dtype='datetime64[s]'))。

更多信息请参见 修正刻度过多的问题

确定图形中绘图元素(Artists)的范围#

有时我们需要知道某个 Artist 的范围。Matplotlib Artist 对象有一个方法 Artist.get_window_extent,通常会返回该元素以像素为单位的范围。然而,某些元素(特别是文本)必须在被知晓范围前至少渲染一次。Matplotlib 提供了 Figure.draw_without_rendering,在调用 get_window_extent 之前应该先调用它。

检查图形是否为空#

“空”实际上可能有不同的含义。图形是否包含任何 Artist?带有空 Axes 的图形是否仍然算作空?如果图形渲染结果为纯白色,它是否为空(可能存在 Artist,但它们在绘图区域之外或透明)?

在此,我们将“空”定义为:“除了背景补丁(background patch)外,图形不包含任何 Artist。”背景补丁的例外是必要的,因为默认情况下每个图形都包含一个 Rectangle 作为其背景。此定义可以通过以下方式进行检查

def is_empty(figure):
    """
    Return whether the figure contains no Artists (other than the default
    background patch).
    """
    contained_artists = figure.get_children()
    return len(contained_artists) <= 1

我们决定不将其包含为图形方法,因为这只是定义“空”的一种方式,且检查上述内容很少有必要。是否有内容被添加到图形中通常是在程序上下文中定义的。

检查图形是否会渲染为空的唯一可靠方法是实际执行渲染并检查结果。

查找图形中特定类型的所有对象#

每个 Matplotlib Artist(参见 Artist 教程)都有一个名为 findobj() 的方法,可用于递归搜索该 Artist 可能包含的任何符合特定条件的 Artist(例如,匹配所有 Line2D 实例或匹配某些任意过滤函数)。例如,以下片段查找图形中所有具有 set_color 属性的对象并将其设置为蓝色

def myfunc(x):
    return hasattr(x, 'set_color')

for o in fig.findobj(myfunc):
    o.set_color('blue')

您也可以按类实例进行过滤

import matplotlib.text as text
for o in fig.findobj(text.Text):
    o.set_fontstyle('italic')

防止刻度标签出现偏移量#

默认格式化程序将使用偏移量来缩短刻度标签的长度。要在每个轴的基础上关闭此功能

ax.xaxis.get_major_formatter().set_useOffset(False)

设置 rcParams["axes.formatter.useoffset"](默认:True),或者使用不同的格式化程序。详情请参阅 ticker

保存透明图形#

savefig() 命令有一个关键字参数 transparent,如果设为 'True',则在保存时使图形和坐标轴背景透明,但这不会影响屏幕上显示的图像。

如果您需要更精细的控制,例如您不想完全透明,或者想同时影响屏幕显示的版本,则可以直接设置 alpha 属性。图形有一个名为 patchRectangle 实例,坐标轴也有一个名为 patch 的 Rectangle 实例。您可以直接设置它们的任何属性(facecolor, edgecolor, linewidth, linestyle, alpha)。例如

fig = plt.figure()
fig.patch.set_alpha(0.5)
ax = fig.add_subplot(111)
ax.patch.set_alpha(0.5)

如果您需要所有图形元素都是透明的,目前没有全局的 alpha 设置,但您可以对各个元素设置 alpha 通道,例如

ax.plot(x, y, alpha=0.5)
ax.set_xlabel('volts', alpha=0.5)

将多个绘图保存到一个 PDF 文件中#

许多图像文件格式每个文件只能有一张图像,但有些格式支持多页文件。目前,Matplotlib 仅通过 backend_pdf.PdfPagesbackend_pgf.PdfPages 类,通过 pdf 或 pgf 后端提供对 PDF 文件的多页输出支持。

为刻度标签留出空间#

默认情况下,Matplotlib 在子图周围使用固定的百分比边距。这可能导致标签重叠或在图形边界处被截断。有多种方法可以解决此问题

跨多个子图对齐 Y 轴标签#

如果您有多个上下排列的子图,且 Y 数据具有不同的比例,则通常会导致 Y 轴标签在多个子图之间无法垂直对齐,这可能看起来不美观。默认情况下,Matplotlib 会放置 Y 轴标签的 X 位置,使其不与任何 Y 轴刻度重叠。您可以通过指定标签的坐标来覆盖此默认行为。要了解如何操作,请参阅 对齐标签和标题

控制绘图元素的绘制顺序#

绘图元素的绘制顺序(即哪些元素在最上方)由 set_zorder 属性决定。有关详细说明,请参阅 Zorder 演示

使绘图的长宽比相等#

Axes 属性 set_aspect() 控制坐标轴的长宽比。您可以将其设置为 'auto'、'equal' 或某个控制比例的数值

ax = fig.add_subplot(111, aspect='equal')

请参阅 相等坐标轴比例 以获取完整示例。

绘制多个 Y 轴刻度#

一个常见的需求是为左右 Y 轴设置两个刻度,这可以使用 twinx() 来实现(目前不支持两个以上的刻度,尽管在愿望清单中)。这运行得很好,但在尝试交互式平移和缩放时会有一些古怪之处,因为两个刻度并不会同时接收到信号。

这种方法使用 twinx()(及其姐妹函数 twiny())来使用 2 个不同的坐标轴,关闭第 2 个坐标轴的矩形框以防止遮挡第一个,并根据需要手动设置刻度位置和标签。由于两个坐标轴是独立的,您可以根据需要使用单独的 matplotlib.ticker 格式化程序和定位器。

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

请参阅 不同比例的绘图 以获取完整示例。

在不弹出窗口的情况下生成图像#

自 Matplotlib 3.1 以来的推荐方法是显式创建一个 Figure 实例

from matplotlib.figure import Figure
fig = Figure()
ax = fig.subplots()
ax.plot([1, 2, 3])
fig.savefig('myfig.png')

这可以防止与 GUI 框架和窗口管理器进行任何交互。

或者,仍然可以使用 pyplot 接口:不要调用 matplotlib.pyplot.show,而是调用 matplotlib.pyplot.savefig。在这种情况下,您必须在保存后关闭图形。不关闭图形会导致内存泄漏,因为 pyplot 会保留所有未显示的图形的引用。

import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
plt.savefig('myfig.png')
plt.close()

另请参阅

嵌入 Web 应用服务器 (Flask),了解有关在 Web 应用中运行 matplotlib 的信息。

使用线程#

Matplotlib 不是线程安全的:实际上,存在影响某些 Artist 的已知竞争条件。因此,如果您使用线程,您有责任设置适当的锁来序列化对 Matplotlib Artist 的访问。

您或许可以在不同的线程中处理不同的图形。但是,在这种情况下,您必须使用非交互式后端(通常是 Agg),因为大多数 GUI 后端也要求从主线程运行。

获取帮助#

有许多好的资源可以获取 Matplotlib 的帮助。您的问题很可能已经被问过了

如果您通过搜索无法找到答案,请在发送至 邮件列表 的电子邮件中提供以下信息

  • 您的操作系统 (Linux/Unix 用户:请贴出 uname -a 的输出)。

  • Matplotlib 版本

    python -c "import matplotlib; print(matplotlib.__version__)"
    
  • 您从哪里获得的 Matplotlib(例如,Linux 发行版的包、GitHub、PyPI 或 Anaconda)。

  • matplotlibrc 文件的任何自定义设置(参见 使用样式表和 rcParams 自定义 Matplotlib)。

  • 如果问题可复现,请尝试提供一个最小化、独立的 Python 脚本来演示该问题。这是关键的一步。如果您不能贴出一份我们可以运行并复现您错误的代码,那么获得帮助的可能性将显著降低。通常,仅仅尝试将代码简化到产生错误的最简部分,就能帮助您发现代码中导致问题的 Bug。

  • Matplotlib 通过 logging 库提供调试信息,以及一个设置日志级别的辅助函数:可以调用

    plt.set_loglevel("INFO")  # or "DEBUG" for more info
    

    来获取此调试信息。

    logging 模块中的标准函数也适用;例如,甚至可以在导入 Matplotlib 之前调用 logging.basicConfig(level="DEBUG")(这对于获取 Matplotlib 导入过程中发出的日志信息尤为必要),或者将自定义处理程序附加到 "matplotlib" 日志记录器。如果您使用自定义日志配置,这可能会很有用。

如果您是自行编译的 Matplotlib,请还提供

  • 您的编译器版本 -- 例如 gcc --version

  • 以下内容的输出

    pip install --verbose
    

    构建输出的开头包含了大量关于您平台的详细信息,这些信息对于 Matplotlib 开发人员诊断您的问题非常有用。

如果您使用旧的预 Meson 构建系统编译了较旧版本的 Matplotlib,请改提供

  • 您对 setup.py/setupext.py 所做的任何更改,

  • 以下内容的输出

    rm -rf build
    python setup.py build
    

将此信息包含在您发送给邮件列表的第一封电子邮件中可以节省大量时间。

写信给邮件列表比在 Bug 追踪器中提交 Bug 可能更快得到响应。大多数开发人员仅定期查看 Bug 追踪器。如果您的 Bug 被确定为 Bug 且无法快速解决,您可能会被要求在追踪器中提交 Bug,以免该问题被遗漏。