何时使用cla()、clf()或close()来清除绘图

740

Matplotlib 提供了以下函数:

cla()   # Clear axis
clf()   # Clear figure
close() # Close a figure window

我应该在什么时候使用每个函数,它们具体是做什么的?

4个回答

926

它们都有不同的功能,因为matplotlib使用分层顺序,其中一个图形窗口包含一个由许多轴组成的图形。此外,有来自pyplot界面的函数和Figure类上的方法。我将在下面讨论这两种情况。

pyplot界面

pyplot是一个收集了几个函数的模块,允许以函数方式使用matplotlib。我在这里假设已经导入pyplot,并命名为import matplotlib.pyplot as plt。 在这种情况下,有三个不同的命令可以移除东西:

请参见matplotlib.pyplot函数:

  • plt.cla()清除轴,即当前图像中的活动轴。 它不会影响其他轴。
  • plt.clf()清除整个当前带有所有轴的图形,但保留打开的窗口,以便可以重复使用它进行其他绘图。
  • plt.close()关闭窗口,如果没有特别指定,则为当前窗口。

因此,哪个函数最适合您取决于您的用例。

close()函数可以让你指定要关闭的窗口。该参数可以是数字或名称,这些数字或名称在创建图形使用figure(number_or_name)时赋予窗口,也可以是图形实例fig,例如使用fig = figure()获得。如果没有给close()提供参数,则将关闭当前活动窗口。此外,还有语法close('all'),它会关闭所有图形。
另外,Figure类提供了清除图形的方法。以下内容假设figFigure的一个实例: fig.clf()清除整个图形。如果fig是当前图形,则此调用等效于plt.clf()fig.clear()fig.clf()的同义词。
请注意,即使del fig也不能关闭相关联的图形窗口。据我所知,唯一关闭图形窗口的方法是使用如上所述的plt.close(fig)

115

今天我发现一个需要注意的问题。如果你有一个函数需要频繁调用plot方法,最好使用plt.close(fig)而不是fig.clf(),因为前者不会在内存中累积。简而言之,如果你关心内存,请使用plt.close(fig)(尽管似乎有更好的方法,请查看本评论末尾的相关链接)。

因此,以下代码将生成一个空列表:

for i in range(5):
    fig = plot_figure()
    plt.close(fig)
# This returns a list with all figure numbers available
print(plt.get_fignums())

而这个将会产生一个有五个数字的列表。

for i in range(5):
    fig = plot_figure()
    fig.clf()
# This returns a list with all figure numbers available
print(plt.get_fignums())

从上面的文档中,我不清楚关闭图形和关闭窗口之间的区别。也许这会澄清问题。

如果您想尝试完整的脚本,请参考以下内容:

import numpy as np
import matplotlib.pyplot as plt
x = np.arange(1000)
y = np.sin(x)

for i in range(5):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(x, y)
    plt.close(fig)

print(plt.get_fignums())

for i in range(5):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(x, y)
    fig.clf()

print(plt.get_fignums())

如果内存是一个问题,有人在stackoverflow上发布了一个解决方法,请参见: Create a figure that is reference counted


40
感谢你提供了有关引用计数问题的有用交叉参考。这正是Matplotlib应该已经做到的。使用标准的pyplot API时,图形从不被垃圾回收,这同样令人恐惧和骇人听闻。 - Cecil Curry
2
然而,我发现如果需要制作动画(例如某些2D轮廓/ pcolormesh地图),最好清除图形并绘制新的字段,而不是关闭旧的并制作新的图形面板。速度会完全不同。 - msi_gerva
谢谢,我的动画问题已经解决了,我在绘图时有轴重复的问题,通过在animate函数中使用plt.cla()解决了。 - zaheer
1
如果您使用plt.clf()后跟plt.close(),那么内存会被正确释放。仅仅使用plt.close()仍然会导致每个打开的图形增加残留内存。 - DrBwts

24

2

何时使用cla()、clf()或close()

正如David Zwicker所解释的那样,这些方法清除的是不同的内容。本文将提供一些示例,以帮助澄清何时使用它们。

