Matplotlib: using a figure object to initialize a plot

matplotlib, plot, python

Solution

You need to understand a bit of architecture of `matplotlib` first (see here for a long article by the founder and current lead developer). At the bottom of the `backend` layer which deals with rendering and talking to the hardware. On top of that layer are `artists` which know how to draw them selves by tell the `backend` object what to do. On top of that layer is the `pyplot` state machine interface which mimics `MATLAB`.

Everything you see in a figure is represented internally as an `Artist` and artists can contain other artists. For example, the `Axes` object keeps track of it's children `Artists` which are the axes spines, tickes, labels, your lines or images etc and `Axes` objects are children of `Figure` objects. When you tell a figure to draw itself (via `fig.canvas.draw()`) all the children artists are drawn recursively.

One draw back of this design is that a given instantiation of an `Artist` can be in exactly one figure (and moving them between figures is hard) so you can't make a `AxesImage` object and then keep reusing it.

This design also separates what `Artists` know about. `Axes` objects know about things like tick location and labels and the display range (which it does by knowing about `Axis` object, but that is getting even more into the weeds). Things like `vmin` and `vmax` are encapsulated in `Normalize` (doc) objects which the `AxesImage` keeps track of. This means that you will need to separate how you deal with everything on your list.

I would suggest either using a factory-like pattern here, or a curry-like pattern

Factory-like:

def set_up_axes(some, arguements):
    '''
    Factory to make configured axes (
    '''
    fig, ax = plt.subplots(1, 1) # or what ever layout you want
    ax.set_*(...)
    return fig, ax


my_norm = matplotlib.colors.Normalize(vmin, mmax) # or write a factory to do fancier stuff
fig, ax = set_up_axes(...)
ax.imshow(..., norm=my_norm)
fig2, ax2 = set_up_axes(...)
ax2.imshow(..., norm=mynorm)

You can wrap up a whole set of kwargs to easily re-use them as such:

my_imshow_args = {'extent':[...],
                  'interpolation':'nearest',
                  'norm': my_norm,
                   ...}

ax2.imshow(..., **my_imshow_args)

Curry-like:

def my_imshow(im, ax=None, *args, **kwargs):
    if ax is None:
        ax = plt.gca()
    # do all of your axes set up
    ax.set_xlim(..)

    # set default vmin and vmax
    # you can drop some of these conditionals if you don't want to be
    # able to explicitly override the defaults
    if 'norm' not in kwargs:
        vmin = kwargs.pop('vmin', None)
        vmax = kwargs.pop('vmax', None)
        if vmin is None:
            vmin = default_vmin # or what ever
        if vmax is None:
            vmax = default_vmax
        my_norm = matplotlib.colors.Normalize(vmin, mmax)
        kwargs['norm'] = norm

    # add a similar block for `extent` 
    # or any other kwargs you want to change the default of

    ax.figure.canvas.draw() # if you want to force a re-draw
    return ax.imshow(im, *args, **kwargs)

If you want to be super clever, you can monkey-patch `plt.imshow` with your version

plt.imshow = my_imshow

There is also the rcParams interface which will allow you to change the default values of many bits and pieces of `matplotlib` in a global way.

And yet another way to accomplish this (through `partial`)

Problem

I am building a class of plot tools for a specific experiment. I currently have two plot methods, a static plot using imshow(), and a "movie" format also using imshow() . Both methods and any future methods, get parameters that are the same for any specific plotting method that I might write. I have all those parameters in a config object at the time the plot class is used. I don't want to rewrite code in every plot method. I would like to initialize an object (AxesImage I think) that will have these args set: vmin, vmax, extent_dim, Xlocs, Xlabels, Ylocs, Ylabels. Then I just pass that object to various methods that do some other specific thing. I don't understand how to do this... ``` import matplotlib.pyplot as plt data = data_dict[type] # could be real part of a complex number, phase, or the mag... v_min, v_max = self.get_data_type_scale(data_dict, Type) freq = data_dict['freq'] # essentially sets the aspect of the plot since the x and y resolutions could be different extent_dim = self._get_extent(2) # gets the labels for physical dimensions of the experiment Xlocs,Xlabels,Ylocs,Ylabels = self._get_ticks(5,5,extent_dim) # in the guts of a plot method, the basic idea is the call below. plt.imshow(data[0,:,:],cmap='jet',vmin=v_min,... vmax=v_max,origin='lower', extent = extent_dim) plt.title('Type: %s Freq: %.3e Hz' %(Type,data_dict['freq'][0]) ) plt.xticks(Xlocs, Xlabels) plt.yticks(Ylocs,Ylabels) ```

Original source

Related problems