Сохранение интерактивных рисунков Matplotlib

Есть ли способ сохранить фигуру Matplotlib таким образом, чтобы ее можно было снова открыть и восстановить типичное взаимодействие? (Как формат .fig в MATLAB?)

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

Ответ 1

Это будет отличная функция, но AFAIK не реализуется в Matplotlib и, вероятно, будет трудно реализовать себя из-за того, как хранятся цифры.

Я бы предложил либо (a) раздельно обработать данные от создания фигуры (которая сохраняет данные с уникальным именем), так и написать цифру, генерирующую script (погрузка указанного файла сохраненных данных) и редактирование, поскольку вы см. соответствие или (b) сохранить как PDF/SVG/PostScript и редактировать в каком-нибудь фантастическом графическом редакторе, например Adobe Illustrator (или Inkscape).

Ответ 2

Как и в Matplotlib 1.2, мы имеем экспериментальную поддержку pickle. Дайте ему поглядеть, хорошо ли это для вашего дела. Если у вас есть какие-либо вопросы, сообщите нам об этом в рассылке Matplotlib или открыв проблему github.com/matplotlib/matplotlib.

Ответ 3

Я только что узнал, как это сделать. "Экспериментальная поддержка солености", упомянутая @pelson, работает достаточно хорошо.

Попробуйте следующее:

# Plot something
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([1,2,3],[10,-10,30])

После интерактивной настройки сохраните фигурный объект как двоичный файл:

import pickle
pickle.dump(fig, open('FigureObject.fig.pickle', 'wb')) # This is for Python 3 - py2 may need `file` instead of `open`

Позже откройте фигуру, и настройки должны быть сохранены, а интерактивность GUI должна присутствовать:

import pickle
figx = pickle.load(open('FigureObject.fig.pickle', 'rb'))

figx.show() # Show the figure, edit it, etc.!

Вы даже можете извлечь данные из графиков:

data = figx.axes[0].lines[0].get_data()

(Он работает для строк, pcolor и imshow - pcolormesh работает с некоторыми трюками для восстановления сглаженных данных.)

Я получил отличный совет от Сохранение фигур Matplotlib с использованием Pickle.

Ответ 4

Почему бы просто не отправить Python script? Файлы MATLAB.fig требуют, чтобы получатель имел MATLAB для их отображения, поэтому примерно эквивалентно отправке Python script, для которого требуется отображение Matplotlib.

В качестве альтернативы (отказ от ответственности: я еще не пробовал это сделать), вы можете попробовать прорисовать фигуру:

import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()

Ответ 5

Хороший вопрос. Вот текст документа из pylab.save:

pylab больше не обеспечивает функцию сохранения, хотя старый pylab     функция по-прежнему доступна как matplotlib.mlab.save(вы все еще можете     обратитесь к нему в pylab как "mlab.save" ). Однако для простого текста     файлов, мы рекомендуем numpy.savetxt. Для сохранения массивов numpy,     мы рекомендуем numpy.save и его аналоговый numpy.load, которые     доступно в pylab как np.save и np.load.

Ответ 6

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

import libscript

import matplotlib.pyplot as plt
import numpy as np

t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2*np.pi*t)

#<plot>
plt.plot(t, s)
plt.xlabel('time (s)')
plt.ylabel('voltage (mV)')
plt.title('About as simple as it gets, folks')
plt.grid(True)
plt.show()
#</plot>

save_plot(fileName='plot_01.py',obj=sys.argv[0],sel='plot',ctx=libscript.get_ctx(ctx_global=globals(),ctx_local=locals()))

с функцией save_plot, определенной следующим образом (простая версия для понимания логики):

def save_plot(fileName='',obj=None,sel='',ctx={}):
    """
    Save of matplolib plot to a stand alone python script containing all the data and configuration instructions to regenerate the interactive matplotlib figure.

    Parameters
    ----------
    fileName : [string] Path of the python script file to be created.
    obj : [object] Function or python object containing the lines of code to create and configure the plot to be saved.
    sel : [string] Name of the tag enclosing the lines of code to create and configure the plot to be saved.
    ctx : [dict] Dictionary containing the execution context. Values for variables not defined in the lines of code for the plot will be fetched from the context.

    Returns
    -------
    Return ``'done'`` once the plot has been saved to a python script file. This file contains all the input data and configuration to re-create the original interactive matplotlib figure.
    """
    import os
    import libscript

    N_indent=4

    src=libscript.get_src(obj=obj,sel=sel)
    src=libscript.prepend_ctx(src=src,ctx=ctx,debug=False)
    src='\n'.join([' '*N_indent+line for line in src.split('\n')])

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(src+'\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

или определяя функцию save_plot следующим образом (лучшая версия с использованием сжатия zip для создания более легких файлов фигур):

def save_plot(fileName='',obj=None,sel='',ctx={}):

    import os
    import json
    import zlib
    import base64
    import libscript

    N_indent=4
    level=9#0 to 9, default: 6
    src=libscript.get_src(obj=obj,sel=sel)
    obj=libscript.load_obj(src=src,ctx=ctx,debug=False)
    bin=base64.b64encode(zlib.compress(json.dumps(obj),level))

    if(os.path.isfile(fileName)): os.remove(fileName)
    with open(fileName,'w') as f:
        f.write('import sys\n')
        f.write('sys.dont_write_bytecode=True\n')
        f.write('def main():\n')
        f.write(' '*N_indent+'import base64\n')
        f.write(' '*N_indent+'import zlib\n')
        f.write(' '*N_indent+'import json\n')
        f.write(' '*N_indent+'import libscript\n')
        f.write(' '*N_indent+'bin="'+str(bin)+'"\n')
        f.write(' '*N_indent+'obj=json.loads(zlib.decompress(base64.b64decode(bin)))\n')
        f.write(' '*N_indent+'libscript.exec_obj(obj=obj,tempfile=False)\n')

        f.write('if(__name__=="__main__"):\n')
        f.write(' '*N_indent+'main()\n')

return 'done'

Это использует модуль libscript моего собственного, который в основном полагается на модули inspect и ast. Я могу попытаться поделиться им с Github, если интерес будет выражен (сначала мне нужно было немного очистить, а я начал с Github).

Идея этой функции save_plot и libscript заключается в том, чтобы извлечь команды python, которые создают рисунок (используя модуль inspect), проанализировать их (используя модуль ast), чтобы извлечь все переменные, функции и модули импортируют его, извлекают их из контекста выполнения и сериализуют их как инструкции python (код для переменных будет как t=[0.0,2.0,0.01]... и код для модулей будет как import matplotlib.pyplot as plt...), добавленный к инструкциям рисунка, Результирующие инструкции python сохраняются как python script, выполнение которого будет перестроить исходный рисунок matplotlib.

Как вы можете себе представить, это хорошо работает для большинства (если не всех) цифр matplotlib.