Как убрать легенду из сюжета - PullRequest
779 голосов
/ 15 января 2011

У меня есть серия из 20 сюжетов (не субплотов), которые должны быть выполнены на одной фигуре.Я хочу, чтобы легенда была за пределами коробки.В то же время я не хочу менять оси, так как размер фигуры уменьшается.Пожалуйста, помогите мне по следующим вопросам:

  1. Я хочу, чтобы поле легенды находилось за пределами области сюжета.(Я хочу, чтобы легенда находилась снаружи с правой стороны области графика).
  2. В любом случае я уменьшу размер шрифта текста внутри поля легенды, чтобы его размербудь маленьким.

Ответы [ 16 ]

1535 голосов
/ 15 января 2011

Есть несколько способов сделать то, что вы хотите.Чтобы добавить к тому, что @inalis и @Navi уже сказали, вы можете использовать аргумент ключевого слова bbox_to_anchor, чтобы частично разместить легенду вне осей и / или уменьшить размер шрифта.

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

Итак, давайте начнем с общего примера:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend()

plt.show()

alt text

Если мы сделаем то же самое, но используем аргумент ключевого слова bbox_to_anchor, мы сможем слегка сместить легенду за границы осей:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend(bbox_to_anchor=(1.1, 1.05))

plt.show()

alt text

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

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
          ncol=3, fancybox=True, shadow=True)
plt.show()

alt text

Кроме того, вы можете уменьшить ширину текущего графика и поместить легенду полностью вне оси рисунка:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

# Put a legend to the right of the current axis
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.show()

alt text

Аналогичным образом вы можете уменьшить сюжет по вертикали и поместить горизонтальную легенду внизу:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis's height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
                 box.width, box.height * 0.9])

# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
          fancybox=True, shadow=True, ncol=5)

plt.show()

alt text

Посмотритев руководство по легендам matplotlib .Вы также можете взглянуть на plt.figlegend().Надеюсь, это немного поможет!

552 голосов
/ 16 апреля 2017

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

Легенда располагается внутри ограничительной рамки осей, используя аргумент loc для plt.legend.
Например. loc="upper right" помещает легенду в верхнем правом углу ограничительной рамки, которая по умолчанию распространяется от (0,0) до (1,1) в координатах осей (или в обозначении ограничительной рамки (x0,y0, width, height)=(0,0,1,1)).

Чтобы разместить легенду за пределами ограничивающей рамки осей, можно указать кортеж (x0,y0) координат осей нижнего левого угла легенды.

plt.legend(loc=(1.04,0))

Однако более универсальным подходом было бы вручную указать ограничивающий прямоугольник, в который следует поместить легенду, используя аргумент bbox_to_anchor. Можно ограничить себя предоставлением только части (x0,y0) bbox. Это создает поле нулевого диапазона, из которого легенда будет расширяться в направлении, заданном аргументом loc. Э.Г.

plt.legend(bbox_to_anchor=(1.04,1), loc="upper left")

размещает легенду вне осей, так что верхний левый угол легенды находится в положении (1.04,1) в координатах осей.

Дополнительные примеры приведены ниже, где дополнительно показано взаимодействие между различными аргументами, такими как mode и ncols.

enter image description here

l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right", 
                bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")

Подробности о том, как интерпретировать аргумент с четырьмя кортежами в bbox_to_anchor, как в l4, можно найти в этого вопроса . mode="expand" расширяет легенду по горизонтали внутри ограничительной рамки, заданной 4-кортежем. Для вертикально развернутой легенды см. этот вопрос .

Иногда бывает полезно указать ограничивающий прямоугольник в координатах фигуры вместо координат осей. Это показано в примере l5 сверху, где аргумент bbox_transform используется для размещения легенды в левом нижнем углу рисунка.

Постобработка

Размещение легенды вне осей часто приводит к нежелательной ситуации, когда она полностью или частично выходит за пределы рисунка.

Решения этой проблемы:

  • Настройка параметров подплощадки
    Можно настроить параметры подзаговора таким образом, чтобы оси занимали меньше места внутри фигуры (и, таким образом, оставляли больше места для легенды), используя plt.subplots_adjust. Например.

    plt.subplots_adjust(right=0.7)
    

    оставляет 30% пространства справа от фигуры, где можно разместить легенду.

  • Плотный макет
    Использование plt.tight_layout Позволяет автоматически настраивать параметры подзаговора таким образом, чтобы элементы фигуры плотно прилегали к краям фигуры. К сожалению, легенда не принимается во внимание в этом автомате, но мы можем предоставить прямоугольник, в который будет помещаться вся область сюжетов (включая метки).

    plt.tight_layout(rect=[0,0,0.75,1])
    
  • Сохранение рисунка с помощью bbox_inches = "tight"
    Аргумент от bbox_inches = "tight" до plt.savefig можно использовать для сохранения рисунка, чтобы все художники на холсте (включая легенду) помещались в сохраненную область. При необходимости размер фигуры настраивается автоматически.

    plt.savefig("output.png", bbox_inches="tight")
    
  • автоматическая настройка параметров подзаговора
    В этом ответе можно найти способ автоматической настройки положения подзаголовка таким образом, чтобы легенда помещалась внутри холста без изменения размера фигуры : Создание фигуры с точным размером и без отступов (и легенда вне оси)

