【问题标题】:Matplotlib: keep grid lines behind the graph but the y and x axis aboveMatplotlib:将网格线保留在图形后面,但 y 和 x 轴在上方
【发布时间】:2013-10-30 09:07:51
【问题描述】:

我很难在图表下绘制网格线而不弄乱主 x 和 y 轴 zorder:

import matplotlib.pyplot as plt
import numpy as np


N = 5
menMeans = (20, 35, 30, 35, 27)
menStd =   (2, 3, 4, 1, 2)

ind = np.arange(N)  # the x locations for the groups
width = 0.35       # the width of the bars

fig, ax = plt.subplots()
rects1 = ax.bar(ind, menMeans, width, color='r', yerr=menStd, alpha=0.9, linewidth = 0,zorder=3)

womenMeans = (25, 32, 34, 20, 25)
womenStd =   (3, 5, 2, 3, 3)
rects2 = ax.bar(ind+width, womenMeans, width, color='y', yerr=womenStd, alpha=0.9, linewidth = 0,zorder=3)

# add some
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind+width)
ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') )

ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') )

fig.gca().yaxis.grid(True, which='major', linestyle='-', color='#D9D9D9',zorder=2, alpha = .9)
[line.set_zorder(4) for line in ax.lines]

def autolabel(rects):
    # attach some text labels
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height),
                ha='center', va='bottom')

autolabel(rects1)
autolabel(rects2)

plt.show()

这个例子取自 matplotlib 自己的,我做了一些调整来展示如何让问题出现。我无法发布图像,但如果您运行代码,您会看到条形图绘制在水平网格线上方以及 x 和 y 轴上方。我不希望 x 和 y 轴被图形隐藏,尤其是当刻度也被阻塞时。

【问题讨论】:

  • 你为什么用fig.gca()而不是ax
  • 出于好奇:你知道是什么造成了ax.lines 中的线条吗?
  • 你说得对,我只是迷失在试图弄清楚如何去做而不是试图理解一切是如何运作的......

标签: python matplotlib plot


【解决方案1】:

我已经尝试过 matplotlib 1.2.1、1.3.1rc2 和 master(提交 06d014469fc5c79504a1b40e7d45bc33acc00773)

要获得条形顶部的轴脊,您可以执行以下操作:

for k, spine in ax.spines.items():  #ax.spines is a dictionary
    spine.set_zorder(10)

编辑

似乎我无法使刻度线出现在条形顶部。我试过了

1. ax.tick_params(direction='in', length=10, color='k', zorder=10)
   #This increases the size of the lines to 10 points, 
   #but the lines stays hidden behind  the bars
2. for l in ax.yaxis.get_ticklines():
       l.set_zorder(10)

和其他一些没有结果的方法。似乎在绘制条形时它们被放在顶部并且忽略了 zorder

一种解决方法是向外绘制刻度线

ax.tick_params(direction='out', length=4, color='k', zorder=10)

或内外都使用direction='inout'

EDIT2

@tcaswell cmets 之后我做了一些测试。

如果 ax.bar 函数中的 zorder 设置为 和网格线。如果值 >2.01(轴的默认值),则在轴、刻度线和网格的顶部绘制条形图。然后可以为脊椎设置更大的值(如上),但任何更改刻度线的zorder 的尝试都会被忽略(尽管值会在相应的艺术家上更新)。

我尝试将zorder=1 用于barzorder=0 用于网格,并且网格在条形的顶部 上绘制。所以 zorder 被忽略了。

回顾

在我看来,刻度线和网格zorder 只是被忽略并保持默认值。对我来说,这是一个与bar 或某些patches 有关的错误。

顺便说一句,我记得在使用 imshow 时成功更改了刻度线中的 zorder

【讨论】:

  • 这听起来像一个错误,你能检查一下它是否也发生在 master 上吗?
  • 我试一试告诉你
  • @tcaswell 我已经在 1.3.1rc2 和 master 上尝试过(刚刚获取)但不起作用。那么这是一个错误。谁归档? [如果我发现任何有关问题的提示,我可以尝试在今天晚些时候查看源代码]
  • 实际上,我认为这更像是一个“功能”而不是一个错误,因为网格线从轴对象继承了它们的 z 顺序。
  • @luke_16 iirc 刻度线 + 轴由渲染器一次性绘制,因此它们不能具有不同的 zorder。
【解决方案2】:

当我在背景中有网格线时,我遇到了相同的问题,即在绘图线下方绘制轴:

ax.yaxis.grid()  # grid lines
ax.set_axisbelow(True)  # grid lines are behind the rest

对我有用的解决方案是将plot() 函数的zorder 参数设置为1 到2 之间的值。目前尚不清楚,但zorder 值可以是任何数字。来自matplotlib.artist.Artist 类的文档:

set_zorder(级别)

为艺术家设置 zorder。首先绘制 zorder 值较低的艺术家。

接受:任何数字

因此:

for i in range(5):
    ax.plot(range(10), np.random.randint(10, size=10), zorder=i / 100.0 + 1)

