Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib - Fast way to create many subplots?

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
like image 613
DanHickstein Avatar asked Jan 06 '14 16:01

DanHickstein


People also ask

How do I make multiple subplots in matplotlib?

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.

What will PLT subplot 333 do?

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.

How do I plot multiple things in matplotlib?

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.

Which method provides a way to plot multiple plots on a single figure?

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 .


1 Answers

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.

like image 174
Joe Kington Avatar answered Oct 21 '22 07:10

Joe Kington