在matplotlib中子类化Axes

5
标题已经很明确了。然而,由于`matplotlib`的设置方式,不能简单地从`Axes`继承并使其工作。通常情况下,`Axes`对象不会被直接使用,它只是从`subplot`或其他函数中返回。
我想这样做有几个原因。首先,为了减少反复使用类似参数重现图表的次数。像这样:
class LogTemp(plt.Axes):
    """ Axes to display temperature over time, in logscale """
    def __init__(self, *args, **kwargs):
        super.__init__(*args, **kwargs)
        self.set_xlabel("Time (s)")
        self.set_ylabel("Temperature(C)")
        self.set_yscale('log')

这并不难写一个自定义函数,尽管它不够灵活。更大的原因是我想覆盖一些默认行为。举个简单的例子,考虑:
class Negative(plt.Axes):
     """ Plots negative of arguments """
     def plot(self, x, y, *args, **kwargs):
         super().plot(x, -y, *args, **kwargs)

或者
class Outliers(plt.Axes):
     """ Highlight outliers in plotted data """
     def plot(self, x, y, **kwargs):
         out = y > 3*y.std()
         super().plot(x, -y, **kwargs)
         super().plot(x[out], y[out], marker='x', linestyle='', **kwargs)       

尝试修改多个行为方面时,如果使用函数,很快就会变得混乱不堪。
然而,我还没有找到一种让matplotlib轻松处理新的Axes类的方法。文档中没有提到过我所看到的任何地方。 这个问题解决了从Figure类继承的问题。然后可以将自定义类传递给一些matplotlib函数。一个未被回答的问题在这里表明Axes并不像想象的那么简单。
更新:可以使用monkey patch matplotlib.axes.Axes来覆盖默认行为,但这只能在程序首次执行时进行一次。使用多个自定义Axes无法使用此方法。

1
matplotlib文档提供了一个创建自定义坐标轴的示例:custom_projection_example。此外,还有一份完整的创建比例尺和转换的指南。可以在这里找到一个用例。 - ImportanceOfBeingErnest
最后,我认为这与这个问题基本相同。 - ImportanceOfBeingErnest
@ImportanceOfBeingErnest,这很相似。那个问题是如何修改已经实例化的实例,尽管你的答案涵盖了这里的情况。 - user2699
1个回答

7
我在github上找到了一个很好的方法。 他们的目标是创建一个没有刻度或标记的Axes对象,这可以显著加快创建时间。 可以通过向matplotlib注册“投影”来实现这一点,然后使用这些投影。 对于我给出的示例,可以通过以下方式完成:
class Outliers(plt.Axes):
    """ Highlight outliers in plotted data """
    name = 'outliers'
    def plot(self, x, y, **kwargs):
         out = abs(y - y.mean()) > 3*y.std()
         super().plot(x, y, **kwargs)
         super().plot(x[out], y[out], marker='x', linestyle='', **kwargs)

import matplotlib.projections as proj
proj.register_projection(Outliers)

并且它可以被以下用户使用:
ax = f.add_subplot(1, 1, 1, projection='outliers')

或者

fig, axes = plt.subplots(20,20, subplot_kw=dict(projection='outliers'))

唯一需要进行的更改是在类定义中添加一个name变量,然后将该名称传递到子图中的projection参数。

1
是的,这似乎是实现此目的的“官方”方式。我曾经尝试使用内部函数来创建新轴并将其添加到图形中,但这比它值得的麻烦多了。我认为Matplotlib所称的“投影”特性有些不妥,暗示它仅用于定义自定义投影的功能,而实际上它可用于自定义与轴有关的任何内容。 - Alex

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