保存交互式Matplotlib数字

时间:2010-12-03 18:41:12

标签: python matplotlib

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

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

7 个答案:

答案 0 :(得分:43)

我刚刚发现了如何做到这一点。 "实验性泡菜支持" @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 works with some tricks to reconstruct the flattened data。)

我从 Saving Matplotlib Figures Using Pickle 获得了很好的提示。

答案 1 :(得分:38)

从Matplotlib 1.2开始,我们现在获得了实验pickle支持。试一试,看看它是否适用于您的情况。如果您有任何问题,请通过Matplotlib mailing list或在github.com/matplotlib/matplotlib上打开问题告诉我们。

答案 2 :(得分:22)

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

我建议(a)单独处理数据以生成图形(使用唯一名称保存数据)并编写图形生成脚本(加载保存数据的指定文件)并根据需要进行编辑或(b)保存为PDF / SVG / PostScript格式,并在Adobe Illustrator(或Inkscape)等奇特的图形编辑器中进行编辑。

2012年秋季后的EDIT :正如其他人在下面指出的那样(虽然这里提到这是接受的答案),自1.2版以来,Matplotlib已经允许你腌制数字。作为release notes state,它是一个实验性功能,不支持在一个matplotlib版本中保存图形并在另一个版本中打开。从不受信任的来源恢复泡菜通常也是不安全的。

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

答案 3 :(得分:7)

为什么不直接发送Python脚本? MATLAB的.fig文件要求收件人让MATLAB显示它们,这相当于发送一个需要Matplotlib显示的Python脚本。

或者(免责声明:我还没有尝试过这个),你可以尝试腌制这个数字:

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

答案 4 :(得分:1)

好问题。以下是pylab.save的文档文字:

  

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

答案 5 :(得分: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,它主要依赖于模块inspectast。如果表达兴趣,我可以尝试在Github上分享它(它首先需要一些清理,然后我开始使用Github)。

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

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

答案 6 :(得分:0)

如果您希望将 python 绘图保存为交互式图形以修改并与其他人共享,例如 MATLAB .fig 文件,那么您可以尝试使用以下代码。这里 z_data.values 只是一个 numpy ndarray,因此您可以使用相同的代码来绘制和保存您自己的数据。那么就不需要使用熊猫了。

这里生成的文件可以被任何有或没有 python 的人打开和交互修改,只需点击它并在 Chrome/Firefox/Edge 等浏览器中打开。

import plotly.graph_objects as go
import pandas as pd

z_data=pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')

fig = go.Figure(data=[go.Surface(z=z_data.values)])

fig.update_layout(title='Mt Bruno Elevation', autosize=False,
                  width=500, height=500,
                  margin=dict(l=65, r=50, b=65, t=90))

fig.show()
fig.write_html("testfile.html")