Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib: Grab Single Subplot from Multiple Subplots

I have an application where I have one figure with nine line plot sub-plots (3x3) and I want to let the user select one of the charts and have a small wx Python application open up to allow editing and zooming on the specified sub-plot.

Is it possible to grab all the information from the selected sub-plot, i.e. axis labels, axis formatting, lines, tick sizes, tick labels, etc and plot it quickly on the canvas of the wx application?

My current solution is too long and bulky, as I just re-do the plot that the user selects. I was thinking something like this, but it doesn't work quite right.

#ax is a dictionary containing each instance of the axis sub-plot
selected_ax = ax[6]
wx_fig = plt.figure(**kwargs)
ax = wx_fig.add_subplots(111)
ax = selected_ax
plt.show()

Is there a way to save the properties from getp(ax) to a variable and use selected properties of that variable with setp(ax) to construct a new chart? I feel this data must be accessible somehow, given how quickly it prints when you call getp(ax), but I can't even get the following code to work on an axis with two y-axes:

label = ax1.yaxis.get_label()
ax2.yaxis.set_label(label)

I have a feeling this isn't possible, but I thought I would ask anyways.

like image 683
hotshotiguana Avatar asked Jan 25 '12 23:01

hotshotiguana


2 Answers

Unfortunately, cloning an axis or sharing artists between multiple axes is difficult in matplotlib. (Not completely impossible, but re-doing the plot will be simpler.)

However, what about something like the following?

When you left-click on a subplot, it will occupy the entire figure, and when you right-click, you'll "zoom out" to show the rest of the subplots...

import matplotlib.pyplot as plt

def main():
    fig, axes = plt.subplots(nrows=2, ncols=2)
    for ax, color in zip(axes.flat, ['r', 'g', 'b', 'c']):
        ax.plot(range(10), color=color)
    fig.canvas.mpl_connect('button_press_event', on_click)
    plt.show()

def on_click(event):
    """Enlarge or restore the selected axis."""
    ax = event.inaxes
    if ax is None:
        # Occurs when a region not in an axis is clicked...
        return
    if event.button == 1:
        # On left click, zoom the selected axes
        ax._orig_position = ax.get_position()
        ax.set_position([0.1, 0.1, 0.85, 0.85])
        for axis in event.canvas.figure.axes:
            # Hide all the other axes...
            if axis is not ax:
                axis.set_visible(False)
    elif event.button == 3:
        # On right click, restore the axes
        try:
            ax.set_position(ax._orig_position)
            for axis in event.canvas.figure.axes:
                axis.set_visible(True)
        except AttributeError:
            # If we haven't zoomed, ignore...
            pass
    else:
        # No need to re-draw the canvas if it's not a left or right click
        return
    event.canvas.draw()

main()
like image 131
Joe Kington Avatar answered Oct 12 '22 13:10

Joe Kington


Here's a working example that uses a class to store the subplot (axis) selected by the user (the zoomed axis). From the stored axis, you can get all of the information you need. This demo, for example, shows how to zoom and restore the subplot (axis). You can then write other methods in the class that have access to the selected axis to fit your needs.

import matplotlib.pyplot as plt

class Demo(object):
    """ Demo class for interacting with matplotlib subplots """
    def __init__(self):
        """ Constructor for Demo """
        self.zoomed_axis = None
        self.orig_axis_pos = None

    def on_click(self, event):
        """ Zoom or restore the selected subplot (axis) """
        # Note: event_axis is the event in the *un-zoomed* figure
        event_axis = event.inaxes
        if event_axis is None: # Clicked outside of any subplot
            return
        if event.button == 1:  # Left mouse click in a subplot
            if not self.zoomed_axis:
                self.zoomed_axis = event_axis
                self.orig_axis_pos = event_axis.get_position()
                event_axis.set_position([0.1, 0.1, 0.85, 0.85])
                # Hide all other axes
                for axis in event.canvas.figure.axes:
                    if axis is not event_axis:
                        axis.set_visible(False)
            else:
                self.zoomed_axis.set_position(self.orig_axis_pos)
                # Restore all axes
                for axis in event.canvas.figure.axes:
                    axis.set_visible(True)
                self.zoomed_axis = None
                self.orig_axis_pos = None
        else:  # any other event in a subplot
            return
        event.canvas.draw()

    def run(self):
        """ Run the demo """
        fig, axes = plt.subplots(nrows=2, ncols=2)
        for axis, color in zip(axes.flat, ['r', 'g', 'b', 'c']):
            axis.plot(range(10), color=color)
        fig.canvas.mpl_connect('button_press_event', self.on_click)
        plt.show()

def main():
    """ Main driver """
    demo = Demo()
    demo.run()

if __name__ == "__main__":
    main()
like image 28
rmcghee Avatar answered Oct 12 '22 13:10

rmcghee