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()
guía de leyenda

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()
guía de leyenda

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_anchorpalabra 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:

ax.legend(bbox_to_anchor=(1, 1),
          bbox_transform=fig.transFigure)

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()
guía de leyenda

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()
guía de leyenda

Controladores de leyenda #

Para crear entradas de leyenda, los identificadores se proporcionan como argumento para una HandlerBasesubclase apropiada. La elección de la subclase de controlador está determinada por las siguientes reglas:

  1. Actualice get_legend_handler_map() con el valor de la handler_mappalabra clave.

  2. Compruebe si handleestá en el archivo recién creado handler_map.

  3. Compruebe si el tipo de handleestá en el recién creado handler_map.

  4. Compruebe si alguno de los tipos en el handlemro está en el recién creado handler_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.HandlerBasesubclases 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)})
guía de leyenda
<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 line1a type(line1). Observe cómo ahora ambas Line2Dinstancias 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_maptiene un tuplecontrolador 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"])
guía de leyenda
<matplotlib.legend.Legend object at 0x7f2cfb693760>

La legend_handler.HandlerTupleclase 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)})
guía de leyenda

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_artistmétodo que devuelva un solo artista para que lo use la leyenda. La firma requerida para legend_artistestá 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()})
guía de leyenda
<matplotlib.legend.Legend object at 0x7f2cddb26a10>

Alternativamente, si hubiéramos querido aceptar AnyObjectinstancias 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()})
guía de leyenda
<matplotlib.legend.Legend object at 0x7f2d00dde710>

Tiempo total de ejecución del script: ( 0 minutos 3.053 segundos)

Galería generada por Sphinx-Gallery