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

119

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

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

Matt
источник

Ответы:

30

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

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

РЕДАКТИРОВАТЬ сообщение Осень 2012 : как другие указывали ниже (хотя здесь упоминается, как это принятый ответ), Matplotlib с версии 1.2 позволяет вам мариновать цифры. Как указано в примечаниях к выпуску , это экспериментальная функция, которая не поддерживает сохранение рисунка в одной версии matplotlib и открытие в другой. Также, как правило, небезопасно восстанавливать рассол из ненадежного источника.

Для совместного использования / последующего редактирования графиков (которые сначала требуют значительной обработки данных и могут потребоваться несколько месяцев спустя, например, во время экспертной оценки научной публикации), я все же рекомендую рабочий процесс (1) иметь сценарий обработки данных, который перед созданием графика сохраняет обработанные данные (которые входят в ваш график) в файл и (2) имеет отдельный сценарий создания графика (который вы настраиваете по мере необходимости) для воссоздания графика. Таким образом, для каждого графика вы можете быстро запустить сценарий и повторно сгенерировать его (и быстро скопировать настройки графика с новыми данными). Тем не менее, выбор фигуры может быть удобен для краткосрочного / интерактивного / исследовательского анализа данных.

доктор джимбоб
источник
2
Немного удивлен, что это не реализовано .. Но ладно, я сохраню обработанные данные в промежуточном файле и отправлю его вместе со сценарием для построения коллегам. Спасибо.
Мэтт
2
Я подозреваю, что реализация сложна, поэтому она так плохо работает с MATLAB. Когда я использовал его, цифры, используемые для сбоя MATLAB, и даже немного разные версии не могли читать файлы .fig друг друга.
Адриан Ратнапала,
6
pickleтеперь работает с фигурами MPL, так что это можно сделать и, похоже, работает неплохо - почти как файл фигур Matlab ".fig". См. Мой ответ ниже (пока?), Чтобы увидеть, как это сделать.
Демис
@Demis: как ptomato указал в своем ответе ниже, он существовал уже в то время.
strpeter
@strpeter - цифры Matlab в 2010 году, как указано в этом комментарии, нельзя было подобрать . Экспериментальная функция была добавлена ​​в matplotlib 1.2, выпущенном осенью 2012 года . Как отмечено там, вы не должны ожидать, что он будет работать между версиями matplotlib, и вы не должны открывать соленые огурцы, поступающие из ненадежного источника.
dr jimbob
64

Я только что узнал, как это сделать. «Экспериментальная поддержка рассола», упомянутая @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`

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

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 .

Demis
источник
Я считаю, что это не устойчиво к изменениям версий и т. Д., А также несовместимо между py2.x и py3.x. Также я думал, что в pickleдокументации указано, что нам нужно настроить среду так же, как когда объект был маринован (сохранен), но я обнаружил, что вам не нужно это import matplotlib.pyplot as pltпри распаковке (загрузке) - он сохраняет операторы импорта в маринованном файле ,
Демис
5
Вам следует подумать о закрытии открытых файлов: напримерwith open('FigureObject.fig.pickle', 'rb') as file: figx = pickle.load(file)
strpeter
1
Я просто получаю: 'AttributeError: объект' Figure 'не имеет атрибута' _cachedRenderer '' '
user2673238
Если вы не хотите, чтобы сценарий продолжался и, вероятно, завершался сразу после этого figx.show(), вам следует plt.show()вместо этого вызвать , что является блокирующим.
Maechler
38

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

pelson
источник
2
По любой причине эта полезная функция может быть добавлена ​​к самому рисунку «Сохранить как». Возможно добавление .pkl?
dashesy
Хорошая идея @dashesy. Я бы поддержал это, если бы вы хотели попробовать реализовать это?
pelson
1
Это работает только на подмножестве серверных ВМ? Когда я пытаюсь собрать простую фигуру в OSX, я получаю PicklingError: Can't pickle <type '_macosx.GraphicsContext'>: it's not found as _macosx.GraphicsContext.
Вперед, 09
Вышеуказанное PicklingErrorпроисходит только в том случае, если вы позвоните plt.show()перед тем, как делать рассол. Так что просто ставьте plt.show()после pickle.dump().
salomonvh 08
На моем py3.5 на MacOS 10.11 порядок fig.show()не имеет значения - возможно, эта ошибка была исправлена. Я могу мариновать до и после show()без проблем.
Демис
7

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

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

import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
ptomato
источник
3
К сожалению, цифры matplotlib нельзя мариновать, поэтому такой подход не сработает. За кулисами слишком много расширений C, не поддерживающих травление. Я полностью согласен с тем, чтобы просто отправить скрипт + данные, хотя ... Думаю, я никогда не видел смысла сохраненных файлов .fig в Matlab, поэтому я никогда их не использовал. В любом случае, по моему опыту, в долгосрочной перспективе отправить кому-то отдельный код и данные было самым простым делом. Тем не менее, было бы неплохо, если бы объекты figure matplotlib были маринованными.
Джо Кингтон,
1
Даже наши предварительно обработанные данные несколько велики, а процедура построения графика сложна. Похоже, это единственный выход. Спасибо.
Мэтт
1
Фигуры, видимо, теперь мариновать - работает неплохо! Пример ниже.
Demis
Преимущество pickle заключается в том, что вам не нужно программно настраивать все интервалы / позиции между фигурами / частями графика. Вы можете использовать графический интерфейс MPL-графика, чтобы фигура выглядела красиво и т. Д., А затем сохранить MyPlot.fig.pickleфайл, сохранив при этом возможность настраивать представление графика по мере необходимости. Это также то, что замечательно в .figфайлах Matlab . Особенно полезно, когда вам нужно изменить размер / соотношение сторон инжира (для вставки в презентации / документы).
Демис
1

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

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

Стив Тхоа
источник
Это сохраняет данные из объекта pylab, но не позволяет вам регенерировать фигуру.
доктор джимбоб 03
Верный. Я должен уточнить, что ответ не был рекомендацией к использованию pylab.save. В самом деле, из текста дока, оказывается , что нужно не использовать его.
Стив Тьоа,
Есть ли какой-либо внешний способ отправить 3D-фигуру? Возможен даже простой графический интерфейс для exe ..
CromeX
0

Я придумал относительно простой (но немного нетрадиционный) способ сохранить мои цифры 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, выполнение которого воссоздает исходную фигуру matplotlib.

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

Astrum42
источник