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中实现,而且由于图形的存储方式,可能很难自己实现

我建议将数据处理与生成地物分开,该地物使用唯一名称保存数据,并编写地物生成脚本,加载保存数据的指定文件并按您认为合适的方式进行编辑,或者将数据另存为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_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模块背后的思想是使用module inspect获取创建图形的python指令,使用module ast对其进行分析,以提取它所依赖的所有变量、函数和模块导入,从执行上下文中提取这些变量并将它们序列化为python指令变量的代码将类似于t=[0.0,2.0,0.01]。。。模块的代码将类似于导入matplotlib.pyplot作为plt。。。在图的前面加上说明。生成的python指令保存为python脚本,其执行将重新构建原始matplotlib图


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

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

这里生成的文件可以被任何使用或不使用python的人打开并交互修改,只需单击它并在Chro等浏览器中打开即可 me/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,所以我从来没有使用过它们。根据我的经验,从长远来看,向某人发送独立的代码和数据是最容易的。不过,如果matplotlib的图形对象是可pickle的,那就更好了。即使我们的预处理数据有点大,绘图过程也很复杂。看起来是唯一的办法。谢谢。数字现在显然是可以腌制的——效果相当好!pickle的好处是,您不必以编程方式调整所有地物/子地块间距/位置。您可以使用MPL plot的GUI使图形看起来漂亮,等等,然后保存MyPlot.fig.pickle文件-保留稍后根据需要调整绘图显示的功能。这也是Matlab的.fig文件的优点所在。当您需要更改用于插入演示文稿/论文的无花果的大小/纵横比时,此功能尤其有用。令人惊讶的是,此功能没有实现。。但好的,我将把处理后的数据保存在一个中间文件中,并将其和一个用于打印的脚本发送给同事。谢谢。我怀疑实现起来很难,这就是为什么它在MATLAB中工作得如此糟糕的原因。回到我使用它的时候,曾经使MATLAB崩溃的图形,甚至是稍微不同的版本都无法读取彼此。fig files.pickle现在可以在MPL图形上工作,所以这可以做到,并且看起来工作得相当好-几乎像MATLAB.fig图形文件。现在看到我下面的答案了吗?“请举例说明如何操作。@黛米斯:正如托马托在下面的回答中指出的那样,它在当时就已经存在了。@strpeter-Matlab的数字在2010年是不可酸洗的,正如中所指出的那样。”。增加了实验特征。如前所述,您不应该期望它在matplotlib版本之间工作,也不应该打开来自不受信任源的pickle。任何原因都可以将此有用功能添加到图形的“另存为”中。添加.pkl?好主意@dashesy。如果您想尝试实现它,我会支持这一点。这只适用于后端的子集吗?当我尝试在OSX中pickle一个简单的图形时,我得到PicklingError:Can't pickle:it's not found as _macosx.GraphicsContext。只有在执行pickle之前调用plt.show时,才会出现上述PicklingError。因此,只要将plt.show放在MacOS 10.11上我的py3.5的pickle.dump.之后,fig.show的顺序似乎就无关紧要了——也许这个bug已经修复了。我可以在放映前/放映后进行pickle处理,没有任何问题。我认为这对版本更改等不稳定,并且py2.x和py3.x之间不交叉兼容。我还认为pickle文档说明我们需要像pickle保存对象时一样设置环境,但我发现,当不进行酸洗处理时,您不需要导入MatpTllb. PyPlice作为PLT——它保存了在被腌制的文件中的导入语句。您应该考虑关闭打开的文件:例如,用OpenFigObjor。“rb”作为file:figx=pickle.loadfileI刚得到:'AttributeError:'Figure'对象没有属性'\u cachedEnderRif如果不希望脚本继续,并且可能在figx.show之后立即终止,则应该调用plt.show,这是阻塞。
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")