I'm using matplotlib to create a figure with many small subplots (something like 4 rows, 8 columns). I've tried a few different ways, and the fastest that I can get matplotlib to make the axes is ~2 seconds. I saw this post about just using one axes object to display many small images, but I would like to be able to have ticks and titles on the axes. Is there any way to speed this process up, or does making axes in matplotlib just take a reasonably long time?
Here is what I have tried so far:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import time
fig = plt.figure(figsize=(10,6))
plt.clf(); t = time.time()
grid = ImageGrid(fig, 111,
nrows_ncols = (4, 8))
print 'Time using ImageGrid: %.2f seconds'%(time.time()-t)
plt.clf(); t = time.time()
axes = plt.subplots(4,8)
print 'Time using plt.subplots: %.2f seconds'%(time.time()-t)
plt.clf(); t = time.time()
axes = []
for row in range(0,4):
for col in range(1,9):
ax = plt.subplot(4,8,row*8+col)
axes.append(ax)
print 'Time using ptl.subplot loop: %.2f seconds'%(time.time()-t)
output:
Time using ImageGrid: 4.25 seconds
Time using plt.subplots: 2.11 seconds
Time using ptl.subplot loop: 2.34 seconds
Taking Joe Kington's suggestion, I tried to pickle the figure and axes so that I would at least not need to create them every time that I run the script. However, loading the file actually takes longer:
import matplotlib.pyplot as plt
import pickle
import time
t = time.time()
fig,axes = plt.subplots(4,8,figsize=(10,6))
print 'Time using plt.subplots: %.2f seconds'%(time.time()-t)
pickle.dump((fig,axes),open('fig.p','wb'))
t = time.time()
fig,axes = pickle.load(open('fig.p','rb'))
print 'Time to load pickled figure: %.2f seconds'%(time.time()-t)
output:
Time using plt.subplots: 2.01 seconds
Time to load pickled figure: 3.09 seconds
To create multiple plots use matplotlib. pyplot. subplots method which returns the figure along with Axes object or array of Axes object. nrows, ncols attributes of subplots() method determine the number of rows and columns of the subplot grid.
subplot(333) do? Create a blank plot that fills the figure. Create a plot with three points at location (3,3). Create a smaller subplot in the topleft of the figure.
In Matplotlib, we can draw multiple graphs in a single plot in two ways. One is by using subplot() function and other by superimposition of second graph on the first i.e, all graphs will appear on the same plot.
subplots method provides a way to plot multiple plots on a single figure. Given the number of rows and columns , it returns a tuple ( fig , ax ), giving a single figure fig with an array of axes ax .
You're not going to beat subplots
by any substantial amount. Creating new axes is a fairly expensive operation, and you're creating 32 of them each time. However it's only done once.
Is creating a new figure and axes really your bottleneck? If so, you're probably doing something wrong.
If you're trying to create an animation, just update the artists (e.g. image.set_data
, etc) and redraw rather than making a new figure and axes each time.
(Also, axes = plt.subplots(4, 8)
is incorrect. axes
will be a tuple instead of a sequence of axes objects as you've currently written it. subplots
returns a figure instance and an array of axes. It should be fig, axes = plt.subplots(...)
.)
Just to expand on my comment below about pickling:
It's often handy to cache "expensive" results when you're changing un-related parameters. This is particularly common in scientific "scripts", where you'll often have some fairly slow data parsing or intermediate calculation and a dependent set of parameters that you're semi-interactively "tweaking".
There's nothing wrong with just commenting out a few lines and explicitly saving/loading the pickled results.
However, I've found it handy to keep a decorator around that does this automatically. There are better ways to do this in many cases, but for a one-size-fits-all solution, it's quite handy:
import os
import cPickle as pickle
from functools import wraps
class Cached(object):
def __init__(self, filename):
self.filename = filename
def __call__(self, func):
@wraps(func)
def new_func(*args, **kwargs):
if not os.path.exists(self.filename):
results = func(*args, **kwargs)
with open(self.filename, 'w') as outfile:
pickle.dump(results, outfile, pickle.HIGHEST_PROTOCOL)
else:
with open(self.filename, 'r') as infile:
results = pickle.load(infile)
return results
return new_func
Then you can just do something like (presuming that the class above is in utilities.py
):
import matplotlib.pyplot as plt
from utilities import Cached
@Cached('temp.pkl')
def setup():
fig, axes = plt.subplots(8, 4)
# And perhaps some other data parsing/whatever
return fig, axes
fig, axes = setup()
for ax in axes.flat:
ax.plot(range(10))
plt.show()
The decorated function will only be run if the given filename ("temp.pkl"
) isn't present, and the cached results stored in temp.pkl
will be loaded otherwise. This will work for anything that can be pickled, not just matplotlib figures.
Beware of working with a saved state, though. If you change what setup
does and re-run, the "old" results will be returned!! Be sure to manually delete the cached file if you change what the wrapped function does.
Of course, this is overkill if you're just doing it in one place. It can be handy to have around if you find yourself caching intermediate results frequently, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With