Python 保存交互式 Matplotlib 图形

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4348733/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 15:21:13  来源:igfitidea点击:

Saving interactive Matplotlib figures

pythonmatplotlib

提问by Matt

Is there a way to save a Matplotlib figure such that it can be re-opened and have typical interaction restored? (Like the .fig format in MATLAB?)

有没有办法保存 Matplotlib 图形,以便它可以重新打开并恢复典型的交互?(像 MATLAB 中的 .fig 格式?)

I find myself running the same scripts many times to generate these interactive figures. Or I'm sending my colleagues multiple static PNG files to show different aspects of a plot. I'd rather send the figure object and have them interact with it themselves.

我发现自己多次运行相同的脚本来生成这些交互式图形。或者我向我的同事发送多个静态 PNG 文件以显示绘图的不同方面。我宁愿发送图形对象并让他们自己与之交互。

采纳答案by dr jimbob

This would be a great feature, but AFAIK it isn't implemented in Matplotlib and likely would be difficult to implement yourself due to the way figures are stored.

这将是一个很棒的功能,但是 AFAIK 它没有在 Matplotlib 中实现,并且由于数字的存储方式可能很难自己实现。

I'd suggest either (a) separate processing the data from generating the figure (which saves data with a unique name) and write a figure generating script (loading a specified file of the saved data) and editing as you see fit or (b) save as PDF/SVG/PostScriptformat and edit in some fancy figure editor like Adobe Illustrator(or Inkscape).

我建议 (a) 将数据与生成图形(以唯一名称保存数据)分开处理并编写图形生成脚本(加载保存数据的指定文件)并根据需要进行编辑或(b ) 保存为 PDF/ SVG/ PostScript格式并在一些花哨的图形编辑器中编辑,如Adobe Illustrator(或Inkscape)。

EDIT post Fall 2012: As others pointed out below (though mentioning here as this is the accepted answer), Matplotlib since version 1.2 allowed you to pickle figures. As the release notes state, it is an experimental feature and does not support saving a figure in one matplotlib version and opening in another. It's also generally unsecure to restore a pickle from an untrusted source.

2012 年秋季发布的编辑帖子:正如其他人在下面指出的(尽管在这里提到这是公认的答案),自 1.2 版以来的 Matplotlib 允许您腌制数字。正如发行说明所述,它是一项实验性功能,不支持在一个 matplotlib 版本中保存图形并在另一个版本中打开。从不受信任的来源恢复泡菜通常也是不安全的。

For sharing/later editing plots (that require significant data processing first and may need to be tweaked months later say during peer review for a scientific publication), I still recommend the workflow of (1) have a data processing script that before generating a plot saves the processed data (that goes into your plot) into a file, and (2) have a separate plot generation script (that you adjust as necessary) to recreate the plot. This way for each plot you can quickly run a script and re-generate it (and quickly copy over your plot settings with new data). That said, pickling a figure could be convenient for short term/interactive/exploratory data analysis.

对于共享/稍后编辑图(需要先进行大量数据处理,可能需要在几个月后进行调整,例如在科学出版物的同行评审期间),我仍然建议 (1) 的工作流程在生成图之前有一个数据处理脚本将处理后的数据(进入您的绘图)保存到一个文件中,并且 (2) 有一个单独的绘图生成脚本(您可以根据需要进行调整)来重新创建绘图。通过这种方式,您可以为每个绘图快速运行脚本并重新生成它(并使用新数据快速复制绘图设置)。也就是说,腌制数字对于短期/交互式/探索性数据分析可能很方便。

回答by Steve Tjoa

Good question. Here is the doc text from pylab.save:

好问题。这是来自的文档文本pylab.save

pylab no longer provides a save function, though the old pylab function is still available as matplotlib.mlab.save (you can still refer to it in pylab as "mlab.save"). However, for plain text files, we recommend numpy.savetxt. For saving numpy arrays, we recommend numpy.save, and its analog numpy.load, which are available in pylab as np.save and np.load.

