54

I work in an psudo-operational environment where we make new imagery on receipt of data. Sometimes when new data comes in, we need to re-open an image and update that image in order to create composites, add overlays, etc. In addition to adding to the image, this requires modification of titles, legends, etc.

Is there something built into matplotlib that would let me store and reload my matplotlib.pyplot object for later use? It would need to maintain access to all associated objects including figures, lines, legends, etc. Maybe pickle is what I'm looking for, but I doubt it.

Vorticity
  • 4,582
  • 4
  • 32
  • 49
  • can't you simply put the figures into a list? – Ruggero Turra Sep 03 '11 at 22:21
  • 11
    This would be a great feature (Matlab has it). I think, though, the answer is no, and the question is a duplicate http://stackoverflow.com/questions/4348733/saving-interactive-matplotlib-figures. – tom10 Sep 04 '11 at 17:32
  • 1
    That works if you always have your data available. Here is my situation in timeline form. 1) Receive data from one satellite 2) Make multiple plots from data with associated legends, titles, etc. 3) Save figures for display on webpage 4) Get new data from different satellite that is needed for an overlay to the previous product (think satellite derived wind barbs overlain on satellite imagery) – Vorticity Sep 04 '11 at 19:42
  • 5) Need to retrieve an object containing all of the original matplotlib.pyplot information so that a new figure can be created and relevant parts updated (legends, titles, etc) The problem here is that the data from the second satellite may come in up to a few hours before or after the data from the first satellite. Since I can't have a process continually running to keep updating the figure, I need to figure out a way to save and update the matplotlib.pyplot object. – Vorticity Sep 04 '11 at 19:49
  • @tom10: Thanks for pointing out the duplicate. This could be really problematic for me, though...maybe I'll have to look into the matplotlib source and see if there is something that can be done about this problem. I'm guessing that my python skills are not quite up to snuff for adding a feature like this, though. – Vorticity Sep 04 '11 at 19:51
  • a matplotlib plot image is made of data and code. Why not passing around both? Then you can reprocess the data, the plot characteristics or both. – joaquin Oct 02 '11 at 19:18
  • Pickle is not what you're looking for, as it breaks on axis objects. – zefciu Oct 11 '11 at 11:21
  • @joaquin - I'm not sure that I understand what you mean. I can always go back and re-process the data, but that seems to be an unnessicary amount of work and CPU time when there might be a way to re-load a prebuilt matplotlib object and add more data to it. The problem, though, is that there appears to be no way to do this. – Vorticity Oct 11 '11 at 22:28
  • @zefciu - Yeah, that's pretty much what I found out later on... I wish there was a way to easily serialize a matplotlib object, but I can't seem to find it. I've given up on matplotlib after running into a few more roadblocks, though, and will be instead using a big hammer. I'll be using GDAL, a GIS package with python bindings. – Vorticity Oct 11 '11 at 22:30
  • Glad you found a bigger hammer. But I would second joaquin's suggestion - save code that generates a plot along with the input data. If there are time-consuming steps (e.g. large image processing followed by downsampling) save the output state (small image) and discard things that came before it in the processing pipeline. At some point (e.g. rendering text as pixels and flattening them into the image layer) the text can no longer be modified, so you'd have to save state before that step. – Dave Oct 18 '11 at 15:47
  • Sorry for the slow response here. The solution that I have come up with is this: I create a figure that is exactly the size of the final image that I want without borders, legends, etc. Then do `fig.add_axes([0,0,1,1])` and use imshow to add my data to the axes. Then save the figure in a scratch directory. From there, I can read that image in the next time I need it, add more data, then resave the figure. Once all of the data have been added to the figure (usually a couple of hours) I can read the image, plot it on a larger figure with titles, labels, etc and save that image permanently. – Vorticity Oct 28 '11 at 21:15
  • By the way, GDAL may be something that I will use in the future, but it is not the solution I wanted right now either. I've gone back to simple matplotlib. – Vorticity Oct 28 '11 at 21:18

5 Answers5

60