Сравнение рассмотренных выше случаев:

enter image description here

Альтернативы

Легенда о фигуре
Вместо осей можно использовать легенду к фигуре matplotlib.figure.Figure.legend. Это стало особенно полезным для версии matplotlib> = 2.1, где не требуется специальных аргументов

fig.legend(loc=7) 

для создания легенды для всех художников по разным осям фигуры.Легенда размещается с использованием аргумента loc, аналогично тому, как она размещается внутри осей, но в отношении всей фигуры - следовательно, она будет несколько вне осей автоматически.Осталось отрегулировать вспомогательные сюжеты так, чтобы не было перекрытия между легендой и осями.Здесь вам поможет пункт «Настройка параметров подзаговора» сверху.Пример:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi)
colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
    axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))

fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)   
plt.show()

enter image description here

Легенда внутри выделенных осей подплощадок
Альтернативой использованию bbox_to_anchor будетразместить легенду по осям отдельных сюжетов (lax).Поскольку вспомогательный сюжет должен быть меньше, чем сюжет, мы можем использовать gridspec_kw={"width_ratios":[4,1]} при создании осей.Мы можем скрыть оси lax.axis("off"), но по-прежнему помещать легенду. Дескрипторы и метки легенды необходимо получить из реального сюжета через h,l = ax.get_legend_handles_labels(), а затем их можно добавить к легенде в подпункте lax, lax.legend(h,l),Полный пример приведен ниже.

import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6,2

fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
ax.plot(x,y, label="y=sin(x)")
....

h,l = ax.get_legend_handles_labels()
lax.legend(h,l, borderaxespad=0)
lax.axis("off")

plt.tight_layout()
plt.show()

Это дает график, который визуально очень похож на график сверху:

enter image description here

Мы также можем использовать первые оси для размещения легенды, но используем bbox_transform осей легенды,

ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
lax.axis("off")

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

Дополнительная литература и примечания:

  • Рассмотрим руководство по легенде matplotlib с некоторыми примерами других вещей, которые вы хотите сделатьс легендами.
  • Некоторые примеры кода для размещения легенд для круговых диаграмм можно найти непосредственно в ответе на этот вопрос: Python - легенда перекрывается круговой диаграммой
  • The *Аргумент 1178 * может принимать числа вместо строк, что делает вызовы короче, однако они не очень интуитивно отображаются друг на друга.Вот сопоставление для справки:

enter image description here

132 голосов
/ 03 июля 2014

Просто позвоните legend() позвоните после plot() вызова, например:

# matplotlib
plt.plot(...)
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))

# Pandas
df.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))

Результаты будут выглядеть примерно так:

enter image description here

87 голосов
/ 15 января 2011

Вы можете уменьшить размер легенды, создав свойства шрифта:

from matplotlib.font_manager import FontProperties

fontP = FontProperties()
fontP.set_size('small')
legend([plot1], "title", prop=fontP) 
# or add prop=fontP to whatever legend() call you already have
74 голосов
/ 17 августа 2014

Краткий ответ: вы можете использовать bbox_to_anchor + bbox_extra_artists + bbox_inches='tight'.


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

Однако обычная проблема заключается в том, что поле с надписями обрезается, например ::

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')

fig.savefig('image_output.png', dpi=300, format='png')

enter image description here

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

fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')

Пример (я только изменил последнюю строку, добавив 2 параметра к fig.savefig()):

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')    

fig.savefig('image_output.png', dpi=300, format='png', bbox_extra_artists=(lgd,), bbox_inches='tight')

enter image description here

Хотелось бы, чтобы matplotlib изначально разрешал расположение для легенды вне поля, как Matlab делает :

figure
x = 0:.2:12;
plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
hleg = legend('First','Second','Third',...
              'Location','NorthEastOutside')
% Make the text of the legend italic and color it brown
set(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])

enter image description here

60 голосов
/ 15 января 2011

Чтобы разместить легенду вне области сюжета, используйте ключевые слова loc и bbox_to_anchor из legend().Например, следующий код поместит легенду справа от области графика:

