92 votes

Arrêter la répétition des étiquettes de matplotlib dans la légende

Voici un exemple très simplifié :

xvalues = [2,3,4,6]

for x in xvalues:
    plt.axvline(x,color='b',label='xvalues')

plt.legend()

enter image description here

La légende montre maintenant 'xvalues' comme une ligne bleue 4 fois dans la légende. Existe-t-il un moyen plus élégant de résoudre ce problème que le suivant ?

for i,x in enumerate(xvalues):
    if not i:
        plt.axvline(x,color='b',label='xvalues')
    else:
        plt.axvline(x,color='b')

enter image description here

166voto

ecatmur Points 64173

plt.legend prend comme paramètres

  1. Une liste de poignées d'axe qui sont Artist objets
  2. Une liste d'étiquettes qui sont des chaînes de caractères

Ces paramètres sont tous deux facultatifs et ont pour valeur par défaut plt.gca().get_legend_handles_labels() . Vous pouvez supprimer les étiquettes dupliquées en les plaçant dans un dictionnaire avant d'appeler legend . C'est parce que les dicts ne peuvent pas avoir de clés dupliquées.

Par exemple :

Pour les versions de Python < 3.7

from collections import OrderedDict
import matplotlib.pyplot as plt

handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())

Pour les versions de Python > 3.7

À partir de Python 3.7, les dictionnaires conservent l'ordre d'entrée par défaut. Ainsi, il n'y a pas besoin de OrderedDict forment le module des collections.

import matplotlib.pyplot as plt

handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())

enter image description here

Docs pour plt.legend

8voto

Matthew Bourque Points 81
handles, labels = ax.get_legend_handles_labels()
handle_list, label_list = [], []
for handle, label in zip(handles, labels):
    if label not in label_list:
        handle_list.append(handle)
        label_list.append(label)
plt.legend(handle_list, label_list)

6voto

bli Points 2369

Je ne sais pas si cela peut être considéré comme "élégant", mais vous pouvez faire en sorte que votre étiquette soit une variable qui prend la valeur suivante "_nolegend_" après la première utilisation :

my_label = "xvalues"
xvalues = [2,3,4,6]

for x in xvalues:
    plt.axvline(x, color='b', label=my_label)
    my_label = "_nolegend_"

plt.legend()

On peut généraliser en utilisant un dictionnaire d'étiquettes si l'on doit mettre plusieurs étiquettes :

my_labels = {"x1" : "x1values", "x2" : "x2values"}
x1values = [1, 3, 5]
x2values = [2, 4, 6]

for x in x1values:
    plt.axvline(x, color='b', label=my_labels["x1"])
    my_labels["x1"] = "_nolegend_"
for x in x2values:
    plt.axvline(x, color='r', label=my_labels["x2"])
    my_labels["x2"] = "_nolegend_"

plt.legend()

Figure with 2 different labels

(Réponse inspirée par https://stackoverflow.com/a/19386045/1878788 )

4voto

A. West Points 342

Problème - Matrice 3D

Questions : Nov. 2012 , Oct. 2013

import numpy as np
a = np.random.random((2, 100, 4))
b = np.random.random((2, 100, 4))
c = np.random.random((2, 100, 4))

Solution - dictée de l'unicité

Dans mon cas _nolegend_ ( bli y DSM ) ne fonctionnerait pas, pas plus que label if i==0 . ecatmur La réponse de l'entreprise utilise get_legend_handles_labels et réduit la légende avec collections.OrderedDict . Fons démontre que cela est possible sans importation.

En accord avec ces réponses, je suggère d'utiliser dict pour des étiquettes uniques.

# Step-by-step
ax = plt.gca()                      # Get the axes you need
a = ax.get_legend_handles_labels()  # a = [(h1 ... h2) (l1 ... l2)]  non unique
b = {l:h for h,l in zip(*a)}        # b = {l1:h1, l2:h2}             unique
c = [*zip(*b.items())]              # c = [(l1 l2) (h1 h2)]
d = c[::-1]                         # d = [(h1 h2) (l1 l2)]
plt.legend(*d)

Ou

plt.legend(*[*zip(*{l:h for h,l in zip(*ax.get_legend_handles_labels())}.items())][::-1])

Peut-être moins lisible et mémorable que Matthew Bourque La solution de l'entreprise. Code golf bienvenu.

Exemple

import numpy as np
a = np.random.random((2, 100, 4))
b = np.random.random((2, 100, 4))

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1)
ax.plot(*a, 'C0', label='a')
ax.plot(*b, 'C1', label='b')

ax.legend(*[*zip(*{l:h for h,l in zip(*ax.get_legend_handles_labels())}.items())][::-1])
# ax.legend()   # Old,  ^ New

plt.show()

3voto

Tobias Wegener Points 31

Basé sur la réponse https://stackoverflow.com/a/13589144/9132798 y https://stackoverflow.com/a/19386045/9132798 plt.gca().get_legend_handles_labels()[1] donne une liste de noms, il est possible de vérifier si l'étiquette est déjà dans la liste pendant la boucle de traçage ( label= name[i] if name[i] not in plt.gca().get_legend_handles_labels()[1] else '' ). Pour l'exemple donné, cette solution se présenterait comme suit :

import matplotlib.pyplot as plt

xvalues = [2,3,4,6]

for x in xvalues:
    plt.axvline(x,color='b',\
    label= 'xvalues' if 'xvalues' \
            not in plt.gca().get_legend_handles_labels()[1] else '')

plt.legend()

Ce qui est beaucoup plus court que https://stackoverflow.com/a/13589144/9132798 et plus souple que https://stackoverflow.com/a/19386045/9132798 car il peut être utilisé pour n'importe quel type de boucle, n'importe quelle fonction de tracé dans la boucle individuellement. Cependant, pour de nombreux cycles, il est probablement plus lent que https://stackoverflow.com/a/13589144/9132798 .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X