pylab 不再提供保存功能,尽管旧的 pylab 函数仍可用作 matplotlib.mlab.save(您仍然可以在 pylab 中将其称为“mlab.save”)。但是,对于纯文本文件,我们建议使用 numpy.savetxt。为了保存 numpy 数组,我们推荐使用 numpy.save 及其模拟 numpy.load,它们在 pylab 中以 np.save 和 np.load 的形式提供。

回答by ptomato

Why not just send the Python script? MATLAB's .fig files require the recipient to have MATLAB to display them, so that's about equivalent to sending a Python script that requires Matplotlib to display.

为什么不直接发送 Python 脚本?MATLAB 的 .fig 文件要求接收者有 MATLAB 来显示它们,所以这大约相当于发送一个需要 Matplotlib 显示的 Python 脚本。

Alternatively (disclaimer: I haven't tried this yet), you could try pickling the figure:

或者(免责声明:我还没有尝试过),您可以尝试酸洗数字:

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

回答by pelson

As of Matplotlib 1.2, we now have experimental picklesupport. Give that a go and see if it works well for your case. If you have any issues, please let us know on the Matplotlib mailing listor by opening an issue on github.com/matplotlib/matplotlib.

从 Matplotlib 1.2 开始,我们现在有实验性的pickle支持。试一试,看看它是否适合您的情况。如果您有任何问题,请通过Matplotlib 邮件列表或在github.com/matplotlib/matplotlib上打开问题告诉我们

回答by Demis

I just found out how to do this. The "experimental pickle support" mentioned by @pelson works quite well.

我刚刚发现如何做到这一点。@pelson 提到的“实验性泡菜支持”效果很好。

Try this:

尝试这个:

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

After your interactive tweaking, save the figure object as a binary file:

交互式调整后,将图形对象保存为二进制文件:

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

Later, open the figure and the tweaks should be saved and GUI interactivity should be present:

稍后,打开图形并保存调整并显示 GUI 交互性:

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

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

You can even extract the data from the plots:

您甚至可以从图中提取数据:

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

(It works for lines, pcolor & imshow - pcolormesh works with some tricks to reconstruct the flattened data.)

(它适用于线条、pcolor 和 imshow - pcolormesh 使用一些技巧来重建扁平数据。)

I got the excellent tip from Saving Matplotlib Figures Using Pickle.

我从使用 Pickle 保存 Matplotlib 图形中得到了一个很好的提示。

回答by Astrum42

I figured out a relatively simple way (yet slightly unconventional) to save my matplotlib figures. It works like this:

我想出了一种相对简单的方法(但有点不合常规)来保存我的 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()))

with function save_plotdefined like this (simple version to understand the logic):

用这样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'

or defining function save_plotlike this (better version using zip compression to produce lighter figure files):

或定义这样的函数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'

This makes use a module libscriptof my own, which mostly relies on modules inspectand ast. I can try to share it on Github if interest is expressed (it would first require some cleanup and me to get started with Github).

这使得使用libscript我自己的模块,它主要依赖于模块inspectast. 如果有兴趣,我可以尝试在 Github 上分享它(首先需要一些清理工作,我才能开始使用 Github)。

The idea behind this save_plotfunction and libscriptmodule is to fetch the python instructions that create the figure (using module inspect), analyze them (using module ast) to extract all variables, functions and modules import it relies on, extract these from the execution context and serialize them as python instructions (code for variables will be like t=[0.0,2.0,0.01]... and code for modules will be like import matplotlib.pyplot as plt...) prepended to the figure instructions. The resulting python instructions are saved as a python script whose execution will re-build the original matplotlib figure.

这个save_plot函数和libscript模块背后的想法是获取创建图形的python指令(使用模块inspect),分析它们(使用模块ast)以提取它所依赖的所有变量、函数和模块,从执行上下文中提取这些并序列化它们作为 python 指令(变量的代码将类似于t=[0.0,2.0,0.01]...,模块的代码将类似于import matplotlib.pyplot as plt...)添加到图形指令之前。生成的 python 指令保存为 python 脚本,其执行将重新构建原始 matplotlib 图。

As you can imagine, this works well for most (if not all) matplotlib figures.

可以想象,这适用于大多数(如果不是全部)matplotlib 图形。