Збереження інтерактивних фігур Matplotlib


119

Чи є спосіб зберегти фігуру Matplotlib таким чином, щоб її можна було знову відкрити та відновити типову взаємодію? (Як формат .fig у MATLAB?)

Мені здається, що я багато разів використовую ті самі сценарії, щоб генерувати ці інтерактивні фігури. Або я надсилаю колегам кілька статичних файлів PNG, щоб показати різні аспекти сюжету. Я вважаю за краще відправити об'єкт фігури і дозволити їм взаємодіяти з ним самі.

Відповіді:


30

Це було б чудовою особливістю, але AFAIK він не реалізований у Matplotlib і, ймовірно, буде важко реалізувати себе через те, як зберігаються фігури.

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

EDIT після осені 2012 року : Як вже вказували інші (хоча тут згадується, як це прийнята відповідь), Matplotlib з версії 1.2 дозволив вам збирати фігури. Як зазначається у випуску , це експериментальна особливість і не підтримує збереження фігури в одній версії matplotlib та відкриття в іншій. Також відновити соління з недовіреного джерела зазвичай не небезпечно.

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


2
Дещо здивований, це не реалізовано. Але добре, я збережу оброблені дані в проміжний файл і надішлю це та сценарій для складання графіків колегам. Дякую.
Метт

2
Я підозрюю, що реалізація є складною, тому вона працює так погано як MATLAB. Ще коли я використовував це, цифри, які використовувались для збоїв MATLAB, і навіть трохи інші версії не змогли прочитати файли .fig.
Адріан Ратнапала

6
pickleзараз працює над цифрами MPL, тому це може бути зроблено і, здається, працює добре - майже як файл фігури Matlab ".fig". Дивіться мою відповідь нижче (наразі?) На приклад того, як це зробити.
Деміс

@Demis: як птомато вказував у своїй відповіді нижче, він існував уже на той час.
стріпетер

@strpeter - цифри Matlab не були обрані в 2010 році, як зазначено в цьому коментарі . Експериментальну особливість було додано з Matplotlib 1.2, випущеним восени 2012 року . Як зазначалося там, не слід очікувати, що він працює між версіями matplotlib, і ви не повинні відкривати соління, що надходять з недовіреного джерела.
д-р Джимбоб

63

Я щойно дізнався, як це зробити. "Експериментальна підтримка соління", згадана @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 за допомогою маринаду .


Я вважаю, що це не є надійним до змін версії тощо, і не є сумісним між 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:' Рисунок 'об’єкта не має атрибута' _cachedRenderer ''
user2673238

Якщо ви не хочете, щоб сценарій продовжувався і, ймовірно, закінчувався відразу після цього figx.show(), вам слід зателефонувати plt.show(), що блокує.
maechler

38

Станом на Matplotlib 1.2 у нас є експериментальна підтримка соління . Дайте це піти і подивіться, чи добре це працює у вашій справі. Якщо у вас є якісь проблеми, повідомте нас про це у списку розсилки Matplotlib або відкривши випуск на github.com/matplotlib/matplotlib .


2
Будь-яка причина цієї корисної функції може бути додана до самої фігури "Зберегти як". Додавання .pkl можливо?
тире

Гарна ідея @dashesy. Я б підтримав це, якщо ви хочете дати йому реалізацію?
Пельсон

1
Це працює лише на підмножині мікстур? Коли я намагаюся зібрати просту фігуру в OSX, я отримую PicklingError: Can't pickle <type '_macosx.GraphicsContext'>: it's not found as _macosx.GraphicsContext.
farenorth

Вищезазначене PicklingErrorвиникає лише в тому випадку, якщо ви телефонуєте plt.show()перед тим, як робити соління. Тож просто помістіть plt.show()після pickle.dump().
саломонвх

У моєму 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()

3
На жаль, цифри matplotlib не піддаються вибору, тому такий підхід не працює. За лаштунками занадто багато розширень C, які не підтримують маринування. Я повністю погоджуюся просто надсилати скрипт + дані ... Я думаю, я ніколи не бачив сенсу збережених .fig matlab, тому я ніколи не використовував їх. На моєму досвіді, надіслати комусь окремі коди та дані було найпростішим у довгостроковій перспективі. І все-таки було б непогано, якби фігурка matplotlib була предметом, де можна вибрати.
Джо Кінгтон

1
Навіть наші попередньо оброблені дані дещо великі, і процедура складання складна. Схоже, це єдиний регрес. Дякую.
Метт

1
Цифри, мабуть, зараз вибирають - це працює досить добре! Приклад нижче.
Деміс

Перевага для маринування полягає в тому, що вам не доведеться програмно коригувати всі відстані / положення фігури / субплоти. Ви можете використовувати графічний інтерфейс MPL-графіку 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.


Це зберігає дані від об'єкта пілаб, але не дозволяє регенерувати фігуру.
dr Jimbob

Правильно. Я повинен уточнити, що відповідь не була рекомендацією щодо використання pylab.save. Насправді з тексту doc виходить, що користуватися ним не слід.
Стів Тьоа

Чи є зовнішній метод для надсилання 3D-фігури? Можливий навіть простий графічний інтерфейс для exe ..
CromeX

0

Я зрозумів порівняно простий спосіб (але трохи нетрадиційний), щоб зберегти свої фігури з матплотлібу. Це працює так:

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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.