Python 保存交互式Matplotlib图形

Python 保存交互式Matplotlib图形,python,matplotlib,Python,Matplotlib,是否有办法保存Matplotlib图形,以便重新打开并恢复典型的交互?(如MATLAB中的.fig格式?) 我发现自己多次运行相同的脚本来生成这些交互式图形。或者我向同事发送多个静态PNG文件,以显示绘图的不同方面。我宁愿发送figure对象,让它们自己与之交互。好问题。这是pylab.save中的文档文本: pylab不再提供保存功能,尽管旧的pylab 函数仍然可以作为matplotlib.mlab.save使用(您仍然可以 在pylab中将其称为“mlab.save”)。但是,对于纯文本

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


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

好问题。这是pylab.save中的文档文本:

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

这将是一个很好的特性,但恐怕它没有在Matplotlib中实现,而且由于图形的存储方式,可能很难自己实现

我建议(a)将数据处理与生成地物(用唯一名称保存数据)分开,编写地物生成脚本(加载保存数据的指定文件)并根据需要进行编辑,或者(b)另存为PDF//格式,并在某些奇特的地物编辑器中进行编辑,如(或)

2012年秋季后编辑:正如其他人在下面指出的(尽管这里提到这是公认的答案),Matplotlib自1.2版以来允许您对数字进行pickle。作为,它是一个实验性功能,不支持在一个matplotlib版本中保存地物,在另一个版本中打开地物。从不受信任的源恢复pickle通常也是不安全的


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

为什么不发送Python脚本呢?MATLAB的.fig文件要求收件人使用MATLAB来显示它们,因此这相当于发送一个需要Matplotlib来显示的Python脚本

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

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

从Matplotlib 1.2开始,我们现在有了实验支持。试一试,看看它是否适合你的情况。如果您有任何问题,请在上或通过在上打开一个问题通知我们。

我刚刚了解了如何做到这一点。@pelson提到的“实验性pickle支持”非常有效

试试这个:

# 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-)


我从中得到了极好的提示。

我想出了一个相对简单的方法(但有点非传统)来保存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\u plot
(更好的版本使用zip压缩生成更轻的图形文件):

这就使用了我自己的模块
libscript
,它主要依赖于模块
inspect
ast
。如果有人对此感兴趣,我可以尝试在Github上共享它(首先需要进行一些清理,然后我才能开始使用Github)

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


可以想象,这对大多数(如果不是全部)matplotlib图形都很有效。

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

这里生成的文件可以被任何使用或不使用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")

这将保存pylab对象中的数据,但不允许重新生成地物。正确。我应该澄清,答案不是建议使用
pylab.save
。事实上,从文档文本来看,似乎不应该使用它。是否有任何外部方法发送三维图形?很遗憾,matplotlib图形是不可pickle的,所以这种方法不起作用。在幕后,有太多的C扩展不支持酸洗。我完全同意只发送脚本+数据,不过。。。我想我从来没有真正看到matlab保存的意义。fig,所以我从来没有使用过它们。在我的经验中,从长远来看,向某人发送独立的代码和数据是最容易的
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'
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")