Matplotlib: линии сверху баров, легенда слева, без сплайнов - PullRequest
0 голосов
/ 21 января 2020

У меня проблемы с настройкой графика ниже.

graph

Вот как выглядит фрейм данных:

   Year  Some  All     Ratio
0  2016     9  157  0.057325
1  2017    13  189  0.068783
2  2018    21  216  0.097222
3  2019    18  190  0.094737
4  2020    28  284  0.098592

Вот что я хочу сделать:

  • Оранжевая линия должен быть перед решеткой. Я попытался использовать параметр zorder, и это не помогло. Я также попытался изменить порядок объекта осей, но не смог назначить линию для основной оси.
  • Мне нужна легенда на левой стороне. В приведенном ниже коде вы заметите, что я использую довольно большой аргумент figsize. Если я использую меньшую, легенда волшебным образом переместится влево, но я не хочу использовать меньшую.
  • Я хочу пометить гистограммы в верхней части каждой полосы соответствующим значением , Я пытался перебирать каждое значение и индивидуально аннотировать столбцы с помощью ax.annotate, но я не мог центрировать значения автоматически. В этом минимальном примере все значения имеют длину три цифры, но в исходных данных у меня есть числа длиной четыре цифры, и я не смог найти хороший способ сделать его центрированным для всех них.
  • Наконец Хочу избавиться от верхних и правых шипиков. Мой код ниже по какой-то причине не удалил их.

Ниже приведен код, помогающий людям начать работу.

data = {'Year': {0: '2016', 1: '2017', 2: '2018', 3: '2019', 4: '2020'},
 'Some': {0: 9, 1: 13, 2: 21, 3: 18, 4: 28},
 'All': {0: 157, 1: 189, 2: 216, 3: 190, 4: 284},
 'Ratio': {0: 0.05732484076433121,
  1: 0.06878306878306878,
  2: 0.09722222222222222,
  3: 0.09473684210526316,
  4: 0.09859154929577464}}

df = __import__("pandas").DataFrame(data)

ax = df.plot(x="Year", y="Ratio",
                 kind="line", linestyle='-', marker='o', color="orange",
                 figsize=((24,12))
                )
df.plot(x="Year", y="All",
            kind="bar", ax=ax, secondary_y=True
           )

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

1 Ответ

1 голос
/ 21 января 2020

Вы спрашиваете довольно много вещей.

  • Чтобы получить линию сверху бара, кажется, что мы должны сначала нарисовать бары, а затем линию. Рисование последней линии сжимает xlims, поэтому мы должны применять их явно.
  • Перемещение легенды более сложное. Обычно вы просто делаете ax1.legend(loc='upper left'), но в нашем случае с двумя графиками это, кажется, всегда рисует вторую легенду с последним нарисованным графиком в качестве единственной записи.
  • Есть функция set_bbox_to_anchor с небольшим документация . Он определяет некоторый блок (x, y, ширина, высота) , но есть также, казалось бы, недоступный параметр loc, который управляет связью блока и позиции. «По умолчанию для loc является loc="best", что дает непредсказуемые результаты при использовании аргумента bbox_to_anchor». Некоторые эксперименты могут быть необходимы. Лучшее решение, это охранять
  • Настройка текста проста. Просто переберите позиции y. Поместите в положение x 0,1,2, .. и по центру по горизонтали (вертикально внизу).
  • Чтобы удалить шипы, кажется, что есть две оси друг над другом (что, вероятно, также вызывает zorder не работать как хотелось). Вы захотите скрыть шипы обоих из них.
  • Чтобы удалить галочки, используйте ax1.axes.yaxis.set_ticks([]).
  • Чтобы переключить галочки ax2 влево, используйте ax2.yaxis.tick_left().
import pandas as pd
from matplotlib import pyplot as plt

data = {'Year': {0: '2016', 1: '2017', 2: '2018', 3: '2019', 4: '2020'},
        'Some': {0: 9, 1: 13, 2: 21, 3: 18, 4: 28},
        'All': {0: 157, 1: 189, 2: 216, 3: 190, 4: 284},
        'Ratio': {0: 0.05732484076433121,
                  1: 0.06878306878306878,
                  2: 0.09722222222222222,
                  3: 0.09473684210526316,
                  4: 0.09859154929577464}}

df = pd.DataFrame(data)

ax1 = df.plot(x="Year", y="All",
              kind="bar",
              )
for i, a in df.All.items():
    ax1.text(i, a, str(a), ha='center', va='bottom', fontsize=18)
xlims = ax1.get_xlim()

ax2 = df.plot(x="Year", y="Ratio",
              kind="line", linestyle='-', marker='o', color="orange", ax=ax1, secondary_y=True,
              figsize=((24, 12))
              )
ax2.set_xlim(xlims)  # needed because the line plot shortens the xlims

# ax1.get_legend().set_bbox_to_anchor((0.03, 0.9, 0.1, 0.1)) # unpredictable behavior when loc='best'
# ax1.legend(loc='upper left') # in our case, this would create a second legend

ax1.get_legend().remove() # remove badly placed legend
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(handles=handles1 + handles2,   # create a new legend
           labels=labels1 + labels2,
           loc='upper left')

# ax1.yaxis.tick_right()  # place the yticks for ax1 at the right
ax2.yaxis.tick_left()  # place the yticks for ax2 at the left
ax2.set_ylabel('Ratio')
ax2.yaxis.set_label_position('left')
ax1.axes.yaxis.set_ticks([]) # remove ticks

for ax in (ax1, ax2):
    for where in ('top', 'right'):
        ax.spines[where].set_visible(False)

plt.show()

plot

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...