Matplotlib: удерживайте линии сетки за графиком, но оси y и x выше

Мне тяжело строить линии сетки под моими графами, не возившись с основной осью 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 own, и я немного изменил, чтобы показать, как сделать проблему. Я не могу размещать изображения, но если вы запустите код, вы увидите, что полосы отображаются над горизонтальными линиями сетки, а также над осью x и y. Я не хочу, чтобы ось x и y была скрыта графиком, особенно когда тики также блокируются.

Ответ 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.

Если zorder в функции ax.bar установлено равным <= 2, ось, клики и линии сетки вычерчены над столбцами. Если значение valus > 2.01 (значение по умолчанию для оси), то бары рисуются над осью, точками и сеткой. Затем можно установить более высокие значения в шипы (как указано выше), но любая попытка изменить zorder для линий вычитания просто игнорируется (хотя значения обновляются у соответствующих исполнителей).

Я попробовал использовать zorder=1 для bar и zorder=0 для сетки, а сетка рисуется сверху. Поэтому zorder игнорируется.

резюме

Мне кажется, что метки и сетка zorder просто игнорируются и сохраняются до значений по умолчанию. Для меня это ошибка, связанная с bar или некоторым patches.

Кстати, я помню, как успешно менялся zorder в метокки при использовании imshow

Ответ 2

У меня была та же проблема с осями, которые рисуются ниже линии сюжета, когда у меня есть линии сетки в фоновом режиме:

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

Решение, которое сработало для меня, заключалось в том, чтобы установить аргумент zorder функции plot() на значение от 1 до 2. Это не сразу понятно, но значение zorder может быть любым числом. Из документации для matplotlib.artist.Artist класс:

set_zorder (уровень)

Установите zorder для исполнителя. Сначала рисуются художники с более низкими значениями zorder.

ACCEPTS: любое число

Таким образом:

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 -option, изменяя порядок графических команд в коде.

Ответ 4

Более простое и лучшее решение - скопировать строки из команды сетки и отключить сетку и снова добавить строки снова, но с правильным zorder:

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