如果您想要重复使用图形的特定配置(如面颜色、dpi、布局等)用于另一个图形,请使用fig.clf()清除图形上的当前对象(包括Axes)。请注意,这不会删除先前实例化的Figure实例,而只会清除其中的对象。因此,在下面的代码中,一个单独的Figure实例被重复使用以生成2个图像文件。
由于fig.clf()会删除Axes,所以重要的是在其上添加一个新的Axes以便绘制内容。
如果您想要重复使用一个图形但更改其中的Axes,则使用ax.cla()清除Axes上的当前对象(例如刻度、标题、绘图等)。特别是,图形上的Axes的位置是一个重要的属性,可以重复使用(例如,图形上的小Axes可以叠加在较大的Axes上)。
如果要关闭一个图形窗口,请使用plt.close()。这在使用具有交互模式的IDE(如Jupyter或Spyder)且不想看到脚本创建的图形时特别有用。如果使用Python shell(附带Python安装的IDLE),则它会关闭显示图形的窗口。即使关闭了窗口,Figure实例仍然在内存中,并且包含在其上定义的任何对象。例如,在下面的代码中,即使关闭了fig,仍然可以对其进行操作。但是,由于图形/Axes没有被清除,fig中的内容仍然存在。

内存使用情况

Heberto Mayorquin 指出 plt.close() 可以节省内存。然而,下面的内存分配追踪显示,如果在循环中需要创建大量相似的图像,使用 clf()cla() 清除实际上比关闭窗口并使用 plt.close() 创建新的图形实例更节省内存。

然而,有一个需要注意的地方,由于 cla()/clf() 是用来重复使用先前定义的 Axes/Figure 的,所以在循环之外(创建图像文件的循环)定义要重复使用的 Figure 对象非常重要。我还在测试中包含了“错误”的使用方式(每次绘制新图形时都创建一个新的 Figure 实例),这确实比在循环中创建新的 Figure 并关闭它更加耗费资源。

close:
current memory usage is 12,456 KB; peak was 13,402 KB.
======================================================
cla:
current memory usage is 899 KB; peak was 1,451 KB.
======================================================
clf:
current memory usage is 3,806 KB; peak was 8,061 KB.
======================================================
clf_wrong:
current memory usage is 7,392 KB; peak was 10,494 KB.
======================================================
cla_wrong:
current memory usage is 29,174 KB; peak was 29,650 KB.
======================================================

上述结果所使用的代码如下:
import os
import tracemalloc
import matplotlib.pyplot as plt

def close(n):
    for i in range(n):
        fig = plt.figure(facecolor='white', figsize=(6,3), dpi=36)
        ax = fig.add_subplot()
        ax.plot([100, 0, i])
        fig.savefig(f'{i}.png')
        plt.close(fig)


def cla(n):
    fig = plt.figure(facecolor='white', figsize=(6,3), dpi=36)
    ax = fig.add_subplot()
    for i in range(n):
        ax.plot([100, 0, i])
        fig.savefig(f'{i}.png')
        ax.cla()


def clf(n):
    fig = plt.figure(facecolor='white', figsize=(6,3), dpi=36)
    for i in range(n):
        ax = fig.add_subplot()
        ax.plot([100, 0, i])
        fig.savefig(f'{i}.png')
        fig.clf()


def cla_wrong(n):
    for i in range(n):
        fig = plt.figure(facecolor='white', figsize=(6,3), dpi=36)
        ax = fig.add_subplot()
        ax.plot([100, 0, i])
        fig.savefig(f'{i}.png')
        ax.cla()


def clf_wrong(n):
    for i in range(n):
        fig = plt.figure(facecolor='white', figsize=(6,3), dpi=36)
        ax = fig.add_subplot()
        ax.plot([100, 0, i])
        fig.savefig(f'{i}.png')
        fig.clf()


if __name__ == '__main__':
    
    for func in (close, cla, clf, clf_wrong, cla_wrong):
        tracemalloc.start()
        func(100)
        size, peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        # delete the saved image files
        for f in os.listdir():
            if f.endswith('.png'):
                os.remove(f)
        print(f"{func.__name__}:\ncurrent memory usage is {size/1024:,.0f} KB; \
peak was {peak/1024:,.0f} KB.")
        print("======================================================")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接