Nota
Haga clic aquí para descargar el código de ejemplo completo
Leyenda guía #
Generación de leyendas de forma flexible en Matplotlib.
Esta guía de leyendas es una extensión de la documentación disponible en
legend()
: asegúrese de estar familiarizado con el contenido de esa documentación antes de continuar con esta guía.
Esta guía utiliza algunos términos comunes, que se documentan aquí para mayor claridad:
- entrada de leyenda #
Una leyenda se compone de una o más entradas de leyenda. Una entrada se compone exactamente de una clave y una etiqueta.
- clave de leyenda #
El marcador de color/diseño a la izquierda de cada etiqueta de leyenda.
- etiqueta de leyenda #
El texto que describe el mango representado por la llave.
- identificador de leyenda #
El objeto original que se utiliza para generar una entrada adecuada en la leyenda.
Controlando las entradas de la leyenda #
Llamar legend()
sin argumentos obtiene automáticamente los identificadores de leyenda y sus etiquetas asociadas. Esta funcionalidad es equivalente a:
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
La get_legend_handles_labels()
función devuelve una lista de identificadores/artistas que existen en los ejes que se pueden usar para generar entradas para la leyenda resultante; sin embargo, vale la pena señalar que no todos los artistas se pueden agregar a una leyenda, momento en el que aparecerá un "proxy". deben crearse (consulte Crear artistas específicamente para agregarlos a la leyenda (también conocidos como artistas proxy) para obtener más detalles).
Nota
Los artistas con una cadena vacía como etiqueta o con una etiqueta que comience con un guión bajo, "_", serán ignorados.
Para un control total de lo que se agrega a la leyenda, es común pasar los identificadores apropiados directamente a 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])
En algunos casos, no es posible establecer la etiqueta del identificador, por lo que es posible pasar por la lista de etiquetas a 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'])
Crear artistas específicamente para agregar a la leyenda (también conocidos como artistas proxy) #
No todos los identificadores se pueden convertir automáticamente en entradas de leyenda, por lo que a menudo es necesario crear un artista que pueda . No es necesario que existan identificadores de leyenda en la figura o los ejes para poder utilizarlos.
Supongamos que quisiéramos crear una leyenda que tiene una entrada para algunos datos que se representa con un color rojo:
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()
Hay muchos identificadores de leyenda admitidos. En lugar de crear un parche de color, podríamos haber creado una línea con un 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()
Leyenda ubicación #
La ubicación de la leyenda se puede especificar mediante el argumento de palabra clave
loc . Consulte la documentación en legend()
para obtener más detalles.
La bbox_to_anchor
palabra clave brinda un alto grado de control para la ubicación manual de la leyenda. Por ejemplo, si desea que la leyenda de sus ejes se ubique en la esquina superior derecha de la figura en lugar de la esquina de los ejes, simplemente especifique la ubicación de la esquina y el sistema de coordenadas de esa ubicación:
Más ejemplos de colocación de leyendas personalizadas:
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últiples leyendas en los mismos ejes #
A veces es más claro dividir las entradas de la leyenda en varias leyendas. Si bien el enfoque instintivo para hacer esto podría ser llamar a la legend()
función varias veces, descubrirá que solo existe una leyenda en los ejes. Esto se ha hecho para que sea posible llamar legend()
repetidamente para actualizar la leyenda a los identificadores más recientes de los ejes. Para mantener instancias de leyendas antiguas, debemos agregarlas manualmente a los Ejes:
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()
Controladores de leyenda #
Para crear entradas de leyenda, los identificadores se proporcionan como argumento para una HandlerBase
subclase apropiada. La elección de la subclase de controlador está determinada por las siguientes reglas:
Actualice
get_legend_handler_map()
con el valor de lahandler_map
palabra clave.Compruebe si
handle
está en el archivo recién creadohandler_map
.Compruebe si el tipo de
handle
está en el recién creadohandler_map
.Compruebe si alguno de los tipos en el
handle
mro está en el recién creadohandler_map
.
Para completar, esta lógica se implementa principalmente en
get_legend_handler()
.
Toda esta flexibilidad significa que tenemos los ganchos necesarios para implementar controladores personalizados para nuestro propio tipo de clave de leyenda.
El ejemplo más simple del uso de controladores personalizados es instanciar una de las legend_handler.HandlerBase
subclases existentes. En aras de la simplicidad, elijamos legend_handler.HandlerLine2D
cuál acepta un argumento numpoints (numpoints también es una palabra clave en la legend()
función por conveniencia). Luego podemos pasar el mapeo de la instancia a Handler como una palabra clave a la leyenda.
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 puede ver, "Línea 1" ahora tiene 4 puntos de marcador, donde "Línea 2" tiene 2 (el valor predeterminado). Pruebe el código anterior, solo cambie la clave del mapa de line1
a
type(line1)
. Observe cómo ahora ambas Line2D
instancias obtienen 4 marcadores.
Junto con los controladores para tipos de gráficos complejos, como barras de error, diagramas de tallo e histogramas, el valor predeterminado handler_map
tiene un tuple
controlador especial ( legend_handler.HandlerTuple
) que simplemente traza los controladores uno encima del otro para cada elemento de la tupla dada. El siguiente ejemplo demuestra la combinación de dos claves de leyenda una encima de la otra:
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>
La legend_handler.HandlerTuple
clase también se puede utilizar para asignar varias claves de leyenda a la misma 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 un controlador de leyenda personalizado #
Se puede implementar un controlador personalizado para convertir cualquier controlador en una clave de leyenda (los controladores no necesariamente tienen que ser artistas de matplotlib). El controlador debe implementar un legend_artist
método que devuelva un solo artista para que lo use la leyenda. La firma requerida para legend_artist
está documentada en
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>
Alternativamente, si hubiéramos querido aceptar AnyObject
instancias globalmente sin tener que configurar manualmente la palabra clave handler_map todo el tiempo, podríamos haber registrado el nuevo controlador con:
from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
Si bien el poder aquí es claro, recuerde que ya hay muchos controladores implementados y lo que desea lograr ya puede ser fácilmente posible con las clases existentes. Por ejemplo, para producir claves de leyenda elípticas, en lugar de rectangulares:
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>
Tiempo total de ejecución del script: ( 0 minutos 3.053 segundos)