2

I am writing a program in Python that generates many graphs. Some of these are interesting both standalone and also in comparison to other graphs. Generating these graphs is expensive (in terms of runtime) and I don't want to generate them more than once. Is there any way to generate a plot once, and the have it be part of a subplot?

I'm basically looking for an alternative to this:

#generate standalone graphs
pylab.figure()
generate_plot0()
pylab.figure()
generate_plot1()

#generate subplot
pylab.figure()
subplot(121)
generate_plot0()
subplot(122)
generate_plot1()

But without calling generate_plot0() and generate_plot1() twice.

Is there a good way to do it?

Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319

1 Answers1

4

Generally speaking, matplotlib artists can't be in more than one axes, and axes can't be in more than one figure. (In some cases, you can break some of these rules, but it won't work in general.)

Therefore, the short answer is no.

However, you might consider something like the following. You can have the plot in question as a subplot, than then bind a click/keypress/whatever to hide all of the other subplots and make the selected axes temporarily fill up the entire figure.

As a quick example:

import numpy as np
import matplotlib.pyplot as plt

def main():
    subplots = ZoomingSubplots(2, 2)
    colors = ['red', 'green', 'blue', 'cyan']
    for ax, color in zip(subplots.axes.flat, colors):
        data = (np.random.random(200) - 0.5).cumsum()
        ax.plot(data, color=color)
    subplots.fig.suptitle('Click on an axes to make it fill the figure.\n'
                 'Click again to restore it to its original position')
    plt.show()

class ZoomingSubplots(object):
    def __init__(self, *args, **kwargs):
        """All parameters passed on to 'subplots`."""
        self.fig, self.axes = plt.subplots(*args, **kwargs)
        self._zoomed = False
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def zoom(self, selected_ax):
        for ax in self.axes.flat:
            ax.set_visible(False)
        self._original_size = selected_ax.get_position()
        selected_ax.set_position([0.125, 0.1, 0.775, 0.8])
        selected_ax.set_visible(True)
        self._zoomed = True

    def unzoom(self, selected_ax):
        selected_ax.set_position(self._original_size)
        for ax in self.axes.flat:
            ax.set_visible(True)
        self._zoomed = False

    def on_click(self, event):
        if event.inaxes is None:
            return
        if self._zoomed:
            self.unzoom(event.inaxes)
        else:
            self.zoom(event.inaxes)
        self.fig.canvas.draw()

if __name__ == '__main__':
    main()

Initial state

enter image description here

After clicking on a subplot

enter image description here

Joe Kington
  • 275,208
  • 71
  • 604
  • 463