6

I generate a lots of figures with a script which I do not display but store to harddrive. After a while I get the message

/usr/lib/pymodules/python2.7/matplotlib/pyplot.py:412: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_num_figures). max_open_warning, RuntimeWarning)

Thus, I tried to close or clear the figures after storing. So far, I tried all of the followings but no one works. I still get the message from above.

plt.figure().clf()
plt.figure().clear()
plt.clf()
plt.close()
plt.close('all')
plt.close(plt.figure())

And furthermore I tried to restrict the number of open figures by

plt.rcParams.update({'figure.max_num_figures':1})

Here follows a piece of sample code that behaves like described above. I added the different options I tried as comments at the places I tried them.

from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))

import matplotlib.pyplot as plt
plt.ioff()
#plt.rcParams.update({'figure.max_num_figures':1})
for i in range(0,30):
    fig, ax = plt.subplots()
    ax.hist([df])
    plt.savefig("/home/userXYZ/Development/pic_test.png")
    #plt.figure().clf()
    #plt.figure().clear()
    #plt.clf()
    #plt.close() # results in an error
    #plt.close('all') # also error
    #plt.close(plt.figure()) # also error

To be complete, that is the error I get when using plt.close:

can't invoke "event" command: application has been destroyed while executing "event generate $w <>" (procedure "ttk::ThemeChanged" line 6) invoked from within "ttk::ThemeChanged"

AnnetteC
  • 490
  • 2
  • 5
  • 20
  • To make sure you always work on the same figure, just use this `plt.figure(1).clf()` – Julien Aug 29 '17 at 08:18
  • 3
    plt.close(fig) should do it – tupui Aug 29 '17 at 08:21
  • @Julien That also produces the RuntimeWarning – AnnetteC Aug 29 '17 at 09:04
  • @Y0da: Results in the same Error as above – AnnetteC Aug 29 '17 at 09:05
  • Have you check this thread: https://stackoverflow.com/q/7101404/6522112 – tupui Aug 29 '17 at 09:07
  • 2
    Possible [duplicate](https://stackoverflow.com/questions/21884271/warning-about-too-many-open-figures). Move the creation of the figure out of the for loop and use `plt.clf()` to clear the figure after every iteration. Thus only one figure will be created and repopulated. – dummman Aug 29 '17 at 09:23
  • @plinius_prem Yes, that works for this simple example. The original code is quite more complicated and I cannot move the figure creation out of the loop.... – AnnetteC Aug 29 '17 at 10:11
  • @Y0da I do not use Django. Or is there something in the background of matplotlib? – AnnetteC Sep 04 '17 at 08:38
  • Although the code you posted does not mention using Matplotlib’s TkAgg backend or Tkinter, the `ttk::ThemeChanged` error is a Tk bug (i.e. not your fault): https://core.tcl-lang.org/tk/info/310c74ecf440 – chrstphrchvz Apr 25 '23 at 12:18
  • @chrstphrchvz Since that was 6 years ago I dealed with that stuff and do not have access to code anymore, I cannot answer that 100%. But I assume that the code above was runnable the way I posted it. And I cannot remember to have used TkAgg or Tkinter explicitly, it is most likely included into the matplotlib library... – AnnetteC Apr 26 '23 at 06:46

4 Answers4

3

The correct way to close your figures would be to use plt.close(fig), as can be seen in the below edit of the code you originally posted.

from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))

import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
    fig, ax = plt.subplots()
    ax.hist(df)        
    name = 'fig'+str(i)+'.png'  # Note that the name should change dynamically
    plt.savefig(name)
    plt.close(fig)              # <-- use this line

The error that you describe at the end of your question suggests to me that your problem is not with matplotlib, but rather with another part of your code (such as ttk).

Bas Jansen
  • 3,273
  • 5
  • 30
  • 66
  • 1
    I ran your code but it also results in that ttk error - do not know the reasons for that. And I do not want to have a plot but an histogram... – AnnetteC Aug 30 '17 at 07:43
  • You can swap the `plt.plot(df)` line with your way of generating the histogram, I just did not fix your `ax.hist([df])` into `ax.hist(df)`, I have modified that in the answer now. However, your question was about how one should close the figure, which is what I have focussed on. – Bas Jansen Aug 30 '17 at 08:29
  • 'ax.hist(df)' produces mess - I do not get bars but only a filled square. And still: 'plt.close(fig)' produces that 'ttk' error for which I did not find any solution. – AnnetteC Sep 04 '17 at 08:31
  • @AnnetteC That is because there is no problem in the matplotlib part. Your problem is caused by something else that is not asked/listed/displayed in the question. – Bas Jansen Sep 04 '17 at 10:21
-1

This does not really solve my problem, but it is a work-around to handle the high memory consumption I faced and I do not get any of the error messages as before:

from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))

import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
    plt.close('all')
    fig, ax = plt.subplots()
    ax.hist([df])
    plt.savefig("/home/userXYZ/Development/pic_test.png")
AnnetteC
  • 490
  • 2
  • 5
  • 20
-1

plt.show() is a blocking function, so in the above code, plt.close() will not execute until the fig windows are closed. You can use plt.ion() at the beginning of your code to make it non-blocking. Even though this has some other implications the fig will be closed.

archana
  • 23
  • 8
-1

I was still having the same issue on Python 3.9.7, matplotlib 3.5.1, and VS Code (the issue that no combination of plt.close() closes the figure). I have three loops which the most inner loop plots more than 20 figures. The solution that is working for me is using agg as backend and del someFig after plt.close(someFig). Subsequently, the order of code would be something like:

import matplotlib
matplotlib.use('agg')

import matplotlib.pyplot as plt

someFig = plt.figure()

.
.
.

someFig.savefig('OUTPUT_PATH')
plt.close(someFig) # --> (Note 1)
del someFig

.
.
.


  • NOTE 1: If this line is removed, the output figures may not be plotted correctly! Especially when the number of elements to be rendered in the figure is high.
  • NOTE 2: I don't know whether this solution could backfire or not, but at least it is working and not hugging RAM or preventing plotting figures!