Подтвердить что ты не робот

Matplotlib и Pyplot.close() не освобождают память? - связанный с базой данных Qt4Agg

РЕДАКТИРОВАТЬ: Если я эксплицирую изменение бэкэнда для matplotlib от "Qt4Agg" до "Agg", то я могу запустить свой код без ошибок. Я предполагаю, что это ошибка в бэкэнд?

Я пишу некоторый код для обработки довольно большого количества данных автоматически. Код прежде всего анализирует мои файлы данных и сохраняет все соответствующие биты. Затем у меня есть разные функции для создания каждого из графиков, которые мне нужны (всего около 25). Тем не менее, я все время сталкиваюсь с какой-то ошибкой памяти, и я думаю, что это потому, что Matplotlib/PyPlot не освобождают память правильно.

Каждая функция построения заканчивается командой pyplot.close(fig), и поскольку я просто хочу сохранить графики и не смотреть на них сразу, они не включают pyplot.show().

Если я запускаю функции построения в отдельности в интерпретаторе, то никаких проблем не возникает. Однако, если я создаю отдельную функцию, которая по очереди вызывает каждую функцию построения графика, я запускаю "MemoryError: не могу выделить память для пути".

Кто-нибудь сталкивался с такой проблемой? Казалось бы, это связано с Matplotlib исчерпывает память при построении графика в цикле, но pyplot.close() не исправляет мою проблему.

Это то, что типичная функция plot выглядит в моем коде:

def TypicalPlot(self, title=None, comment=False, save=False, show=True):

    if title is None:
        title = self.dat.title

    fig = plt.figure()
    host = SubplotHost(fig, 111)
    fig.add_subplot(host)
    par = host.twinx()
    host.set_xlabel("Time (hrs)")
    host.set_ylabel("Power (W)")
    par.set_ylabel("Temperature (C)")
    p1, = host.plot(self.dat.timebase1, self.dat.pwr, 'b,', label="Power",
                    markevery= self.skip)
    p2, = par.plot(self.dat.timebase2, self.dat.Temp1, 'r,', 
                   label="Temp 1", markevery= self.skip)
    p3, = par.plot(self.dat.timebase2, self.dat.Temp2, 'g,', 
                   label="Temp 2", markevery= self.skip)
    p4, = par.plot(self.dat.timebase2, self.dat.Temp3, 'm,', 
                   label="Temp 3", markevery= self.skip)
    host.axis["left"].label.set_color(p1.get_color())
    # par.axis["right"].label.set_color(p2.get_color())
    #host.legend(loc='lower left')
    plt.title(title+" Temperature")

    leg=host.legend(loc='lower left',fancybox=True)
    #leg.get_frame().set_alpha(0.5)
    frame  = leg.get_frame()
    frame.set_facecolor('0.80')

    ### make the legend text smaller
    for t in leg.get_texts():
        t.set_fontsize('small')

    ### set the legend text color to the same color as the plots for added
    ### readability
    leg.get_texts()[0].set_color(p1.get_color())
    leg.get_texts()[1].set_color(p2.get_color())
    leg.get_texts()[2].set_color(p3.get_color())    
    leg.get_texts()[3].set_color(p4.get_color())        

    if show is True and save is True:
        plt.show()
        plt.savefig('temp.png')
    elif show is True and save is False:
        plt.show()
    elif show is False and save is True:
        plt.savefig('temp.png')
        plt.clf()
        plt.close(fig)

Если я сейчас запустил терминал

MyClass.TypicalPlot(save=True, show = False) 

Тогда я не получаю никаких ошибок. То же самое верно для всех моих функций графика.

Если я создаю новую функцию, которая делает это:

def saveAllPlots(self, comments = False):

        if self.comment is None: comment = False
        else: comment = True
        self.TypicalPlot(save=True, show=False, comment=comment)
        self.AnotherPlot(save=True, show=False)
        self.AnotherPlot2(save=True, show=False)
        self.AnotherPlot3(save=True, show=False)
        ...etc, etc, etc

Затем он проходит примерно половину графиков, а затем я получаю "MemoryError: не удалось выделить память для пути".

4b9b3361

Ответ 1

Я думаю, что причина, по которой это происходит, состоит в том, что, поскольку она проходит через все разные графики, у нее тогда заканчивается память, потому что она не освобождает ее должным образом.

Почему бы вам не попробовать создать около 3 или около того программ, каждая из которых выполняет несколько графиков вместо одной программы, выполняющей все графики:

Программа 1: Графики 1-8

Программа 2: Графики 9-16

Программа 3: Графики 17-25

Надеюсь, это поможет @FakeDIY:)

Ответ 2

Я сталкиваюсь с очень похожими проблемами один раз. Я предполагаю, что matplotlib хранит ссылки для каждого участка внутри. Учитывая следующий код, создайте три отдельные цифры:

import matplotlib.pyplot as plt
import numpy as np

# block 1
f, ax = plt.subplots(1)
plt.plot(np.arange(10), np.random.random(10))
plt.title("first")
print 'first', sys.getrefcount(f), sys.getrefcount(ax)

# bock 2
f, ax = plt.subplots(1)
plt.plot(np.arange(10), np.random.random(10)+1)
plt.title("second")
print 'second', sys.getrefcount(f), sys.getrefcount(ax)

# block 3
f, ax = plt.subplots(1)
plt.plot(np.arange(10), np.random.random(10)+2)
plt.title("third")
print 'third', sys.getrefcount(f), sys.getrefcount(ax)

plt.show()

print 'after show', sys.getrefcount(f), sys.getrefcount(ax)

Вывод:

first 69 26
second 69 26
third 69 26
after show 147 39

Это счетчик интуитивно понятен, потому что мы несколько раз определяли f и ax. С каждым блоком мы создали новую цифру, на которую можно ссылаться через plt. Создание другой фигуры изменяет самые верхние ссылки, доступные с помощью plt. Но должна быть какая-то внутренняя ссылка, которая позволяет plt.show() показывать все цифры. Эти ссылки кажутся постоянными, и, следовательно, цифры не будут собираться gc.

Обходной путь, с которым я согласился, менял данные сюжета. В ретроспективе это был лучший подход:

plt.ion()
f, ax = plt.subplots(1)
line = ax.plot(np.arange(10), np.random.random(10))[0]
plt.title('first')
plt.show()

for i, s in [(2, 'second'), (3, 'third')]:
    x = np.arange(10)
    y = np.random.random(10)+i
    line.set_data(x, y)
    ax.set_xlim(np.min(x), np.max(x))
    ax.set_ylim(np.min(y), np.max(y))
    plt.title(s)
    plt.draw()
    raw_input(s)

Единственным недостатком является то, что вы должны оставить окно с открытой фигурой. И без raw_input программа будет просто запускаться через