有没有办法保存Matplotlib图,以便可以重新打开并恢复典型的交互? (就像MATLAB中的.fig格式一样?)
我发现自己多次运行相同的脚本来生成这些交互式数据。或者我正在向我的同事发送多个静态PNG文件以显示情节的不同方面。我宁愿发送图形对象并让它们自己与它进行交互。
答案 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
,它主要依赖于模块inspect
和ast
。如果表达兴趣,我可以尝试在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")