Как разместить линии сетки от вторичной оси за основным графиком?

Следующий скрипт создает экземпляр 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. Это говорит

you can't interleave the drawing orders of artists from one Axes with those from another

и это объясняет, почему ax2.set_axisbelow(True) не влияет на основной Axes.

Могу ли я каким-то образом достичь своей цели? Обходные пути приветствуются (я полагаю, что канонического решения, согласно приведенной выше цитате, не существует).

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
2 011
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваш желаемый порядок рисования (сначала самый задний)

  • сетка для осей
  • сетка для сдвоенных осей
  • график в осях
  • график в двух осях

Однако это невозможно, как видно из комментария

you can't interleave the drawing orders of artists from one Axes with those from another

Это означает, что вам нужно 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()

Это правильный ответ. Спасибо за объяснение.

amanb 10.04.2019 21:54

Я не знал, что мы можем сделать четверняшки, используя twinx(), и никогда бы не придумал эту идею. Спасибо!

yudai-nkt 11.04.2019 01:28

Я только что попробовал ваш код и у меня есть два вопроса. i) Что делает оператор for? Сюжеты и без этого хорошо смотрятся. ii) порядок создания экземпляров, кажется, имеет значение: т. е. создание ax1b до того, как ax2a наложит вторичную сетку на синий график. Почему twinx() так себя ведет? Я открою еще один вопрос, если он предпочтительнее (особенно для последнего).

yudai-nkt 11.04.2019 17:49

i) Сравнивая Без удаления шипов, с удалением шипов, я нахожу разницу достаточно разительной, чтобы оставить этот цикл for в коде. Но, конечно, вы можете отказаться от этого, если хотите. ii) Ключевым здесь является порядок, в котором оси рисуются на экране. Если не указано другое zorder, они появляются в порядке их создания. Это решение обязательно требует, чтобы две оси с сетками были ниже двух других осей.

ImportanceOfBeingErnest 11.04.2019 21:33

Так сложно найти информацию по этой теме, этот ответ потрясающий! Большое спасибо!

Felix Crazzolara 02.01.2021 14:53

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

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

Другие вопросы по теме