Observação
Clique aqui para baixar o código de exemplo completo
Guia de legenda #
Gerando legendas de forma flexível no Matplotlib.
Este guia de legendas é uma extensão da documentação disponível em
legend()
- verifique se você está familiarizado com o conteúdo dessa documentação antes de prosseguir com este guia.
Este guia faz uso de alguns termos comuns, que estão documentados aqui para maior clareza:
- entrada da legenda #
Uma legenda é composta de uma ou mais entradas de legenda. Uma entrada é composta por exatamente uma chave e um rótulo.
- chave da legenda #
O marcador colorido/padronizado à esquerda de cada legenda.
- rótulo da legenda #
O texto que descreve o identificador representado pela chave.
- identificador de legenda #
O objeto original que é usado para gerar uma entrada apropriada na legenda.
Controlando as entradas de legenda #
Chamar legend()
sem argumentos busca automaticamente os identificadores de legenda e seus rótulos associados. Esta funcionalidade é equivalente a:
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
A get_legend_handles_labels()
função retorna uma lista de manipuladores/artistas que existem nos Axes que podem ser usados para gerar entradas para a legenda resultante - vale a pena notar, entretanto, que nem todos os artistas podem ser adicionados a uma legenda, ponto em que um "proxy" será devem ser criados (consulte Criando artistas especificamente para adicionar à legenda (aka. Artistas proxy) para obter mais detalhes).
Observação
Artistas com uma string vazia como rótulo ou com um rótulo começando com um sublinhado, "_", serão ignorados.
Para controle total do que está sendo adicionado à legenda, é comum passar os handles apropriados diretamente para legend()
:
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles=[line_up, line_down])
Em alguns casos, não é possível definir o rótulo do identificador, portanto, é possível passar pela lista de rótulos para legend()
:
fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend([line_up, line_down], ['Line Up', 'Line Down'])
Criando artistas especificamente para adicionar à legenda (aka. Artistas proxy) #
Nem todos os identificadores podem ser transformados em entradas de legenda automaticamente, portanto, muitas vezes é necessário criar um artista que possa . Os identificadores de legenda não precisam existir na Figura ou nos Eixos para serem usados.
Suponha que queremos criar uma legenda que tenha uma entrada para alguns dados representados por uma cor vermelha:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
red_patch = mpatches.Patch(color='red', label='The red data')
ax.legend(handles=[red_patch])
plt.show()
Existem muitos identificadores de legenda suportados. Em vez de criar um patch de cor, poderíamos ter criado uma linha com um marcador:
import matplotlib.lines as mlines
fig, ax = plt.subplots()
blue_line = mlines.Line2D([], [], color='blue', marker='*',
markersize=15, label='Blue stars')
ax.legend(handles=[blue_line])
plt.show()
Localização da legenda #
A localização da legenda pode ser especificada pelo argumento de palavra-chave
loc . Consulte a documentação em legend()
para obter mais detalhes.
A bbox_to_anchor
palavra-chave fornece um alto grau de controle para o posicionamento manual da legenda. Por exemplo, se você deseja que a legenda dos eixos seja localizada no canto superior direito da figura em vez do canto dos eixos, basta especificar a localização do canto e o sistema de coordenadas desse local:
Mais exemplos de colocação de legenda personalizada:
fig, ax_dict = plt.subplot_mosaic([['top', 'top'], ['bottom', 'BLANK']],
empty_sentinel="BLANK")
ax_dict['top'].plot([1, 2, 3], label="test1")
ax_dict['top'].plot([3, 2, 1], label="test2")
# Place a legend above this subplot, expanding itself to
# fully use the given bounding box.
ax_dict['top'].legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left',
ncol=2, mode="expand", borderaxespad=0.)
ax_dict['bottom'].plot([1, 2, 3], label="test1")
ax_dict['bottom'].plot([3, 2, 1], label="test2")
# Place a legend to the right of this smaller subplot.
ax_dict['bottom'].legend(bbox_to_anchor=(1.05, 1),
loc='upper left', borderaxespad=0.)
plt.show()
Múltiplas legendas nos mesmos eixos #
Às vezes é mais claro dividir as entradas de legenda em várias legendas. Embora a abordagem instintiva para fazer isso possa ser chamar a legend()
função várias vezes, você descobrirá que apenas uma legenda existe nos eixos. Isso foi feito para que seja possível chamar legend()
repetidamente para atualizar a legenda para os últimos manipuladores dos eixos. Para manter as instâncias de legendas antigas, devemos adicioná-las manualmente aos Axes:
fig, ax = plt.subplots()
line1, = ax.plot([1, 2, 3], label="Line 1", linestyle='--')
line2, = ax.plot([3, 2, 1], label="Line 2", linewidth=4)
# Create a legend for the first line.
first_legend = ax.legend(handles=[line1], loc='upper right')
# Add the legend manually to the Axes.
ax.add_artist(first_legend)
# Create another legend for the second line.
ax.legend(handles=[line2], loc='lower right')
plt.show()
Manipuladores de legendas #
Para criar entradas de legenda, os manipuladores são fornecidos como um argumento para uma HandlerBase
subclasse apropriada. A escolha da subclasse do manipulador é determinada pelas seguintes regras:
Atualize
get_legend_handler_map()
com o valor na palavra-handler_map
chave.Verifique se o
handle
está no arquivohandler_map
.Verifique se o tipo de
handle
está no arquivohandler_map
.Verifique se algum dos tipos no
handle
mro está no recém-criadohandler_map
.
Para completar, essa lógica é implementada principalmente em
get_legend_handler()
.
Toda essa flexibilidade significa que temos os ganchos necessários para implementar manipuladores personalizados para nosso próprio tipo de chave de legenda.
O exemplo mais simples de uso de manipuladores personalizados é instanciar uma das legend_handler.HandlerBase
subclasses existentes. Para simplificar, vamos escolher legend_handler.HandlerLine2D
qual aceita um argumento numpoints (numpoints também é uma palavra-chave na legend()
função por conveniência). Podemos então passar o mapeamento da instância para o Handler como uma palavra-chave para a legenda.
from matplotlib.legend_handler import HandlerLine2D
fig, ax = plt.subplots()
line1, = ax.plot([3, 2, 1], marker='o', label='Line 1')
line2, = ax.plot([1, 2, 3], marker='o', label='Line 2')
ax.legend(handler_map={line1: HandlerLine2D(numpoints=4)})
<matplotlib.legend.Legend object at 0x7f2cf9a16ef0>
Como você pode ver, a "Linha 1" agora tem 4 marcadores, onde a "Linha 2" tem 2 (o padrão). Tente o código acima, apenas altere a chave do mapa de line1
para
type(line1)
. Observe como agora ambas as Line2D
instâncias obtêm 4 marcadores.
Juntamente com manipuladores para tipos complexos de plotagem, como barras de erro, plotagens de haste e histogramas, o padrão handler_map
possui um tuple
manipulador especial ( legend_handler.HandlerTuple
) que simplesmente plota os manipuladores uns sobre os outros para cada item na tupla especificada. O exemplo a seguir demonstra a combinação de duas chaves de legenda uma sobre a outra:
from numpy.random import randn
z = randn(10)
fig, ax = plt.subplots()
red_dot, = ax.plot(z, "ro", markersize=15)
# Put a white cross over some of the data.
white_cross, = ax.plot(z[:5], "w+", markeredgewidth=3, markersize=15)
ax.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"])
<matplotlib.legend.Legend object at 0x7f2cfb693760>
A legend_handler.HandlerTuple
classe também pode ser usada para atribuir várias chaves de legenda à mesma entrada:
from matplotlib.legend_handler import HandlerLine2D, HandlerTuple
fig, ax = plt.subplots()
p1, = ax.plot([1, 2.5, 3], 'r-d')
p2, = ax.plot([3, 2, 1], 'k-o')
l = ax.legend([(p1, p2)], ['Two keys'], numpoints=1,
handler_map={tuple: HandlerTuple(ndivide=None)})
Implementando um manipulador de legenda personalizado #
Um manipulador personalizado pode ser implementado para transformar qualquer identificador em uma chave de legenda (os identificadores não precisam necessariamente ser artistas matplotlib). O manipulador deve implementar um legend_artist
método que retorne um único artista para a legenda usar. A assinatura necessária para legend_artist
está documentada em
legend_artist
.
import matplotlib.patches as mpatches
class AnyObject:
pass
class AnyObjectHandler:
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
x0, y0 = handlebox.xdescent, handlebox.ydescent
width, height = handlebox.width, handlebox.height
patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red',
edgecolor='black', hatch='xx', lw=3,
transform=handlebox.get_transform())
handlebox.add_artist(patch)
return patch
fig, ax = plt.subplots()
ax.legend([AnyObject()], ['My first handler'],
handler_map={AnyObject: AnyObjectHandler()})
<matplotlib.legend.Legend object at 0x7f2cddb26a10>
Como alternativa, se quiséssemos aceitar AnyObject
instâncias globalmente sem precisar definir manualmente a palavra-chave handler_map o tempo todo, poderíamos ter registrado o novo manipulador com:
from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
Embora o poder aqui seja claro, lembre-se de que já existem muitos manipuladores implementados e o que você deseja alcançar já pode ser facilmente possível com as classes existentes. Por exemplo, para produzir chaves de legenda elípticas, em vez de retangulares:
from matplotlib.legend_handler import HandlerPatch
class HandlerEllipse(HandlerPatch):
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
p = mpatches.Ellipse(xy=center, width=width + xdescent,
height=height + ydescent)
self.update_prop(p, orig_handle, legend)
p.set_transform(trans)
return [p]
c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green",
edgecolor="red", linewidth=3)
fig, ax = plt.subplots()
ax.add_patch(c)
ax.legend([c], ["An ellipse, not a rectangle"],
handler_map={mpatches.Circle: HandlerEllipse()})
<matplotlib.legend.Legend object at 0x7f2d00dde710>
Tempo total de execução do script: (0 minutos 3,053 segundos)