Matplotlib:使对象被轴自动缩放忽略

Matplotlib: make objects ignored by axis autoscaling

是否可以创建一个被 Axes 自动缩放器忽略的绘图对象?

我经常需要添加垂直线,或为绘图区域添加阴影以显示所需的数据范围(作为查看器的参考框架),但随后我必须设置坐标轴自动缩放 x/ylimits 回到它们之前的位置 - 或将 lines/shading 截断为当前轴限制,或其他各种 fandangos。

如果这些 shader/vertical 线充当绘图上的“背景”对象,被自动缩放器忽略,会容易得多,所以只有我的真实数据影响了自动缩放。

举个例子: 这个图是真实世界的数据,我想看看每天的数据是否在期望的范围内。

我想在 -50 nm ≤ Y ≤ +50 nm 范围内对第 3 轴图进行着色。 我很想简单地从 -50 --> +50nm 添加一个巨大的半透明矩形,但让自动缩放忽略它。 例如。像这样(我在绘图程序中手动添加了红色阴影。):

此外,您可以看到我使用这样的代码手动添加了垂直线(我真的应该只使用垂直网格线位置...):

ax1.set_ylim(ymin, ymax)
ax1.vlines( self.Dates , color="grey", alpha=0.05, ymin=ax1.get_ylim()[0], ymax=ax1.get_ylim()[1] )

您可以在第 2 轴和第 3 轴中看到,VLine 将 AutoScaling 向外推,因此现在 VLine 和 Axis 之间存在间隙。目前我需要欺骗调用 fig.tight_layout()ax2/ax3.plot() 的顺序,或者转换为手动设置 X-Tick locations/gridlines 等 - 但如果这些 VLines 是甚至没有被视为数据,所以自动缩放忽略了它们。

是否可以让自动缩放“忽略”某些对象?

autoscale_view 主要使用轴的 dataLim 属性来计算轴限制。反过来,数据限制由 _update_image_limits_update_line_limits_update_patch_limits 等轴方法设置。这些方法都使用这些艺术家的基本属性来计算新的数据限制(例如路径),因此为“背景”艺术家覆盖它们是行不通的。所以不,严格来说,我不认为自动缩放可以忽略某些对象,只要它们是可见的。

但是,除了目前提到的选项之外,还有其他选项可以保留数据视图。

使用不影响数据限制的艺术家,例如axhlineaxvline 或使用 add_artist.

添加补丁(和派生 类)
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt

x, y = np.random.randn(2, 1000)

fig, ax = plt.subplots()
ax.scatter(x, y, zorder=2)
ax.add_artist(plt.Rectangle((0,0), 6, 6, alpha=0.1, zorder=1))
ax.axhline(0)
ax.axvline(0)

您可以绘制前景对象,然后关闭自动缩放。

#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt

x, y = np.random.randn(2, 1000)

fig, ax = plt.subplots()
ax.scatter(x, y, zorder=2)
ax.autoscale_view() # force auto-scale to update data limits based on scatter
ax.set_autoscale_on(False)
ax.add_patch(plt.Rectangle((0,0), 6, 6, alpha=0.1, zorder=1))

我唯一的其他想法是猴子补丁 Axes.relim() 来检查 background 属性(这可能是最接近您想象的):

import numpy as np
import matplotlib.axes
import matplotlib.transforms as mtransforms
import matplotlib.image as mimage
import matplotlib.lines as mlines
import matplotlib.patches as mpatches

class PatchedAxis(matplotlib.axes.Axes):
    def relim(self, visible_only=False):
        """
        Recompute the data limits based on current artists.
        At present, `.Collection` instances are not supported.
        Parameters
        ----------
        visible_only : bool, default: False
            Whether to exclude invisible artists.
        """
        # Collections are deliberately not supported (yet); see
        # the TODO note in artists.py.
        self.dataLim.ignore(True)
        self.dataLim.set_points(mtransforms.Bbox.null().get_points())
        self.ignore_existing_data_limits = True

        for artist in self._children:
            if not visible_only or artist.get_visible():
                if not hasattr(artist, "background"):
                    if isinstance(artist, mlines.Line2D):
                        self._update_line_limits(artist)
                    elif isinstance(artist, mpatches.Patch):
                        self._update_patch_limits(artist)
                    elif isinstance(artist, mimage.AxesImage):
                        self._update_image_limits(artist)

matplotlib.axes.Axes = PatchedAxis

import matplotlib.pyplot as plt

x, y = np.random.randn(2, 1000)

fig, ax = plt.subplots()
ax.scatter(x, y, zorder=2)
rect = plt.Rectangle((0,0), 6, 6, alpha=0.1, zorder=1)
rect.background = True
ax.add_patch(rect)
ax.relim()
ax.autoscale_view()

但是,由于某些原因,在调用 relimax._children 未填充。也许其他人可以弄清楚 ax._children 属性是在什么条件下创建的。