防止matplotlib有状态

nic*_*ten 8 python matplotlib

如果我创建一个Axes对象matplotlib并进行变异(即通过绘制一些数据)然后我调用一个函数而不将我的Axes对象传递给该函数,那么该函数仍然可以改变我的Axes.例如:

import matplotlib.pyplot as plt
import numpy as np

def innocent_looking_function():
    #let's draw a red line on some unsuspecting Axes!
    plt.plot(100*np.random.rand(20), color='r')

fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20), color='b') #draw blue line on ax
#ax now has a blue line, as expected

innocent_looking_function()
#ax now unexpectedly has a blue line and a red line!
Run Code Online (Sandbox Code Playgroud)

我的问题是:我能否一般地阻止这种全局变量行为?我知道我可以在打电话plt.close()之前打电话给innocent_looking_function()但是有什么办法让这个默认吗?

Joe*_*ton 12

当然!您需要做的是pyplot在制作数字时完全绕过状态机.

它更加冗长,因为你不能只是打电话fig = plt.figure().


首先,让我解释一下如何plt.gca()plt.gcf()有效.使用pyplot界面时,matplotlib会存储所有已创建但未显示的图形管理器.图管理器基本上是数字的gui包装器.

plt._pylab_helpers.Gcf是存储图形管理器的单例对象,并跟踪哪一个当前处于活动状态.plt.gcf()从中返回活动数字_pylab_helpers.Gcf.每个Figure对象都跟踪它自己的轴,所以plt.gca()就是这样plt.gcf().gca().

通常,当你打电话时plt.figure(),它:

  1. 创建返回的图形对象
  2. FigureManager使用适当的后端为该数字创建一个
  3. 图形管理器创建一个FigureCanvasgui窗口(根据需要)和NavigationToolbar2(缩放按钮等)
  4. 然后将图形管理器实例添加到_pylab_helpers.Gcf图形列表中.

这是我们想要绕过的最后一步.


这是使用非交互式后端的快速示例.请注意,因为我们不担心与绘图交互,所以我们可以跳过整个图管理器并只创建一个FigureFigureCanvas实例.(从技术上讲,我们可以跳过FigureCanvas,但只要我们想要将图表保存到图像等,就会需要它.)

import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure

# The pylab figure manager will be bypassed in this instance. `plt.gca()`
# can't access the axes created here.
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)
Run Code Online (Sandbox Code Playgroud)

只是为了证明gca看不到这个轴:

import matplotlib.pyplot as plt
import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure

# Independent figure/axes
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot(range(10))

# gca() is completely unaware of this axes and will create a new one instead:
ax2 = plt.gca()
print 'Same axes?:', id(ax) == id(ax2)

# And `plt.show()` would show the blank axes of `ax2`
Run Code Online (Sandbox Code Playgroud)

通过交互式支持,触摸更加复杂.你不能打电话plt.show(),所以你需要自己启动gui的主循环.您可以"从头开始"完成所有操作(请参阅任何"嵌入matplotlib"示例),但是将FigureManager特定于支持的部分抽象出来:

作为使用TkAgg后端的示例:

import matplotlib.backends.backend_tkagg as backend
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(111)

manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()
Run Code Online (Sandbox Code Playgroud)

要使用其他后端之一,只需更改后端导入即可.例如,对于Qt4:

import matplotlib.backends.backend_qt4agg as backend
from matplotlib.figure import Figure

fig = Figure()
ax = fig.add_subplot(111)

manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()
Run Code Online (Sandbox Code Playgroud)

这实际上甚至nbagg适用于IPython笔记本中使用的后端.只需将后端导入更改为import matplotlib.backends.backend_nbagg as backend