Как убрать легенду из сюжета

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

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

16 ответов


создать свойства шрифта

from matplotlib.font_manager import FontProperties

fontP = FontProperties()
fontP.set_size('small')
legend([plot1], "title", prop=fontP)

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


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

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

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

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

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

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

подробнее о том, как интерпретировать Аргумент 4-кортежа в bbox_to_anchor, as в l4, можно найти в этот вопрос. The 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")
    
  • автоматическая настройка подзаголовка params
    Способ автоматической настройки положения подзаголовка таким образом, чтобы легенда помещалась внутри холста без изменения размера фигуры можно найти в этот ответ: создание фигуры с точным размером и без заполнения (и легенды вне осей)

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

enter image description here

варианты

цифра легенда
Вместо осей на фигуре можно использовать легенду,matplotlib.figure.Figure.legend. Это стало особенно полезным для matplotlib version >=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")

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


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

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

мы просто цепи legend() звонить после plot().

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

enter image description here


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


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

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

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

pylab.legend(loc='best')

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


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

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

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


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

как уже отмечалось, вы также можете поместить легенду в сюжет или немного от него к краю. Вот пример использования 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

вы можете перемещать и изменять стиль легенды и графика с помощью кода или GUI. Чтобы переместить легенду, у вас есть следующие опции для размещения легенды внутри графика путем назначения x и y значения

  • {"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


что-то в этом роде сработало для меня. Начиная с фрагмента кода, взятого из 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()

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


вот пример из учебника 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()

решение, которое сработало для меня, когда у меня была огромная легенда, состояло в использовании дополнительного пустого макета изображения. В следующем примере я сделал 4 строки, а внизу я рисую изображение со смещением для legend (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')

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

вместо добавления всех ваших дополнений в ограничивающую рамку, когда вы хотите ее записать, вы можете добавить их заранее в Figure'ы художников. Используя что-то похожее на Франка 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's 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")

вот сгенерированный сюжет.


Не знаю, если вы уже разобрались с вашим вопросом...возможно, да, но..... Я просто использовал строку "снаружи" для местоположения, как в 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)

Нажмите, чтобы увидеть на земельный участок