Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
210 views
in Technique[技术] by (71.8m points)

python - Matplotlib: using a figure object to initialize a plot

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)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...