防止matplotlib有状态

时间:2015-02-20 14:48:59

标签: 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!

我的问题是:我能否一般阻止这种全局变量行为?我知道我可以在致电任何plt.close()之前致电innocent_looking_function(),但是有什么方法可以将其设为默认值吗?

1 个答案:

答案 0 :(得分:11)

当然!您需要做的是在制作数字时完全绕过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. 使用适当的后端
  3. 为该数字创建FigureManager
  4. 图形管理器创建FigureCanvas,gui窗口(根据需要)和NavigationToolbar2(缩放按钮等)
  5. 然后将数字管理器实例添加到_pylab_helpers.Gcf的数字列表中。
  6. 这是我们想要绕过的最后一步。


    这是使用非交互式后端的快速示例。请注意,因为我们并不担心与绘图进行交互,所以我们可以跳过整个数字管理器,只需创建一个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)
    

    只是为了证明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`
    

    通过互动支持,触摸起来更加复杂。你不能拨打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()
    

    要使用其他后端之一,只需更改后端导入即可。例如,对于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()
    

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

    即可