问题:保存交互式Matplotlib图形
有没有一种方法可以保存Matplotlib图形,以便可以重新打开它并恢复典型的交互作用?(就像MATLAB中的.fig格式一样?)
我发现自己多次运行相同的脚本来生成这些交互式图形。或者我要向同事发送多个静态PNG文件,以显示绘图的不同方面。我宁愿发送图形对象,并让它们自己与之交互。
回答 0
这将是一个很棒的功能,但是AFAIK并没有在Matplotlib中实现,并且由于存储数据的方式可能很难实现自己。
我建议(a)将数据处理与生成图形分开(以唯一的名称保存数据),然后编写图形生成脚本(加载已保存数据的指定文件)并根据需要进行编辑或(b )另存为PDF / SVG / PostScript格式,并在某些精美的图形编辑器(如Adobe Illustrator(或Inkscape))中进行编辑。
编辑后,2012年秋季:正如其他人在下面指出的(尽管在此提及,因为这是公认的答案),自1.2版以来,Matplotlib允许您腌制人物。如发行说明所述,这是一项实验性功能,不支持在一个matplotlib版本中保存图形并在另一个matplotlib版本中打开图形。从不受信任的来源恢复泡菜通常也是不安全的。
对于共享/以后的编辑图(需要首先进行大量数据处理,并且可能需要在几个月后进行调整,例如在科学出版物的同行评审中进行调整),我仍然建议(1)的工作流程具有数据处理脚本,该脚本在生成图之前将处理后的数据(放入绘图中)保存到文件中,并且(2)具有单独的绘图生成脚本(您可以根据需要进行调整)以重新创建绘图。通过这种方式,您可以为每个绘图快速运行脚本并重新生成脚本(并使用新数据快速复制绘图设置)。话虽如此,腌制一个人物可能会方便短期/互动/探索性数据分析。
回答 1
我刚刚发现了如何做到这一点。@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可使用一些技巧来重建展平的数据。)
我从使用Pickle保存Matplotlib图形获得了出色的技巧。
回答 2
从Matplotlib 1.2开始,我们现在具有实验性的pickle支持。试一试,看看它是否适合您的情况。如果您有任何问题,请通过Matplotlib邮件列表或通过在github.com/matplotlib/matplotlib上打开问题来告知我们。
回答 3
为什么不发送Python脚本呢?MATLAB的.fig文件要求收件人具有MATLAB才能显示它们,因此,这等效于发送需要Matplotlib显示的Python脚本。
或者(免责声明:我还没有尝试过),您可以尝试腌制该图:
import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
回答 4
好问题。这是来自的文档文本pylab.save
:
pylab不再提供保存功能,尽管旧的pylab函数仍然可以作为matplotlib.mlab.save使用(您仍然可以在pylab中将其称为“ mlab.save”)。但是,对于纯文本文件,我们建议使用numpy.savetxt。为了保存numpy数组,我们建议使用numpy.save及其类似的numpy.load,它们可以在pylab中以np.save和np.load的形式提供。
回答 5
我想出了一种相对简单的方法(但还有些不常规)来保存我的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指令(使用module inspect
),对其进行分析(使用module ast
)以提取依赖于其的所有变量,函数和模块,从执行上下文中提取这些变量并对其进行序列化如python指令(变量的代码将类似于t=[0.0,2.0,0.01]
…,模块的代码将类似于import matplotlib.pyplot as plt
…)附加在该图指令之前。生成的python指令将另存为python脚本,其执行将重新构建原始的matplotlib图。
可以想象,这对于大多数(如果不是全部)matplotlib图形都适用。