Given a graph defined in SageMath:
G = Graph({...})
one can call G.show() to open a preview.
But how can I save this picture to file instead?
I'm aware I can do this within the preview dialog,
but I can't see an option to do this from code.
Something like .write('/tmp/file').
https://doc.sagemath.org/html/en/reference/plotting/sage/graphs/graph_plot.html
I can use latex, write the output to a file, and then compile it with LaTeX, but this seems a little round the corner:
https://doc.sagemath.org/html/en/reference/graphs/sage/graphs/graph_latex.html#module-sage.graphs.graph_latex
The result of a call to graph.plot() is a sage Graphics object, which is the fundamental object for manipulating and saving graphics. To save the plot, name the Graphics object and call one of its save methods.
For a complete minimum example:
# In sage session or script
G = graphs.WheelGraph(15)
p = G.plot()
p.save_image("myimage.png") # saves the graph to myimage.png
Related
I’m using rhino.compute to calculate a mesh.
How could I convert the 3dm decoded mesh to an STL file?
Currently, I can only save it as a 3dm:
import compute_rhino3d.Grasshopper as gh
import rhino3dm
output = gh.EvaluateDefinition(definition_path, trees)
mesh = output['values'][0]['InnerTree']['{0}'][0]["data"]
mesh = rhino3dm.CommonObject.Decode(json.loads(mesh))
doc = rhino3dm.File3dm()
doc.Objects.AddMesh(mesh)
doc.Write("model.3dm", version=0)
Thank you very much!
You can use Rhino.RhinoDoc.WriteFile to write to all file types that rhino conventionally supports for exporting.
def ExportStl():
#Path to save File.
filepath = r"C:\Temp\TestExport.stl"
#Create write options to specify file info.
write_options = Rhino.FileIO.FileWriteOptions()
#Export all geometry, not just selected geometry.
write_options.WriteSelectedObjectsOnly = False
#Write File.
result = Rhino.RhinoDoc.ActiveDoc.WriteFile(filepath, write_options)
ExportStl()
In this case, I'm using the Rhino 'RunPythonScript' command with the open ActiveDoc but in your example, you could use doc.WriteFile(filepath, write_options) instead.
When you first run this, there is a .stl export dialogue that has export options. This window can be suppressed to the command line with write_options.SuppressDialogBoxes = True.
Or you can check the 'Always use these settings. Do not show this dialogue again.' option and it will not interrupt export in the future.
Your example suggests you may be working in a headless environment so I'm not sure how these dialogues would be handled in that scenario.
I have a pretty well functioning patch that uses two cameras, and employs cv.jit.track to track an object in "3D".
The console prints out sets of coordinates as the object moves through space (format [x,y,z]). My end goal is to translate this data into 3D modelling software to map a path that the object has travelled. One option for achieving this is a python script like this.
I need to determine how exactly to send the stream of data from the Max console outside of Max. Perhaps this is into a Python application, or a table, or literally a text file. Any suggestions are greatly appreciated!
excuse the code dump:
<pre><code>
----------begin_max5_patcher----------
3299.3oc6cz1aaia9yI+JDL5F1.7L3qhjGv.51ssaeaC31vvgtCExxpIpUVx
UVNuzC2+8IQRESEKISIJkljUihj7XIa879a7gr+xkWrXc1cQ6W38cduy6hK9
kKu3B4aU8FWnguXw1f6BSB1KusEoQ2ls9iKVptTQzcEx2N2KDV+lYGJRhJJt
eWj5KdwBueVeocAEgWGmd06yiBKTWEAvq.K8fLX0uPB4OQq.O7YROrMNs7KT
97A52Ldi7wVhJ+Ae+EGuS0yVdqvp27Wu7xperzYpCMNpi4yjTmPLVpiNcT21
n86CtJ5DxaeTgGv6MPu23FUhP9U+xm2OUhZgJIu.nRslJBPGKUBmcM08F1gs
PBXBZE17ECtziTQDK8vv9IH3oDDsCBBLoDDpGBR.jlTDDdjj.gMcjPWZdWEU
bylnaRh2WLNMONU4iTQrsn4su39jHSb3GxR1rvR0Rn+7a7UQ+wgQkleiiCHv
hUH.h.XB8qREWHDrRPfTAP+BJJ4NRePHnA24CYoE6i+h7AAgqnBJj6W+8H5i
KU8ISC1pXs+o73fjEsv+3SG+6v1nzCKLd5eHHLxLzvIrs3zRkpRt2xwvAY3U
bHlyv0uGujqRnmne0fCV4s3wpcR71ToKtHZqNwhE+sRZ3eEuMx6u+W799RtY
dPE1tr5G+0+wO58ehVGFr06eWDmDWbu2eNp330+wzfcO9y78A6CCVmD48Oyy
ze3E8auamXTbzOSd4MWDk+9nzpGjI+uoHFOg4XT90F4U6mvKNcW4ioWOFiVp
SjtIgH3DXofGBKFAVLYrwtCymtw6KdAIIiySiO2WEQQlRCUL3n7Hzz4N3iwE
qBiRRVmjE9o5u1a1GlmURdFZks4o35pOXXVRVthv.q3XeJyeYi+RfwbJpTav
fBOggozCaSkaeTx1rMMdtqyx2Dk23ACVZ7Cymz0QAO9dYDLrRIss+x7it9pF
eLg.gf7ks9Wler1xdkHk3XgRvqhe.L9L4Y2dh6jZqDwCNCKuqqihu5Z42J5A
ovoBqR7913MEWW8dDp0ge9gnznaBdvGUI9GuONKU9szhVHt9NuJOdSRbZjcR
jl5rjin72zgeNqCq8Z8JSG1+4jN7taiS2jcamQUJum2uMnHO9tyGUVkHOT63
AIofxfzCntETGIchlvPoqCRuZjMDfJqQgIighoCNFJFL+8zQluU4W99n9Swx
BAIVHITeUUZhNRn5nY13q0.imNwdGLlJc8Co6BB+jG1Rk89frIYKLU0REwfK
eG2QiiHSG+H7lUUrjh7RNxpM4AV4AvBhFIHU+R.Ft0Ac1sNLIZu2ltKqrLy8
dPu2lGrI9vdO1T3FrlQnbV.43gK98eRLGxuZMJ4v1fojngRpkM7NVgYyum+v
jr9bK1aup.LidUgYCW6lO+siJaWTpSQ1pIugGZiL+g1pTY+bwpqxCVWkbQUl
Edu8PZ7mOD4AmPcXHU8KcK2FTaWgyuRryscEURlByWDbo+Z3rzCVxy+dvhY3
S6kj.8rnEruD5.aq7uxLe9VGXqWerWgMfsUgtZ0p9Jz.V9h4lKtK3SkEjq92
3bynJJ1mxrYUVws3K.N+CDPvtcUsY9mGIEJMuYkEMJWCVHCPfkkPBDR2ACMT
JDL+kCrKORRvuyIBtr93SIXwHHXxS.AK8peFJhigpDDXDguOfHph9gQCOaH9
7uN5RJ5Cd+ljM+W4qkM9KmjqnVjqPDc3Vt.77mEjb.P7dC1EJ1WfFtoaKY8I
lvRcBy1VlAPwokxEkj38S8nJiTYzR0sNlbRqiOm1KaJ0dOrccTdugbkckr2z
1Ks.KqI43KjzOiT7PAC13jgFdZISYYLiMNJT.ZgM.Ty5CZEUkFR6YdfmfVU5
KlcuzEdq6ofVU6qU4mOb8EMiRYNLTF0fR6c9aoaPX3gs8kP1GRxBFCShHidW
5.YLZSCJyU1721jvqKUX5ew.zk5cNMJc+QnBcfQLQfPLAfhKcvxGrGHevrm1
9zQ6Pnj.obY96ifZohWPTqpXEGH1Irhrtx.XeXd7tBuhay9Nu679idah1Ub8
hyOALL5oZuPc.zgje.EO6Y2Vh7Qw2D48Eml4GJDbJESYCdlsoSYAYSPBQ0jG
k0B4M7DhnrmlDhNi9bVZTk97u06due28kp0e42u3b1oD0JLNJkXzSlR78iLe
OsiWfLkkwn19Drn6ZR7NWZMz3oPh34kgYsHiBGYsID+ut0lHG1x6G+vVpJmF
y7G4rVRdR1cLkz3cNSi3wNOoDx2lmzWTyhGjCTZ07WSyhG4ayS5+OoCidMpC
SetnCOEIOnSFZz4Nfeh5q4DO6rX8tWQEOcTyNKV7bd1YgBzwLEFw.FQeYL5r
Z4HlKdhFcVj3U0nyV6gVYGLhQmERe4M5rZhFoJXXDiNKD+0ZzYmBeePCucVr
DqsIzQu3FXVMQC4vQNvrP3y5AlEobDpVE1QLurPvy44kUGMSSciXIxe4Osr0
JvX9XmVV1raz94x7+xy7P8uXpVPk5gIve3s4X5DzEYWcUR2VimcErzdYUaGA
R8OseqYM77pMoR4yYQU0IO5f4QhpUueSRee1g7vZZqdSQ3cDc2DsuHNMnPWW
z6NtwYLtoswaTApzqXffJWRWtz30moFooFPUGkHxDOj2oD5XwxUU61szaZID
5.HDCnSQVfSHqOwFj0uOjkUlVSE5oVU8ZjkHSJ1MFIyYbqFaPMXjnYPpKmey
yhsUiNkmq7k5ujdeRxoRy4GU0Z3e9GkjzQN9np1aEmWZ2TkXprXY1ZwBOmhF
Aa55oIDWs6R8UlKUPSt0rM5f9v9rXPP4PVAUCdlIjqpRTavMhMdZTVylPtha
1n6UUDxY4aHjJco5wosAjZrrfHgZviHNi2UMG3r3MsW4MlBpvFBTuomgq3mb
RaUcMW8kis.SIr9vTAjafolPtha13OkzKWjCk5hPUQQZmARHW8JBbF2pNl6l
GbyFqFDvh3y50dR6nrIDV0zDBeE6waHkpKM0tPsJL9YTF7QyCCG5thZq7Qs+
ItZqc+HnoWswYhP6AEpp0oF0ZLImSUVcVfq85zBWlz9o765cRkv.wcCOIthn
Hg+Jxi20nSOhZSlVbaj89JWEpS0xlPpQrApNFOZ.nFbk1kIyP5XBmEJ.ngh9
SrX.BGfbv.ZpsAcV0tF4floeIgNEUQtYFBbVhqRGa5k31vEQnA30UGqtADog
GYR6djcylhZSYDTlEIcPwsLm6ceMWSxzF7Fws.uwpy3Oc74lPv5txympn0xN
tAOGdi6MATXKaBCrZ3yYTEMwYSCldVNrMsKRO+HlPtVnF2BtHF2u4GTZ+oOr
zD5rieDD1P7KgbEwQSESkPMypuIDVsln0LbAvckAa7Di41T2jZKUUWgGXFZB
qUHKAXOxZBM4oDaSVMP9fKBQzccdBe20ispQlhAi0pI.oyK4HVaS8d9Ct7C8
n810kbsWZ1vnGLNy5Ny8Iv8Lyp715u+ekwZ8OYmFxzqFWWWyUdsUUXzqiip+
Od3TbizCdSbGuoBqWVAX2wCk937UKnmtkQD26EL0FitdiTqRowu9bRQsrXMf
n0y.C+THhf2IuWdsIOiZt0BiyQxP0L.dDXpC9PsovUeaZ4HkC6N6+SulqJUV
Eg2u+7+j56T8RJX.4JtYy5VZQWJ35yfgG.lbguUK6o8XppT61wT2LoH13eCi
NuEkVZa.3Za.rI7LUXihnZZFMgbE2rpD4dWgPJQVpAfazzulPXlpigbUwTL2
S3gXUcmzyi1XlYuzjPOEsZmZeSev.id9f060BkOehAN+XiI77SDD6IBEp1wx
EPbqPNgMMKoWqKeJ7gFhTo6pyUuIjpyOJ+WXt6qiL1lp7obK7Wo2Sjzoxm.1
4DbaOIVT2IYMEtYwVnGHQaX2MB1uaztzUl.NCnsUpAVXooWzE0ljnIfYRBzY
HzqMjfMNKHbCewD9LzGEpygMpiT3CLia36d2yfTa7i0Oaj32rouT1wHb7IKB
GzFGDXgMbQ8Yirpe5MgzKIt1iqDxU71F8TnMRejOwXsOaBgQlK4EFMCIkaGg
fG.gX.M4C2AzVjEdNjUUydMWuIDSePcH0VjPecHDaVNODnAWGLGpH.ab0Q3m
OCYNB1xIXXWWxUST.wpBNsHydFm2Ed2xkbFuwVg2VTHU0+GS0Ede5kZf2p8C
Pvtc2DkWu4lkn7hsAeTs6k4KkfwoJP4dRXQdzMM2LzKBxCuNtHJr3PtZqRdm
9+2UWTse0ySODqUQKYVWpOaoezdP3gcYoZej7SQIIY2Ve7aWxf9Pvgjhlr0f
vvnzhla5dDExjahcNjK2M6.pfRnM210la.z2IOzqqNlw40LmkZYYd429wiAa
MlrsDMhq8NXJ6OR.xsfc0Al8fQelOgAjmT.TABRkTBD.E90aG+gifJgrbKrT
gg62oO3Bj6zkK+0K+e.gM30E
-----------end_max5_patcher-----------
</code></pre>
Is there a way to save a Matplotlib figure such that it can be re-opened and have typical interaction restored? (Like the .fig format in MATLAB?)
I find myself running the same scripts many times to generate these interactive figures. Or I'm sending my colleagues multiple static PNG files to show different aspects of a plot. I'd rather send the figure object and have them interact with it themselves.
I just found out how to do this. The "experimental pickle support" mentioned by #pelson works quite well.
Try this:
# Plot something
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([1,2,3],[10,-10,30])
After your interactive tweaking, save the figure object as a binary file:
import pickle
pickle.dump(fig, open('FigureObject.fig.pickle', 'wb')) # This is for Python 3 - py2 may need `file` instead of `open`
Later, open the figure and the tweaks should be saved and GUI interactivity should be present:
import pickle
figx = pickle.load(open('FigureObject.fig.pickle', 'rb'))
figx.show() # Show the figure, edit it, etc.!
You can even extract the data from the plots:
data = figx.axes[0].lines[0].get_data()
(It works for lines, pcolor & imshow - pcolormesh works with some tricks to reconstruct the flattened data.)
I got the excellent tip from Saving Matplotlib Figures Using Pickle.
As of Matplotlib 1.2, we now have experimental pickle support. Give that a go and see if it works well for your case. If you have any issues, please let us know on the Matplotlib mailing list or by opening an issue on github.com/matplotlib/matplotlib.
This would be a great feature, but AFAIK it isn't implemented in Matplotlib and likely would be difficult to implement yourself due to the way figures are stored.
I'd suggest either (a) separate processing the data from generating the figure (which saves data with a unique name) and write a figure generating script (loading a specified file of the saved data) and editing as you see fit or (b) save as PDF/SVG/PostScript format and edit in some fancy figure editor like Adobe Illustrator (or Inkscape).
EDIT post Fall 2012: As others pointed out below (though mentioning here as this is the accepted answer), Matplotlib since version 1.2 allowed you to pickle figures. As the release notes state, it is an experimental feature and does not support saving a figure in one matplotlib version and opening in another. It's also generally unsecure to restore a pickle from an untrusted source.
For sharing/later editing plots (that require significant data processing first and may need to be tweaked months later say during peer review for a scientific publication), I still recommend the workflow of (1) have a data processing script that before generating a plot saves the processed data (that goes into your plot) into a file, and (2) have a separate plot generation script (that you adjust as necessary) to recreate the plot. This way for each plot you can quickly run a script and re-generate it (and quickly copy over your plot settings with new data). That said, pickling a figure could be convenient for short term/interactive/exploratory data analysis.
Why not just send the Python script? MATLAB's .fig files require the recipient to have MATLAB to display them, so that's about equivalent to sending a Python script that requires Matplotlib to display.
Alternatively (disclaimer: I haven't tried this yet), you could try pickling the figure:
import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
Good question. Here is the doc text from pylab.save:
pylab no longer provides a save function, though the old pylab
function is still available as matplotlib.mlab.save (you can still
refer to it in pylab as "mlab.save"). However, for plain text
files, we recommend numpy.savetxt. For saving numpy arrays,
we recommend numpy.save, and its analog numpy.load, which are
available in pylab as np.save and np.load.
I figured out a relatively simple way (yet slightly unconventional) to save my matplotlib figures. It works like this:
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()))
with function save_plot defined like this (simple version to understand the logic):
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'
or defining function save_plot like this (better version using zip compression to produce lighter figure files):
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'
This makes use a module libscript of my own, which mostly relies on modules inspect and ast. I can try to share it on Github if interest is expressed (it would first require some cleanup and me to get started with Github).
The idea behind this save_plot function and libscript module is to fetch the python instructions that create the figure (using module inspect), analyze them (using module ast) to extract all variables, functions and modules import it relies on, extract these from the execution context and serialize them as python instructions (code for variables will be like t=[0.0,2.0,0.01] ... and code for modules will be like import matplotlib.pyplot as plt ...) prepended to the figure instructions. The resulting python instructions are saved as a python script whose execution will re-build the original matplotlib figure.
As you can imagine, this works well for most (if not all) matplotlib figures.
If you are looking to save python plots as an interactive figure to modify and share with others like MATLAB .fig file then you can try to use the following code. Here z_data.values is just a numpy ndarray and so you can use the same code to plot and save your own data. No need of using pandas then.
The file generated here can be opened and interactively modified by anyone with or without python just by clicking on it and opening in browsers like Chrome/Firefox/Edge etc.
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")
I have an in-house distributed computing library that we use all the time for parallel computing jobs. After the processes are partitioned, they run their data loading and computation steps and then finish with a "save" step. Usually this involved writing data to database tables.
But for a specific task, I need the output of each process to be a .png file with some data plots. There are 95 processes in total, so 95 .pngs.
Inside of my "save" step (executed on each process), I have some very simple code that makes a boxplot with matplotlib's boxplot function and some code that uses savefig to write it to a .png file that has a unique name based on the specific data used in that process.
However, I occasionally see output where it appears that two or more sets of data were written into the same output file, despite the unique names.
Does matplotlib use temporary file saves when making boxplots or saving figures? If so, does it always use the same temp file names (thus leading to over-write conflicts)? I have run my process using strace and cannot see anything that obviously looks like temp file writing from matplotlib.
How can I ensure that this will be threadsafe? I definitely want to conduct the file saving in parallel, as I am looking to expand the number of output .pngs considerably, so the option of first storing all the data and then just serially executing the plot/save portion is very undesirable.
It's impossible for me to reproduce the full parallel infrastructure we are using, but below is the function that gets called to create the plot handle, and then the function that gets called to save the plot. You should assume for the sake of the question that the thread safety has nothing to do with our distributed library. We know it's not coming from our code, which has been used for years for our multiprocessing jobs without threading issues like this (especially not for something we don't directly control, like any temp files from matplotlib).
import pandas
import numpy as np
import matplotlib.pyplot as plt
def plot_category_data(betas, category_name):
"""
Function to organize beta data by date into vectors and pass to box plot
code for producing a single chart of multi-period box plots.
"""
beta_vector_list = []
yms = np.sort(betas.yearmonth.unique())
for ym in yms:
beta_vector_list.append(betas[betas.yearmonth==ym].Beta.values.flatten().tolist())
###
plot_output = plt.boxplot(beta_vector_list)
axs = plt.gcf().gca()
axs.set_xticklabels(betas.FactorDate.unique(), rotation=40, horizontalalignment='right')
axs.set_xlabel("Date")
axs.set_ylabel("Beta")
axs.set_title("%s Beta to BMI Global"%(category_name))
axs.set_ylim((-1.0, 3.0))
return plot_output
### End plot_category_data
def save(self):
"""
Make calls to store the plot to the desired output file.
"""
out_file = self.output_path + "%s.png"%(self.category_name)
fig = plt.gcf()
fig.set_figheight(6.5)
fig.set_figwidth(10)
fig.savefig(out_file, bbox_inches='tight', dpi=150)
print "Finished and stored output file %s"%(out_file)
return None
### End save
In your two functions, you're calling plt.gcf(). I would try generating a new figure every time you plot with plt.figure() and referencing that one explicitly so you skirt the whole issue entirely.
Is there a way to save a Matplotlib figure such that it can be re-opened and have typical interaction restored? (Like the .fig format in MATLAB?)
I find myself running the same scripts many times to generate these interactive figures. Or I'm sending my colleagues multiple static PNG files to show different aspects of a plot. I'd rather send the figure object and have them interact with it themselves.
I just found out how to do this. The "experimental pickle support" mentioned by #pelson works quite well.
Try this:
# Plot something
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.plot([1,2,3],[10,-10,30])
After your interactive tweaking, save the figure object as a binary file:
import pickle
pickle.dump(fig, open('FigureObject.fig.pickle', 'wb')) # This is for Python 3 - py2 may need `file` instead of `open`
Later, open the figure and the tweaks should be saved and GUI interactivity should be present:
import pickle
figx = pickle.load(open('FigureObject.fig.pickle', 'rb'))
figx.show() # Show the figure, edit it, etc.!
You can even extract the data from the plots:
data = figx.axes[0].lines[0].get_data()
(It works for lines, pcolor & imshow - pcolormesh works with some tricks to reconstruct the flattened data.)
I got the excellent tip from Saving Matplotlib Figures Using Pickle.
As of Matplotlib 1.2, we now have experimental pickle support. Give that a go and see if it works well for your case. If you have any issues, please let us know on the Matplotlib mailing list or by opening an issue on github.com/matplotlib/matplotlib.
This would be a great feature, but AFAIK it isn't implemented in Matplotlib and likely would be difficult to implement yourself due to the way figures are stored.
I'd suggest either (a) separate processing the data from generating the figure (which saves data with a unique name) and write a figure generating script (loading a specified file of the saved data) and editing as you see fit or (b) save as PDF/SVG/PostScript format and edit in some fancy figure editor like Adobe Illustrator (or Inkscape).
EDIT post Fall 2012: As others pointed out below (though mentioning here as this is the accepted answer), Matplotlib since version 1.2 allowed you to pickle figures. As the release notes state, it is an experimental feature and does not support saving a figure in one matplotlib version and opening in another. It's also generally unsecure to restore a pickle from an untrusted source.
For sharing/later editing plots (that require significant data processing first and may need to be tweaked months later say during peer review for a scientific publication), I still recommend the workflow of (1) have a data processing script that before generating a plot saves the processed data (that goes into your plot) into a file, and (2) have a separate plot generation script (that you adjust as necessary) to recreate the plot. This way for each plot you can quickly run a script and re-generate it (and quickly copy over your plot settings with new data). That said, pickling a figure could be convenient for short term/interactive/exploratory data analysis.
Why not just send the Python script? MATLAB's .fig files require the recipient to have MATLAB to display them, so that's about equivalent to sending a Python script that requires Matplotlib to display.
Alternatively (disclaimer: I haven't tried this yet), you could try pickling the figure:
import pickle
output = open('interactive figure.pickle', 'wb')
pickle.dump(gcf(), output)
output.close()
Good question. Here is the doc text from pylab.save:
pylab no longer provides a save function, though the old pylab
function is still available as matplotlib.mlab.save (you can still
refer to it in pylab as "mlab.save"). However, for plain text
files, we recommend numpy.savetxt. For saving numpy arrays,
we recommend numpy.save, and its analog numpy.load, which are
available in pylab as np.save and np.load.
I figured out a relatively simple way (yet slightly unconventional) to save my matplotlib figures. It works like this:
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()))
with function save_plot defined like this (simple version to understand the logic):
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'
or defining function save_plot like this (better version using zip compression to produce lighter figure files):
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'
This makes use a module libscript of my own, which mostly relies on modules inspect and ast. I can try to share it on Github if interest is expressed (it would first require some cleanup and me to get started with Github).
The idea behind this save_plot function and libscript module is to fetch the python instructions that create the figure (using module inspect), analyze them (using module ast) to extract all variables, functions and modules import it relies on, extract these from the execution context and serialize them as python instructions (code for variables will be like t=[0.0,2.0,0.01] ... and code for modules will be like import matplotlib.pyplot as plt ...) prepended to the figure instructions. The resulting python instructions are saved as a python script whose execution will re-build the original matplotlib figure.
As you can imagine, this works well for most (if not all) matplotlib figures.
If you are looking to save python plots as an interactive figure to modify and share with others like MATLAB .fig file then you can try to use the following code. Here z_data.values is just a numpy ndarray and so you can use the same code to plot and save your own data. No need of using pandas then.
The file generated here can be opened and interactively modified by anyone with or without python just by clicking on it and opening in browsers like Chrome/Firefox/Edge etc.
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")