Повторяющиеся элементы в легенде в matplotlib?

Я пытаюсь добавить легенду к своему сюжету с помощью этого фрагмента:

import matplotlib.pylab as plt

fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
for i, representative in enumerate(representatives):
    axes.plot([e[0] for e in representative], [e[1] for e in representative], color='b', label='Representatives')
axes.scatter([e[0] for e in intersections], [e[1] for e in intersections], color='r', label='Intersections')
axes.legend()   

Я в конечном итоге с этим сюжетом

Очевидно, элементы дублируются в сюжете. Как я могу исправить эту ошибку?

6 ответов

Решение

Как говорят документы, хотя это легко пропустить:

Если атрибут label представляет собой пустую строку или начинается с "_", эти исполнители будут игнорироваться.

Так что, если я строю похожие линии в цикле и мне нужна только одна примерная линия в легенде, я обычно делаю что-то вроде

ax.plot(x, y, label="Representatives" if i == 0 else "")

где i мой индекс цикла

Это не так приятно, как строить их отдельно, но часто я хочу, чтобы логика надписей была как можно ближе к рисованию линий.

(Обратите внимание, что matplotlib Сами разработчики склонны использовать "_nolegend_" быть явным.)

Основываясь на ответе EL_DON, вот общий метод рисования легенды без дублирующих меток:

def legend_without_duplicate_labels(ax):
    handles, labels = ax.get_legend_handles_labels()
    unique = [(h, l) for i, (h, l) in enumerate(zip(handles, labels)) if l not in labels[:i]]
    ax.legend(*zip(*unique))

Пример использования: ( открыть в repl.it)

fig, ax = plt.subplots()

ax.plot([0,1], [0,1], c="y", label="my lines")
ax.plot([0,1], [0,2], c="y", label="my lines")

legend_without_duplicate_labels(ax)

plt.show()

Вот метод для удаления дублированных записей легенды после того, как обычно уже назначены метки:

representatives=[[[-100,40],[-50,20],[0,0],[75,-5],[100,5]], #made up some data
                 [[-60,80],[0,85],[100,90]],
                 [[-60,15],[-50,90]],
                 [[-2,-2],[5,95]]]
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
for i, representative in enumerate(representatives):
    axes.plot([e[0] for e in representative], [e[1] for e in representative],color='b', label='Representatives')
#make sure only unique labels show up (no repeats)
handles,labels=axes.get_legend_handles_labels() #get existing legend item handles and labels
i=arange(len(labels)) #make an index for later
filter=array([]) #set up a filter (empty for now)
unique_labels=tolist(set(labels)) #find unique labels
for ul in unique_labels: #loop through unique labels
    filter=np.append(filter,[i[array(labels)==ul][0]]) #find the first instance of this label and add its index to the filter
handles=[handles[int(f)] for f in filter] #filter out legend items to keep only the first instance of each repeated label
labels=[labels[int(f)] for f in filter]
axes.legend(handles,labels) #draw the legend with the filtered handles and labels lists

И вот результаты: Слева результат сценария выше. Справа, вызов легенды был заменен на axes.legend(),

Преимущество в том, что вы можете просматривать большую часть своего кода и просто назначать метки как обычно, не беспокоясь о встроенных циклах или ifs. Вы также можете встроить это в обертку вокруг легенды или что-то в этом роде.

Вот функция для добавления легенды к вашей фигуре без дубликатов:

      def legend_without_duplicate_labels(figure):
    handles, labels = plt.gca().get_legend_handles_labels()
    by_label = dict(zip(labels, handles))
    figure.legend(by_label.values(), by_label.keys(), loc='lower right')

Затем мы можем использовать его в примере ниже:

      import matplotlib.pyplot as plt

plt.plot([0,3], [0,1], c="red", label="line")
plt.plot([0,3], [0,2], c="red", label="line")
legend_without_duplicate_labels(plt)
plt.show()

Результат


Это не ошибка. Ваш ярлык внутри for цикл добавляет len(representatives)-1 повторяющиеся надписи к вашей легенде. Что делать, если вместо этого вы сделали что-то вроде

for i, representative in enumerate(representatives):
    rep, = axes.plot([e[0] for e in representative], [e[1] for e in representative], color='b')
inter = axes.scatter([e[0] for e in intersections], [e[1] for e in intersections], color='r')
axes.legend((rep, inter), ("Representatives", "Intersections"))

Редактировать: формат приведенного ниже кода использует формат, опубликованный в руководстве по легенде matplotlib. Причина, по которой вышеприведенный код не удался, заключается в том, что после пропущенной rep, =, Каждая итерация, rep перезаписывается и когда он используется для вызова legendтолько последний представитель участка хранится в rep,

fig = plt.figure()
ax = fig.add_subplot(111)
for i, representative in enumerate(representatives):
    rep, = ax.plot([e[0] for e in representative], [e[1] for e in representative], color='b')
inter = ax.scatter([e[0] for e in intersections], [e[1] for e in intersections], color='r')
ax.legend((rep, inter), ("Representatives", "Intersections"))

Вы также можете попробовать представить данные так, как вы это делаете в своем OP, но сделайте легенду, используя

handles, labels = ax.get_legend_handles_labels()

и редактирование содержимого handles а также labels,

Согласно предыдущим ответам, я решил свою аналогичную проблему, используя список, как показано ниже:

plotted = []
for class_label in classes:
    if class_label == class_label:
        if label not in plotted:
            plt.scatter(x, y, label=class_label)
            plotted.append(label)
        else:
            plt.scatter(x, y)

надеюсь быть кому-то полезным:)

Другие вопросы по тегам