Я использую matplotlib как часть приложения Python для анализа некоторых данных. Моя цель - перебрать контейнер объектов и для каждого из них создать график содержащихся данных. Вот список использованных версий:
- Python 3.6.9
- matplotlib 3.1.1
- numpy 1.15.4
ПРОБЛЕМА
Проблема, с которой я сталкиваюсь, заключается в том, что объем памяти, используемой приложением Python, значительно увеличивается во время цикла, который генерирует графики, до такой степени, что компьютер становится почти не реагирующим.
Я упростил реализацию до простого кода, который можно запустить для воспроизведения поведения (это было вдохновлено публикацией, которую я нашел на matplotlib github во время поиска некоторых ответов в Интернете)
import logging
import matplotlib as mpl
import matplotlib.pyplot as plt
from memory_profiler import profile
from memory_profiler import memory_usage
import numpy as np
TIMES = 20
SAMPLES_PER_LINE = 20000
STATIONS = 100
np_ax = np.linspace(1, STATIONS, num=SAMPLES_PER_LINE)
np_ay = np.random.random_sample(size=SAMPLES_PER_LINE)
logging.basicConfig(level="INFO")
@profile
def do_plots_close(i):
fig = plt.figure(figsize=(16, 10), dpi=60)
for stn in range(STATIONS):
plt.plot(np_ax, np_ay + stn)
s_file_name = 'withClose_{:03d}.png'.format(i)
plt.savefig(s_file_name)
logging.info("Printed file %s", s_file_name)
plt.close(fig)
@profile
def do_plots_clf(i):
plt.clf()
for stn in range(STATIONS):
plt.plot(np_ax, np_ay + stn)
s_file_name = "withCLF_{:03d}.png".format(i)
plt.savefig(s_file_name)
logging.info("Printed file %s", s_file_name)
def with_close():
for i in range(TIMES):
do_plots_close(i)
def with_clf():
fig = plt.figure(figsize=(16, 10), dpi=60)
for i in range(TIMES):
do_plots_clf(i)
plt.close(fig)
if __name__ == "__main__":
logging.info("Matplotlib backend used: %s", mpl.get_backend())
mem_with_close = memory_usage((with_close, [], {}))
mem_with_clf = memory_usage((with_clf, [], {}))
plt.plot(mem_with_close, label='New figure opened and closed at each loop')
plt.plot(mem_with_clf, label='Single figure cleared at every loop')
plt.legend()
plt.title("Backend: {:s} - memory usage".format(mpl.get_backend()))
plt.ylabel('MB')
plt.grid()
plt.xlabel('time [s * 0.1]') # `memory_usage` logs every 100ms
plt.show()
Есть 2 функции, которые отображают графики TIMES
, каждая с SAMPLES
линиями с SAMPLES_PER_LINE
элементами (что несколько близко к размеру данных, с которыми я имею дело). do_plots_close()
создает новую фигуру в начале каждого цикла и закрывает ее в конце;тогда как do_plots_clf()
работает на том же рисунке, после чего выполняется очистка. memory_profiler
используется для получения памяти, используемой двумя функциями, которая затем отображается в виде рисунка, который прикрепляется:
with_close() and with_clf()
">
ПросмотрИз рисунка видно, что ни в одной из функций память, занятая одной фигурой, не освобождается полностью после закрытия фигуры (для do_plots_close()
) или очистки (для do_plots_clf()
). Я также пытался удалить параметр figsize
при создании фигуры, но я не увидел никакой разницы в результате .
ВОПРОС
Я ищу лучший способ решения этой проблемы и уменьшения объема памяти, используемой в функции сохранения графиков.
- Я неправильно использую API-интерфейс matplotlib?
- Есть ли способ управлять одним и тем же объемом данных, не испытывая при этом увеличения памяти?
- Почему память, кажется, освобождается только после нескольких графиков?
Любая помощь и предложения приветствуются.
Спасибо.