Noter
Cliquez ici pour télécharger l'exemple de code complet
Guide de légende #
Générer des légendes de manière flexible dans Matplotlib.
Ce guide de légende est une extension de la documentation disponible sur
legend()
- veuillez vous assurer que vous êtes familiarisé avec le contenu de cette documentation avant de continuer avec ce guide.
Ce guide utilise certains termes courants, qui sont documentés ici pour plus de clarté :
- entrée de légende #
Une légende est composée d'une ou plusieurs entrées de légende. Une entrée est composée exactement d'une clé et d'un libellé.
- clé de légende #
Marqueur coloré/motif à gauche de chaque étiquette de légende.
- étiquette de légende #
Le texte qui décrit le handle représenté par la clé.
- poignée de légende #
L'objet d'origine qui est utilisé pour générer une entrée appropriée dans la légende.
Contrôle des entrées de légende #
L'appel legend()
sans argument récupère automatiquement les poignées de légende et leurs étiquettes associées. Cette fonctionnalité équivaut à :
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
La get_legend_handles_labels()
fonction renvoie une liste de poignées/artistes qui existent sur les axes qui peuvent être utilisés pour générer des entrées pour la légende résultante - il convient de noter cependant que tous les artistes ne peuvent pas être ajoutés à une légende, auquel cas un "proxy" sera doivent être créés (voir Création d'artistes spécifiquement pour l'ajout à la légende (aka. Artistes mandataires) pour plus de détails).
Noter
Les artistes avec une chaîne vide comme étiquette ou avec une étiquette commençant par un trait de soulignement, "_", seront ignorés.
Pour un contrôle total de ce qui est ajouté à la légende, il est courant de passer les poignées appropriées directement à 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])
Dans certains cas, il n'est pas possible de définir le libellé de la poignée, il est donc possible de passer par la liste des libellés pour 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'])
Créer des artistes spécifiquement pour ajouter à la légende (aka. Artistes proxy) #
Toutes les poignées ne peuvent pas être automatiquement transformées en entrées de légende, il est donc souvent nécessaire de créer un artiste qui peut . Les poignées de légende ne doivent pas nécessairement exister sur la figure ou les axes pour être utilisées.
Supposons que nous voulions créer une légende contenant une entrée pour certaines données représentées par une couleur rouge :
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()
Il existe de nombreuses poignées de légende prises en charge. Au lieu de créer un patch de couleur, nous aurions pu créer une ligne avec un marqueur :
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()
Emplacement de la légende #
L'emplacement de la légende peut être spécifié par l'argument mot-clé
loc . Veuillez consulter la documentation sur legend()
pour plus de détails.
Le bbox_to_anchor
mot-clé donne un grand degré de contrôle pour le placement manuel de la légende. Par exemple, si vous souhaitez que la légende de vos axes soit située dans le coin supérieur droit de la figure au lieu du coin des axes, spécifiez simplement l'emplacement du coin et le système de coordonnées de cet emplacement :
Autres exemples de placement de légende personnalisé :
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()
Légendes multiples sur les mêmes Axes #
Parfois, il est plus clair de répartir les entrées de légende sur plusieurs légendes. Bien que l'approche instinctive pour ce faire puisse être d'appeler la legend()
fonction plusieurs fois, vous constaterez qu'une seule légende existe sur les Axes. Cela a été fait pour qu'il soit possible d'appeler à legend()
plusieurs reprises pour mettre à jour la légende avec les dernières poignées sur les axes. Pour conserver les anciennes instances de légende, nous devons les ajouter manuellement aux 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()
Gestionnaires de légende #
Afin de créer des entrées de légende, des poignées sont données comme argument à une HandlerBase
sous-classe appropriée. Le choix de la sous-classe de gestionnaire est déterminé par les règles suivantes :
Mettez à jour
get_legend_handler_map()
avec la valeur du mot-handler_map
clé.Vérifiez si le
handle
est dans le nouveau fichierhandler_map
.Vérifiez si le type de
handle
se trouve dans le nouveau fichierhandler_map
.Vérifiez si l'un des types du
handle
mro du est dans le nouveau fichierhandler_map
.
Pour être complet, cette logique est principalement implémentée dans
get_legend_handler()
.
Toute cette flexibilité signifie que nous avons les crochets nécessaires pour implémenter des gestionnaires personnalisés pour notre propre type de clé de légende.
legend_handler.HandlerBase
L'exemple le plus simple d'utilisation de gestionnaires personnalisés consiste à instancier l'une des sous-classes existantes . Par souci de simplicité, choisissons legend_handler.HandlerLine2D
celui qui accepte un argument numpoints (numpoints est également un mot clé sur la legend()
fonction pour plus de commodité). Nous pouvons ensuite transmettre le mappage d'instance à Handler en tant que mot-clé de légende.
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>
Comme vous pouvez le voir, "Ligne 1" a maintenant 4 points de repère, où "Ligne 2" en a 2 (la valeur par défaut). Essayez le code ci-dessus, changez uniquement la clé de la carte de line1
à
type(line1)
. Remarquez comment les deux Line2D
instances obtiennent maintenant 4 marqueurs.
En plus des gestionnaires pour les types de tracés complexes tels que les barres d'erreur, les tracés de tiges et les histogrammes, la valeur par défaut handler_map
a un tuple
gestionnaire spécial ( legend_handler.HandlerTuple
) qui trace simplement les poignées les unes sur les autres pour chaque élément du tuple donné. L'exemple suivant illustre la combinaison de deux clés de légende l'une sur l'autre :
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
classe peut également être utilisée pour affecter plusieurs clés de légende à une même entrée :
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)})
Implémentation d'un gestionnaire de légende personnalisé #
Un gestionnaire personnalisé peut être implémenté pour transformer n'importe quelle poignée en clé de légende (les poignées n'ont pas nécessairement besoin d'être des artistes matplotlib). Le gestionnaire doit implémenter une legend_artist
méthode qui renvoie un seul artiste pour la légende à utiliser. La signature requise pour legend_artist
est documentée à
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>
Alternativement, si nous avions voulu accepter globalement les AnyObject
instances sans avoir à définir manuellement le mot-clé handler_map tout le temps, nous aurions pu enregistrer le nouveau gestionnaire avec :
from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})
Bien que la puissance ici soit claire, rappelez-vous qu'il existe déjà de nombreux gestionnaires implémentés et que ce que vous voulez réaliser peut déjà être facilement réalisable avec les classes existantes. Par exemple, pour produire des clés de légende elliptiques plutôt que rectangulaires :
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>
Durée totale d'exécution du script : (0 minutes 3,053 secondes)