As of 1.2 matplotlib ships with experimental pickling support. If you come across any issues with it, please let us know on the mpl mailing list or by opening an issue on github.com/matplotlib/matplotlib

HTH

EDIT: Added a simple example

import matplotlib.pyplot as plt
import numpy as np
import pickle

ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
pickle.dump(ax, open('myplot.pickle', 'w'))

Then in a separate session:

import matplotlib.pyplot as plt
import pickle

ax = pickle.load(open('myplot.pickle'))
plt.show()
Community
  • 1
  • 1
pelson
  • 21,252
  • 4
  • 92
  • 99
  • 2
    I'm having problems making pickling work with matplotlib. Is there any sample code showing how to use it? – Pontus Granström Mar 04 '13 at 22:18
  • 1
    I've added an example. If you're having problems after this example, it could be you've found a bug - please do let us know. HTH – pelson Mar 05 '13 at 09:52
  • @pelson - Does pickling not work within a pylab session?? I seem to have no problems when using the above examples or with pyplot format. If I am in a pylab session and generate a plot it always throws a pickling error. Can't pickle : it's not found as __main__.copy_from_bbox. Anyway, thanks for the great feature. Seems very handy and just noticed now it was added as hadn't followed updates recently. – J Spen Aug 01 '13 at 09:38
  • @JSpen - not sure whether it works in pylab. I've never actually used it ;-) . If I replace "import matplotlib.pyplot as plt" with "import pylab as plt" the above example still works for me. What version of mpl are you running? – pelson Aug 02 '13 at 19:40
  • @pelson - current development version: 1.4.x from a couple days ago so real recent. Pylab in ipython basically runs the command: from pylab import *. With that it doesn't work properly. If you do that does it work properly? – J Spen Aug 02 '13 at 19:56
  • @pelson - Ok, I've done some extensive testing. It seems only in ipython qtconsole the error arise. Do this. Run ipython qtconsole --pylab. Then fig = figure(); plot(range(10)); pickle.dump(fig,FILE); The error will come up. Seems only when pylab is activated that the error arises. I think when I loaded it without worked fine. Even normal ipython with the --pylab option seemed to work fine. So maybe the gui wrapper around matplotlib is causing the issue? – J Spen Aug 02 '13 at 20:19
  • @pelson - I think as soon as something is drawn so the plot is shown it causes this actually. If you use matplotlib it is fine (fig = figure(); ax = fig.add_subplot(111); ax.plot(range(10)); pickle.dump(fig, FILE). You can dump this but as soon as you call plt.draw(). Then try to dump again and the error above comes up. – J Spen Aug 02 '13 at 20:24
  • import pickle; from tempfile import TemporaryFile; t = TemporaryFile('w'); import matplotlib; # matplotlib.use('tkagg'); import matplotlib.pyplot as plt; fig = plt.figure(); ax = fig.add_subplot(111); ax.plot(range(10)); plt.show(); pickle.dump(fig, t); # if interactive backend like tkagg activated error thrown here plt.draw(); pickle.dump(fig, t); # if non-interactive backend agg then error thrown here – J Spen Aug 02 '13 at 20:36
  • @pelson - I figured out that the code above actually throws an error in ipython or ipython qtconsole. Basically if the figure is drawn it seems the error is thrown because plt.show() with tkagg would force it to draw the image. Might be better to submit a bug report somewhere if you think this is a bug or fixable. – J Spen Aug 02 '13 at 20:38
  • @JSpen definately encourage you to open a bug report on the matplotlib tracker. It looks like you've put a lot of effort isolating the issue, so I recon finding a fix should be relatively quick. Thanks for all your hard work! – pelson Aug 08 '13 at 15:04
  • This does not work with http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html – denfromufa Sep 24 '14 at 11:51
  • To get this to work I had to remove any call that referenced the draw method. – Lightyear Buzz Sep 10 '15 at 00:19
  • Also had to comment out line 185 of six.py as I was getting import errors from gdbm. – Lightyear Buzz Sep 10 '15 at 00:20
  • 1
    as of Jan 2015, on Py3.5 this works great without many bugs that i've encountered, although I've been saving the `fig`, not the `ax` - not sure what the difference is (I didn't actually know `Axes` had a `show()` method.) – Demis Feb 01 '16 at 19:49
  • 1
    Thanks. But zoom rect option is not working after pickling. Zooming is fine in the original picture, but after loading figure from pickle, everything works fine without zoom option. – Md Kamruzzaman Sarker Jul 09 '17 at 21:42
  • 2
    To re-show the saved figure, I found that creating a dummy figure as described in [this answer](https://stackoverflow.com/questions/31729948/matplotlib-how-to-show-a-figure-that-has-been-closed) helped bring the figure back to life. – MD004 Jul 24 '18 at 20:19
16

A small modification to Pelson's answer for people working on a Jupyterhub

Use %matplotlib notebook before loading the pickle. Using %matplotlib inline did not work for me in either jupyterhub or jupyter notebook. and gives a traceback ending in AttributeError: 'module' object has no attribute 'new_figure_manager_given_figure'.

import matplotlib.pyplot as plt
import numpy as np
import pickle

%matplotlib notebook

ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
with open('myplot.pkl','wb') as fid:
    pickle.dump(ax, fid)

Then in a separate session:

import matplotlib.pyplot as plt
import pickle

%matplotlib notebook

with open('myplot.pkl','rb') as fid:
    ax = pickle.load(fid)
plt.show()
Ben Bowen
  • 161
  • 1
  • 3
  • In JulpyterLab, %matplotlib notebook did not work, but %matplotlib inline worked for me. – stok Oct 05 '21 at 20:09
1

Tested in jupyter notebook.

import matplotlib.pyplot as plt
import numpy as np
import pickle

fig, axes = plt.subplots(figsize=(20, 5),nrows=1, ncols=2) 
x,y = np.arange(10), np.random.random(10)
axes[0].plot(x,y)
axes[1].plot(x,y)
plt.show()

# myfname = input("Save figure? [enter filename]: ") 
myfname = 'test'
if (myfname!=''):
    fig.savefig(f'./{myfname}.png')
    print(f'file saved to ./{myfname}.png')
    with open(f'./{myfname}.png.pkl','wb') as fid:
        pickle.dump(fig, fid)
        print(f'pickled to ./{myfname}.png.pkl') 

###################################
####### in a separate session
myfname = 'test'
with open(f'./{myfname}.png.pkl', 'rb') as fh:
    fig_loaded = pickle.load(fh)

print(fig_loaded.axes[0].lines[0].get_data()) # get data
fig_loaded # show fig
Sida Zhou
  • 3,529
  • 2
  • 33
  • 48
1

I produced figures for a number of papers using matplotlib. Rather than thinking of saving the figure (as in MATLAB), I would write a script that plotted the data then formatted and saved the figure. In cases where I wanted to keep a local copy of the data (especially if I wanted to be able to play with it again) I found numpy.savez() and numpy.load() to be very useful.

At first I missed the shrink-wrapped feel of saving a figure in MATLAB, but after a while I have come to prefer this approach because it includes the data in a format that is available for further analysis.

Tim D
  • 1,645
  • 1
  • 25
  • 46
  • You can actually extract data from a saved `Figure`/`Axis` object, just like in Matlab. See `Line.get_data()` (and the `Lines` are stored in the `Axis`, which is stored in the `Figure`). I used to do this in Matlab quite a lot. – Demis Feb 01 '16 at 20:09
0

Did you try the pickle module? It serialises an object, dumps it to a file, and can reload it from the file later.

Andrew
  • 897
  • 1
  • 9
  • 19
  • I don't think that pickle is able to serialise matplotlib objects. It seems to fail. One of the comments above mentions that pickle breaks axis objects (and likely other matplotlib objects besides). – Vorticity Oct 28 '11 at 21:16
  • 1
    I don't think pickle serialises nested objects by default. Maybe this hack will help: http://stackoverflow.com/questions/1947904/how-can-i-pickle-a-nested-class-in-python – Andrew Nov 12 '11 at 19:27