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) ```