【问题标题】:How to put grid lines from the secondary axis behind the primary plot?如何将网格线从辅助轴放在主图后面?
【发布时间】:2019-09-01 02:21:23
【问题描述】:

以下脚本创建了一个 Figure 实例,在二级网格线后面有一个蓝色直方图,而这些网格线本身在一个橙色累积直方图后面。

import matplotlib.pyplot as plt
import numpy as np

plt.style.use("seaborn-darkgrid")
np.random.seed(42)

foo = np.random.randn(1000)

fig, ax = plt.subplots()
ax.hist(foo, bins=50)
ax2 = ax.twinx()
ax2.hist(
    foo, bins=50, density=True, cumulative=True, histtype="step", color="tab:orange"
)

plt.show()

我正在寻找一种将网格线放在蓝色直方图后面的方法,并在matplotlib/matplotlib#7984 发现了一个相关问题。它说

您不能将一个 Axes 的艺术家的绘图顺序与另一个 Axes 的艺术家的绘图顺序交错

这就解释了为什么ax2.set_axisbelow(True) 对主Axes 没有影响。

我能以某种方式实现我的目标吗?欢迎使用变通方法(我想根据上面的引用没有规范的解决方案)。

【问题讨论】:

    标签: python matplotlib


    【解决方案1】:

    您想要的绘图顺序是(最靠后的)

    • 坐标轴网格
    • 双轴网格
    • 在轴上绘图
    • 双轴绘图

    然而,正如评论所见,这是不可能的

    您不能将一个 Axes 的艺术家的绘图顺序与另一个 Axes 的艺术家的绘图顺序交错

    这意味着您需要 4 个轴而不是两个。

    • 主要 y 比例网格的轴
    • 二级 y 比例网格的轴
    • 主要 y 尺度上的绘图轴
    • 次要 y 刻度上的绘图轴

    这可能看起来像这样:

    import matplotlib.pyplot as plt
    import numpy as np
    
    np.random.seed(42)
    
    foo = np.random.randn(1000)
    
    fig, ax1a = plt.subplots()  # ax1a for the histogram grid
    ax2a = ax1a.twinx()         # ax2a for the cumulative step grid
    ax1b = ax1a.twinx()         # ax1b for the histogram plot
    ax2b = ax1a.twinx()         # ax2a for the cumulative step plot
    # Link the respective y-axes for grid and plot
    ax1a.get_shared_y_axes().join(ax1a, ax1b)
    ax2a.get_shared_y_axes().join(ax2a, ax2b)
    # Remove ticks and labels and set which side to label
    ticksoff = dict(labelleft=False, labelright=False, left=False, right=False)
    ax1a.tick_params(axis="y", **ticksoff)
    ax2a.tick_params(axis="y", **ticksoff)
    ax1b.tick_params(axis="y", labelleft=True, labelright=False, left=True, right=False)
    ax2b.tick_params(axis="y", labelleft=False, labelright=True, left=False, right=True)
    # Spines off
    for ax in [ax1a, ax2a, ax1b]:
        for k,v in ax.spines.items():
            v.set_visible(False)
    
    ax1b.hist(foo, bins=50)
    
    ax2b.hist(
        foo, bins=50, density=True, cumulative=True, histtype="step", color="tab:orange"
    )
    ax1a.grid()
    ax2a.grid()
    plt.show()
    

    【讨论】:

    • 这是正确答案。谢谢你的解释。
    • 我不知道我们可以使用twinx() 制作四胞胎,也永远不会想出这个主意。谢谢!
    • 我刚试过你的代码,有两个问题。 i) for 语句有什么作用?没有它,情节看起来很好。 ii) 实例化的顺序似乎很重要:即,在 ax2a 覆盖蓝色图上的辅助网格之前创建 ax1b。为什么twinx() 会这样?我会打开另一个问题,如果它是首选(尤其是后者)。
    • i) 比较 Without the removal of spineswith removal of spines,我发现差异足够惊人,可以在代码中留下 for 循环。但是,当然,如果您愿意,您可以决定将其排除在外。 ii) 这里的关键是在屏幕上绘制轴的顺序。如果没有给出不同的zorder,它们会按照创建的顺序出现。此解决方案必然要求带有网格的两个轴低于其他两个轴。
    • 很难找到关于这个主题的信息,这个答案太棒了!非常感谢!
    【解决方案2】:

    已经有一个公认的可行的答案,但我认为还有另一种方法可以解决这个问题,同时也能稍微提高美感:

    • 不要为ax2绘制网格
    • 使用ax.set_ylim()ax.set_yticks() 手动指定第一个y 轴上的范围和刻度,以便刻度与ax2 上的刻度对齐。这样就不需要第二组网格线,并且图表在视觉上变得不那么繁忙。

    在上面的示例中,您可以通过以下方式执行此操作:

    ax2.set_ylim(0.0, 1.05)
    ax2.set_yticks(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)
    
    ax.set_ylim(0.0, 70*1.05)
    ax.set_yticks(0, 14, 28, 42, 56, 70)
    ax.grid(both)
    ax.set_axisbelow(True)
    

    当然,这可能会更好或更差,具体取决于实际数字,在示例中,我移至非整数。在 15、30 ... 处使用刻度可能会起作用,或者将第二个 y 轴重新缩放到 140 或将第一个 y 轴重新缩放到 100,但是没有理想的解决方案。自动化这种方法当然很困难——但如果我需要为特定数据集制作漂亮的图表,我有时会使用它,花时间调整它是合理的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-25
      • 2020-10-29
      • 1970-01-01
      相关资源
      最近更新 更多