我没有检查过这个范围之外的值,也许它们也可以工作。

【讨论】:

    【解决方案3】:

    在坐标轴上方绘图时,我遇到了与@luke_16 相同的问题。就我而言,它是在使用选项 ax.set_axisbelow(True) 设置情节背后的问题时出现的。

    我对这个错误的解决方法是,不使用板载网格,而是模拟它:

    def grid_selfmade(ax,minor=False):
        y_axis=ax.yaxis.get_view_interval()
        for xx in ax.xaxis.get_ticklocs():
            ax.plot([xx,xx],y_axis,linestyle=':',linewidth=0.5,zorder=0)
        if minor==True:
            for xx in ax.xaxis.get_ticklocs(minor=True):
                ax.plot([xx,xx],y_axis,linestyle=':',linewidth=0.5,zorder=0)
        x_axis=ax.xaxis.get_view_interval()
        for yy in ax.yaxis.get_ticklocs():
            ax.plot(x_axis,[yy,yy],linestyle=':',linewidth=0.5,zorder=0,)
        if minor==True:
            for yy in ax.yaxis.get_ticklocs(minor=True):
                ax.plot(x_axis,[yy,yy],linestyle=':',linewidth=0.5,zorder=0)
    

    这个函数只需要当前的轴实例,然后在主要刻度处绘制一个类似板载的网格在其他所有东西的后面(对于次要刻度也是可选的)。

    为了使图表顶部有坐标轴和坐标轴刻度,必须将ax.set_axisbelow(False) 留给False,并且不要在绘图中使用zorder>2。我通过更改代码中绘图命令的顺序来管理不带任何zorder-选项的绘图的 zorder。

    【讨论】:

      【解决方案4】:

      一个更简单更好的解决方案是从网格命令中复制线条并禁用网格,然后再次添加线条,但使用正确的 zorder:

      ax.grid(True)
      lines = ax.xaxis.get_gridlines().copy() + ax.yaxis.get_gridlines().copy()
      ax.grid(False)
      for l in lines:
          ax.add_line(l)
          l.set_zorder(0)
      

      【讨论】:

      • 我不明白您的解决方案中的 ax1 是什么。
      【解决方案5】:

      我知道这是一个相当老的问题,但问题似乎基本上仍然存在,所以让我写下一个 MRE 和我使用 Python 3.9.6 和 Matplotlib 3.4.2 的解决方案。

      问题

      正如 OP 所知,条形图的默认 zorder 相对较低。 下面的代码导致了一个刻度线和网格线都位于条形上方的绘图。

      import matplotlib.pyplot as plt
      
      y = (20, 35, 30, 35, 27)
      x = range(len(y))
      
      fig, ax = plt.subplots()
      ax.bar(x, y)
      ax.grid(True)
      ax.tick_params(direction="in", length=10)
      
      plt.show()
      

      可以使用ax.set_axisbelow(True) 将刻度线和网格线都移动到条形后面,但是x 轴上的刻度线会被条形隐藏。

      import matplotlib.pyplot as plt
      
      y = (20, 35, 30, 35, 27)
      x = range(len(y))
      
      fig, ax = plt.subplots()
      ax.bar(x, y)
      ax.grid(True)
      ax.tick_params(direction="in", length=10)
      ax.set_axisbelow(True)  # This line added.
      
      plt.show()
      

      解决方案

      在绘图中的所有元素都绘制完成后重新绘制刻度线。 (我认为它们是不透明的,重绘是无害的。)

      import matplotlib.artist
      import matplotlib.backend_bases
      import matplotlib.pyplot as plt
      
      
      class TickRedrawer(matplotlib.artist.Artist):
          """Artist to redraw ticks."""
      
          __name__ = "ticks"
      
          zorder = 10
      
          @matplotlib.artist.allow_rasterization
          def draw(self, renderer: matplotlib.backend_bases.RendererBase) -> None:
              """Draw the ticks."""
              if not self.get_visible():
                  self.stale = False
                  return
      
              renderer.open_group(self.__name__, gid=self.get_gid())
      
              for axis in (self.axes.xaxis, self.axes.yaxis):
                  loc_min, loc_max = axis.get_view_interval()
      
                  for tick in axis.get_major_ticks() + axis.get_minor_ticks():
                      if tick.get_visible() and loc_min <= tick.get_loc() <= loc_max:
                          for artist in (tick.tick1line, tick.tick2line):
                              artist.draw(renderer)
      
              renderer.close_group(self.__name__)
              self.stale = False
      
      
      y = (20, 35, 30, 35, 27)
      x = range(len(y))
      
      fig, ax = plt.subplots()
      ax.bar(x, y)
      ax.grid(True)
      ax.tick_params(direction="in", length=10)
      ax.set_axisbelow(True)
      ax.add_artist(TickRedrawer())  # This line added.
      
      plt.show()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        • 2021-10-07
        • 1970-01-01
        • 1970-01-01
        • 2014-06-14
        • 2019-11-04
        • 2013-12-23
        相关资源
        最近更新 更多