Как вывести легенду из сюжета

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

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

Ответ 1

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

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

Ответ 2

Существует несколько способов сделать то, что вы хотите. Чтобы добавить к тому, что уже сказали @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 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(). Надеюсь, что это поможет, во всяком случае!

Ответ 3

Размещение легенды (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.

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

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")

Подробности о том, как интерпретировать аргумент 4-tuple для 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" to plt.savefig можно использовать для сохранения фигуры таким образом, чтобы весь художник на холсте (включая легенду) вписывался в сохраненной области. При необходимости размер фигуры автоматически настраивается.

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

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

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

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

Фигурная легенда
Можно использовать легенду для фигуры вместо осей, 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()

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

Легенда внутри выделенных осей подсети
Альтернативой использованию 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()

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

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

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

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

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

Дальнейшее чтение и примечания:

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

Ответ 4

Просто вызовите 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

Ответ 5

Короткий ответ: вы можете использовать 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

Ответ 6

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

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

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

Ответ 7

Короткий ответ: вызывать перетаскивание по легенде и интерактивно перемещать его туда, где вы хотите:

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()

Ответ 8

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

pylab.legend(loc='best')

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

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

Ответ 9

Не то, что вы просили, но я нашел альтернативу для той же проблемы. Сделайте легенду полупрозрачной, например: 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)

Ответ 10

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

Чтобы начать, вы захотите установить необходимые пакеты:

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

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

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

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

enter image description here

Ответ 11

Что-то в этом направлении работало для меня. Начиная с небольшого кода, взятого из Joe, этот метод изменяет ширину окна, чтобы автоматически поместить легенду справа от рисунка.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()

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)

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

plt.draw()

# Get the ax dimensions.
box = ax.get_position()
xlocs = (box.x0,box.x1)
ylocs = (box.y0,box.y1)

# Get the figure size in inches and the dpi.
w, h = fig.get_size_inches()
dpi = fig.get_dpi()

# Get the legend size, calculate new window width and change the figure size.
legWidth = leg.get_window_extent().width
winWidthNew = w*dpi+legWidth
fig.set_size_inches(winWidthNew/dpi,h)

# Adjust the window size to fit the figure.
mgr = plt.get_current_fig_manager()
mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))

# Rescale the ax to keep its original size.
factor = w*dpi/winWidthNew
x0 = xlocs[0]*factor
x1 = xlocs[1]*factor
width = box.width*factor
ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])

plt.draw()

Ответ 12

Вы также можете попробовать figlegend. Можно создать легенду, независимую от любого объекта Axes. Однако вам может понадобиться создать несколько контуров "dummy", чтобы убедиться, что форматирование объектов прошло правильно.

Ответ 13

Вот пример из учебника matplotlib, найденного здесь. Это один из более простых примеров, но я добавил прозрачность в легенду и добавил plt.show(), чтобы вы могли вставить это в интерактивную оболочку и получить результат:

import matplotlib.pyplot as plt
p1, = plt.plot([1, 2, 3])
p2, = plt.plot([3, 2, 1])
p3, = plt.plot([2, 3, 1])
plt.legend([p2, p1, p3], ["line 1", "line 2", "line 3"]).get_frame().set_alpha(0.5)
plt.show()

Ответ 14

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

f = plt.figure()
ax = f.add_subplot(414)
lgd = ax.legend(loc='upper left', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
ax.autoscale_view()
plt.savefig(fig_name, format='svg', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches='tight')

Ответ 15

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

Вместо того, чтобы добавлять все свои дополнения в ограничительную рамку, когда вы хотите их выписать, вы можете добавить их раньше времени художникам Figure. Используя что-то похожее на Franck Dernoncourt ответьте выше:

import matplotlib.pyplot as plt

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

# plotting function
def gen_plot(x, y):
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)
    lgd = ax.legend( [ "Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
    fig.artists.append(lgd) # Here the change
    ax.set_title("Title")
    ax.set_xlabel("x label")
    ax.set_ylabel("y label")
    return fig

# plotting
fig = gen_plot(all_x, all_y)

# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

Здесь создан сюжет.

Ответ 16

не знаю, если вы уже разобрались в своей проблеме... возможно, да, но... Я просто использовал строку 'outside' для местоположения, как в Matlab. Я импортировал pylab из matplotlib. см. следующий код:

from matplotlib as plt
from matplotlib.font_manager import FontProperties
...
...
t = A[:,0]
sensors = A[:,index_lst]

for i in range(sensors.shape[1]):
    plt.plot(t,sensors[:,i])

plt.xlabel('s')
plt.ylabel('°C')
lgd = plt.legend(b,loc='center left', bbox_to_anchor=(1, 0.5),fancybox = True, shadow = True)

Нажмите, чтобы увидеть сюжет