Guida alla leggenda #

Generare legende in modo flessibile in Matplotlib.

Questa guida alla legenda è un'estensione della documentazione disponibile all'indirizzo legend(): assicurati di conoscere i contenuti di tale documentazione prima di procedere con questa guida.

Questa guida fa uso di alcuni termini comuni, qui documentati per maggiore chiarezza:

voce legenda #

Una legenda è composta da una o più voci della legenda. Una voce è composta esattamente da una chiave e da un'etichetta.

chiave legenda #

L'indicatore colorato/motivo a sinistra di ogni etichetta della legenda.

etichetta legenda #

Il testo che descrive l'handle rappresentato dalla chiave.

maniglia della legenda #

L'oggetto originale utilizzato per generare una voce appropriata nella legenda.

Controllo delle voci della legenda #

La chiamata legend()senza argomenti recupera automaticamente gli handle della legenda e le etichette associate. Questa funzionalità equivale a:

handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)

La get_legend_handles_labels()funzione restituisce un elenco di handle/artisti esistenti sugli assi che possono essere utilizzati per generare voci per la legenda risultante - vale la pena notare tuttavia che non tutti gli artisti possono essere aggiunti a una legenda, a quel punto un "proxy" verrà devono essere creati (vedi Creazione di artisti appositamente per l'aggiunta alla legenda (ovvero artisti proxy) per ulteriori dettagli).

Nota

Gli artisti con una stringa vuota come etichetta o con un'etichetta che inizia con un carattere di sottolineatura, "_", verranno ignorati.

Per il controllo completo di ciò che viene aggiunto alla legenda, è comune passare gli handle appropriati direttamente 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])

In alcuni casi non è possibile impostare l'etichetta dell'handle, quindi è possibile passare dall'elenco delle etichette 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'])

Creazione di artisti appositamente per l'aggiunta alla legenda (ovvero artisti proxy) #

Non tutte le maniglie possono essere trasformate automaticamente in voci di legenda, quindi è spesso necessario creare un artista che possa . Le maniglie della legenda non devono esistere sulla figura o sugli assi per essere utilizzate.

Supponiamo di voler creare una legenda che abbia una voce per alcuni dati che è rappresentata da un colore rosso:

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()
guida alla leggenda

Esistono molti handle di legenda supportati. Invece di creare una macchia di colore avremmo potuto creare una linea con un pennarello:

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()
guida alla leggenda

Posizione della legenda n.

La posizione della legenda può essere specificata dall'argomento della parola chiave loc . Consultare la documentazione all'indirizzo legend()per ulteriori dettagli.

La bbox_to_anchorparola chiave offre un elevato grado di controllo per il posizionamento manuale della legenda. Ad esempio, se desideri che la legenda degli assi si trovi nell'angolo in alto a destra della figura anziché nell'angolo degli assi, specifica semplicemente la posizione dell'angolo e il sistema di coordinate di quella posizione:

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

Altri esempi di posizionamento personalizzato della legenda:

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()
guida alla leggenda

Più legende sugli stessi assi #

A volte è più chiaro suddividere le voci della legenda in più legende. Mentre l'approccio istintivo per farlo potrebbe essere quello di chiamare la legend()funzione più volte, scoprirai che esiste solo una leggenda sugli assi. Ciò è stato fatto in modo che sia possibile richiamare legend()ripetutamente per aggiornare la legenda agli ultimi handle sugli assi. Per mantenere le vecchie istanze della legenda, dobbiamo aggiungerle manualmente agli assi:

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()
guida alla leggenda

Gestori della leggenda n.

Per creare voci di legenda, gli handle vengono forniti come argomento a una HandlerBasesottoclasse appropriata. La scelta della sottoclasse del gestore è determinata dalle seguenti regole:

  1. Aggiorna get_legend_handler_map() con il valore nella handler_mapparola chiave.

  2. Controlla se handleè nel file handler_map.

  3. Controlla se il tipo di handleè nel file handler_map.

  4. Controlla se qualcuno dei tipi in handle's mro è nel file appena creato handler_map.

Per completezza, questa logica è per lo più implementata in get_legend_handler().

Tutta questa flessibilità significa che abbiamo gli hook necessari per implementare gestori personalizzati per il nostro tipo di chiave legenda.

L'esempio più semplice di utilizzo di gestori personalizzati consiste nell'istanziare una delle legend_handler.HandlerBasesottoclassi esistenti. Per semplicità, scegliamo legend_handler.HandlerLine2D quale accetta un argomento numpoints (numpoints è anche una parola chiave della legend()funzione per comodità). Possiamo quindi passare la mappatura dell'istanza a Handler come parola chiave a legend.

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)})
guida alla leggenda
<matplotlib.legend.Legend object at 0x7f2cf9a16ef0>

Come puoi vedere, "Linea 1" ora ha 4 punti marcatore, mentre "Linea 2" ne ha 2 (impostazione predefinita). Prova il codice sopra, cambia solo la chiave della mappa da line1a type(line1). Nota come ora entrambe le Line2Distanze ottengono 4 marcatori.

Insieme ai gestori per tipi di grafici complessi come barre di errore, grafici stem e istogrammi, l'impostazione predefinita handler_mapha un tuplegestore speciale ( legend_handler.HandlerTuple) che traccia semplicemente le maniglie una sopra l'altra per ogni elemento nella tupla data. L'esempio seguente mostra la combinazione di due tasti legenda uno sopra l'altro:

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"])
guida alla leggenda
<matplotlib.legend.Legend object at 0x7f2cfb693760>

La legend_handler.HandlerTupleclasse può anche essere utilizzata per assegnare più chiavi di legenda alla stessa voce:

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)})
guida alla leggenda

Implementazione di un gestore di legende personalizzato #

È possibile implementare un gestore personalizzato per trasformare qualsiasi handle in una chiave di legenda (gli handle non devono necessariamente essere artisti matplotlib). Il gestore deve implementare un legend_artistmetodo che restituisca un singolo artista per la legenda da utilizzare. La firma richiesta per legend_artistè documentata in 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()})
guida alla leggenda
<matplotlib.legend.Legend object at 0x7f2cddb26a10>

In alternativa, se avessimo voluto accettare globalmente le AnyObjectistanze senza dover impostare manualmente la parola chiave handler_map per tutto il tempo, avremmo potuto registrare il nuovo gestore con:

from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})

Sebbene il potere qui sia chiaro, ricorda che ci sono già molti gestori implementati e ciò che vuoi ottenere potrebbe già essere facilmente possibile con le classi esistenti. Ad esempio, per produrre chiavi di legenda ellittiche, anziché rettangolari:

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()})
guida alla leggenda
<matplotlib.legend.Legend object at 0x7f2d00dde710>

Tempo di esecuzione totale dello script: (0 minuti 3,053 secondi)

Galleria generata da Sphinx-Gallery