Python - Легенда перекрывается круговой диаграммой

Использование matplotlib в python. Легенда перекрывается моей круговой диаграммой. Пробовал различные варианты "loc", такие как "best", 1,2,3... но безуспешно. Любые предложения относительно того, как точно указать положение легенды (например, дать отступы из границ круговой диаграммы) или, по крайней мере, убедиться, что он не перекрывается?

Ответ 1

Короткий ответ: вы можете использовать plt.legend аргументы loc, bbox_to_anchor и дополнительно bbox_transform и mode, чтобы разместить легенду в осях или фигуре.


Длинная версия:

Шаг 1: Убедитесь, что требуется легенда.

Во многих случаях никакая легенда не требуется вообще, и информация может быть выведена непосредственно контекстом или цветом:

введите описание изображения здесь

введите описание изображения здесь

Если сюжет не может жить без легенды, перейдите к шагу 2.

Шаг 2: Убедитесь, что нужна круговая диаграмма.

Во многих случаях круговые диаграммы не являются лучшим способом передачи информации.

введите описание изображения здесь

Если потребность в круговой диаграмме однозначно определена, давайте переместим легенду.

Размещение легенды

plt.legend() имеет два основных аргумента для определения положения легенды. Самым важным и само по себе достаточным является аргумент loc.
Например. plt.legend(loc="upper left") поместил легенду таким образом, чтобы она сидела в верхнем левом углу рамки. Если дальнейший аргумент не указан, это ограничивающее поле будет целыми осями.

Однако мы можем указать нашу собственную ограничительную рамку, используя аргумент bbox_to_anchor. Если bbox_to_anchor задан 2-кортеж, например. bbox_to_anchor=(1,1) Это означает, что ограничивающий прямоугольник расположен в верхнем правом углу осей и не имеет степени. Затем он действует как точка, относительно которой легенда будет помещена в соответствии с аргументом loc. Затем он будет расширяться из ограничивающего прямоугольника нулевого размера. Например. если loc "upper left", верхний левый угол легенды находится в позиции (1,1), и легенда будет расширяться вправо и вниз.

Эта концепция используется для вышеупомянутого сюжета, который говорит нам потрясающую правду о предвзятости на выборах Мисс Вселенная.

import matplotlib.pyplot as plt
import matplotlib.patches

total = [100]
labels = ["Earth", "Mercury", "Venus", "Mars", "Jupiter",  "Saturn", 
           "Uranus", "Neptune", "Pluto *"]
plt.title('Origin of Miss Universe since 1952')
plt.gca().axis("equal")
pie = plt.pie(total, startangle=90, colors=[plt.cm.Set3(0)],
                            wedgeprops = { 'linewidth': 2, "edgecolor" :"k" })
handles = []
for i, l in enumerate(labels):
    handles.append(matplotlib.patches.Patch(color=plt.cm.Set3((i)/8.), label=l))
plt.legend(handles,labels, bbox_to_anchor=(0.85,1.025), loc="upper left")
plt.gcf().text(0.93,0.04,"* out of competition since 2006", ha="right")
plt.subplots_adjust(left=0.1, bottom=0.1, right=0.75)

Чтобы легенда не превышала цифру, мы используем plt.subplots_adjust, чтобы получить больше пространства между краем фигуры и осью, которая затем может быть воспринята легендой.

Существует также возможность использовать 4-кортеж для bbox_to_anchor. Как использовать или интерпретировать это подробно в этом вопросе: Что означает аргумент 4-элементного кортежа для 'bbox_to_anchor' в matplotlib?
и затем можно использовать аргумент mode="expand", чтобы легенда вписывалась в указанную ограничительную рамку.

Есть несколько полезных альтернатив этому подходу:

Использование координат фигуры

Вместо указания положения легенды в координатах осей можно использовать координаты фигуры. Преимущество заключается в том, что это позволит просто разместить легенду в одном углу рисунка, не изменяя большую часть остальной части. Для этого нужно использовать аргумент bbox_transform и предоставить ему преобразование фигуры. Координаты, заданные bbox_to_anchor, затем интерпретируются как координаты фигуры.

plt.legend(pie[0],labels, bbox_to_anchor=(1,0), loc="lower right", 
                          bbox_transform=plt.gcf().transFigure)

Здесь (1,0) - нижний правый угол фигуры. Из-за стандартных расстояний между осями и краем фигуры достаточно разместить легенду, чтобы она не перекрывалась с пирогом.

введите описание изображения здесь

В других случаях, возможно, еще нужно будет адаптировать эти интервалы таким образом, чтобы не наблюдалось перекрытия, например.

title = plt.title('What slows down my computer')
title.set_ha("left")
plt.gca().axis("equal")
pie = plt.pie(total, startangle=0)
labels=["Trojans", "Viruses", "Too many open tabs", "The anti-virus software"]
plt.legend(pie[0],labels, bbox_to_anchor=(1,0.5), loc="center right", fontsize=10, 
           bbox_transform=plt.gcf().transFigure)
plt.subplots_adjust(left=0.0, bottom=0.1, right=0.45)

введите описание изображения здесь

Сохранение файла с помощью bbox_inches="tight"

Теперь могут быть случаи, когда мы больше интересуемся сохраненной фигурой, чем тем, что показано на экране. Затем мы можем просто позиционировать легенду на краю фигуры, например,

введите описание изображения здесь

но затем сохраните его с помощью bbox_inches="tight" до savefig,

plt.savefig("output.png", bbox_inches="tight")

Это создаст большую фигуру, которая сидит вокруг содержимого холста:

введите описание изображения здесь

Представлен сложный подход, который позволяет разместить легенду плотно внутри фигуры, без изменения размера фигуры: Создание фигуры с точным размером и без дополнений (и легенда за пределами осей)

Использование подзаголовков

Альтернативой является использование подзаговоров для резервирования места для легенды. В этом случае одна подзапись может принимать круговую диаграмму, другой подзаговор содержит легенду. Это показано ниже.

fig = plt.figure(4, figsize=(3,3))
ax = fig.add_subplot(211) 
total = [4,3,2,81]
labels = ["tough working conditions", "high risk of accident", 
              "harsh weather", "it not allowed to watch DVDs"]
ax.set_title('What people know about oil rigs')
ax.axis("equal")
pie = ax.pie(total, startangle=0)
ax2 = fig.add_subplot(212)
ax2.axis("off") 
ax2.legend(pie[0],labels, loc="center")

введите описание изображения здесь