legend(loc="upper left", bbox_to_anchor=(1,1))

Для получения дополнительной информации см. Руководство легенда

55 голосов
/ 08 декабря 2014

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

pylab.legend(loc='best')

Это автоматически отодвинет легенду от данных, если это возможно!Compare the use of loc='best'

Однако, если некуда поместить легенду, не перекрывая данные, вам нужно попробовать один из других ответов;использование loc="best" никогда не поставит легенду за пределы сюжета.

55 голосов
/ 20 февраля 2013

Короткий ответ : вызовите перетаскиваемый элемент над легендой и в интерактивном режиме переместите его в любое место:

ax.legend().draggable()

Длинный ответ : если вы предпочитаете разместитьЛегенда в интерактивном / ручном режиме, а не программно, вы можете переключать режим легенды с помощью перетаскивания, чтобы вы могли перетащить его туда, куда вы хотите.Проверьте пример ниже:

import matplotlib.pylab as plt
import numpy as np
#define the figure and get an axes instance
fig = plt.figure()
ax = fig.add_subplot(111)
#plot the data
x = np.arange(-5, 6)
ax.plot(x, x*x, label='y = x^2')
ax.plot(x, x*x*x, label='y = x^3')
ax.legend().draggable()
plt.show()
13 голосов
/ 17 апреля 2014

Не совсем то, что вы просили, но я нашел альтернативу для той же проблемы. Сделайте легенду полупрозрачной, вот так: matplotlib plot with semi transparent legend and semitransparent text box

Сделайте это с:

fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot(x,y,label=label,color=color)
# Make the legend transparent:
ax.legend(loc=2,fontsize=10,fancybox=True).get_frame().set_alpha(0.5)
# Make a transparent text box
ax.text(0.02,0.02,yourstring, verticalalignment='bottom',
                     horizontalalignment='left',
                     fontsize=10,
                     bbox={'facecolor':'white', 'alpha':0.6, 'pad':10},
                     transform=self.ax.transAxes)
7 голосов
/ 09 февраля 2014

Как уже отмечалось, вы также можете разместить легенду на графике или слегка отодвинуть ее к краю. Вот пример использования Plotly Python API , сделанного с помощью IPython Notebook . Я в команде.

Для начала вам понадобится установить необходимые пакеты:

import plotly
import math
import random
import numpy as np

Затем установите Plotly:

un='IPython.Demo'
k='1fw3zw2o13'
py = plotly.plotly(username=un, key=k)


def sin(x,n):
sine = 0
for i in range(n):
    sign = (-1)**i
    sine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*sign
return sine

x = np.arange(-12,12,0.1)

anno = {
'text': '$\\sum_{k=0}^{\\infty} \\frac {(-1)^k x^{1+2k}}{(1 + 2k)!}$',
'x': 0.3, 'y': 0.6,'xref': "paper", 'yref': "paper",'showarrow': False,
'font':{'size':24}
}

l = {
'annotations': [anno], 
'title': 'Taylor series of sine',
'xaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'yaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'legend':{'font':{'size':16},'bordercolor':'white','bgcolor':'#fcfcfc'}
}

py.iplot([{'x':x, 'y':sin(x,1), 'line':{'color':'#e377c2'}, 'name':'$x\\\\$'},\
      {'x':x, 'y':sin(x,2), 'line':{'color':'#7f7f7f'},'name':'$ x-\\frac{x^3}{6}$'},\
      {'x':x, 'y':sin(x,3), 'line':{'color':'#bcbd22'},'name':'$ x-\\frac{x^3}{6}+\\frac{x^5}{120}$'},\
      {'x':x, 'y':sin(x,4), 'line':{'color':'#17becf'},'name':'$ x-\\frac{x^5}{120}$'}], layout=l)

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

enter image description here

Для альтернативного размещения вы можете точно выровнять край графика и границу легенды и удалить граничные линии для более точного соответствия.

enter image description here

Вы можете перемещать и изменять стиль легенды и графика с помощью кода или с помощью графического интерфейса. Чтобы переместить легенду, у вас есть следующие опции, чтобы расположить легенду внутри графика, присваивая значения x и y <= 1. E.g: </p>

  • {"x" : 0,"y" : 0} - Слева внизу
  • {"x" : 1, "y" : 0} - справа внизу
  • {"x" : 1, "y" : 1} - вверху справа
  • {"x" : 0, "y" : 1} - Верхний левый
  • {"x" :.5, "y" : 0} - Нижний центр
  • {"x": .5, "y" : 1} - Верхний центр

В этом случае мы выбираем верхний правый угол legendstyle = {"x" : 1, "y" : 1}, также описанный в документации :

enter image description here

...