Matplotlib:使用figure对象初始化绘图

6
我正在为一个特定实验构建一类绘图工具。我目前有两种绘图方法,一种使用imshow()进行静态绘图,另一种使用“电影”格式也使用imshow()。
这两种方法以及任何未来的方法都会得到相同的参数,这些参数对于我可能编写的任何特定绘图方法都是相同的。在绘图类被使用时,我已经将所有这些参数存储在一个配置对象中。
我不想在每个绘图方法中重写代码。我想初始化一个对象(我认为是AxesImage),该对象将设置这些参数:vmin、vmax、extent_dim、Xlocs、Xlabels、Ylocs、Ylabels。
然后我只需将该对象传递给执行其他特定任务的各种方法即可。我不明白如何做到这一点...
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)

你的问题是什么?如果你想初始化一个 figure 对象,那就直接做吧。只需要执行 fig = figure() 就可以了。 - Phillip Cloud
假设我创建了一个指向我的图形的句柄:im = plt.imshow(),那么我可以使用im.set_extent((0,32,0,32))。这是将绘图对象传递给方法的正确方式吗?如何显示绘图?im.show()似乎不起作用。基本上,我如何编写良好的面向对象代码与绘图,其中可以在__init__()中设置多个参数,并将对象引用传递给各种方法。 - wbg
你说的“handle”是什么意思?这听起来像是MATLAB术语,在这里很容易引起混淆,因为Python只有一个东西:对象。在matplotlib中有Axes对象、Figure对象和大量其他对象。im = imshow(randn(10, 10))返回一个AxesImage对象。那么...你到底指的是哪一个对象呢? - Phillip Cloud
句柄有问题...这是旧版Matlab的习惯。我怀疑我指的是AxesImage对象。 - wbg
你介意把你的问题标题改成更具描述性的内容吗,比如“更改pyplot函数参数的默认值”?来玩一下Jeopardy游戏吧 ;) - tacaswell
@tcaswell,我在为问题的名称苦恼。我觉得,是的,我想知道如何修改绘图的属性,但这也涉及到如何使用matplotlib编写面向对象的代码。我不想重复代码,例如每个绘图方法都要调用相同值的轴来更改。我还试图控制和消除某些应该在实验条件构思时就决定的选择。 - wbg
2个回答

13
首先,您需要了解一下matplotlib的架构(请参阅创始人和现任首席开发人员撰写的长文)。在处理渲染和与硬件通信的backend层的底部。在该层之上是artists,它们通过告诉backend对象要做什么来知道如何绘制自己。在该层之上是pyplot 状态机接口,它模仿了MATLAB
图中看到的所有内容都在内部表示为Artist,而且Artists可以包含其他Artists。例如,Axes对象跟踪其子代Artists,这些Artists是轴脊柱、刻度、标签、线条或图像等,Axes对象是Figure对象的子代。当您告诉一个figure去画自己(通过fig.canvas.draw()),所有子代的Artists都会递归地绘制。
这种设计的一个缺点是,给定一个Artist实例可以在一个figure中(并且在不同figure之间移动它们很困难),因此您不能创建一个AxesImage对象,然后不断重用它。
这种设计还将Artists了解的内容分开。例如,Axes对象了解刻度位置和标签以及显示范围(它通过了解Axis对象来实现,但这更加深入)。像vminvmax这样的内容封装在Normalize (doc)对象中,AxesImage跟踪这些对象。这意味着您需要将如何处理列表中的所有内容分开。
我建议在这里使用类似工厂的模式或类似咖喱的模式。
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)

你可以将一整组kwargs打包起来,方便地重复使用它们,示例如下:

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

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

类似于咖喱:

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)

如果你想要更聪明,可以使用猴子补丁将plt.imshow替换为你的版本。
plt.imshow = my_imshow

还有rcParams接口,可以全局更改matplotlib的许多默认值。

通过partial,还有另一种实现此目的的方法。


这非常有帮助... :) 我会一段时间参考这个。非常感谢。 - wbg

2
要显示一个图形,您需要使用Figure类的实例fig调用fig.canvas.draw()fig.canvas.draw()是交互式shell(即pylab)函数draw()的API版本。
如果您需要从AxesImage对象中获取AxesFigure,可以分别调用im.get_axes()im.get_figure()
就编写“好”的面向对象代码而言,用户界面示例可能是一个很好的起点。

挑剔一点,pylab 只是一个用于批量导入许多有用内容的桶,而 pyplot 则是状态机接口。 - tacaswell
我也不太明白这如何回答问题。 - tacaswell
他问如何让图形显示出来。fig.canvas.draw() 不是常用的方法吗? - Phillip Cloud
它解决了评论中提出的问题,而不是原始帖子中的问题。 - tacaswell
好的,非常感谢您澄清! - Phillip Cloud
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接