【问题标题】:Duplicate items in legend in matplotlib?matplotlib中图例中的重复项目?
【发布时间】:2013-10-15 15:51:47
【问题描述】:

我正在尝试使用这个 sn-p 将图例添加到我的情节中:

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()   

我结束了这个情节

显然,这些项目在图中重复。我该如何纠正这个错误?

【问题讨论】:

  • 这不是一个错误,它添加了许多重复的条目,因为标签是相同的。如果必须更改 for 循环内的标签...
  • 这是您问题的一个很好的答案:stackoverflow.com/questions/13588920/…

标签: python matplotlib


【解决方案1】:

正如docs 所说,虽然很容易错过:

如果标签属性为空字符串或以“_”开头,那些艺术家 将被忽略。

因此,如果我在一个循环中绘制相似的线,并且我只想要图例中的一个示例线,我通常会这样做

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

i 是我的循环索引。

看起来不如单独构建它们好,但通常我希望标签逻辑尽可能接近线条图。

(请注意,matplotlib 开发人员自己倾向于使用 "_nolegend_" 来表示明确。)

【讨论】:

  • 不错的见解。 i==0 确实可以解决问题。但是分散的东西不能通过i==0来解决,因为它不在循环中。我怎样才能让它变得完美? i.stack.imgur.com/B3osh.jpg
  • @mavErick:对不起,我没有关注。图例中只有一个Intersections 行。
  • 是的,完全正确。但是为什么我在图例中有三个红点?不应该只有一个吗?
  • @macErick:啊,这完全是一个不同的问题。这只是一个奇怪的matplotlib 约定。查看我的回答here:您可以改用axes.legend(scatterpoints=1) 来更改号码。
  • 太棒了!问题解决了。但老实说,我很喜欢this answer 说明的方式。但它会引发错误。如果可以修复错误,那对我来说似乎更“标准”。
【解决方案2】:

基于the answer by 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))

示例用法: (open in 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()

【讨论】:

  • 它对我有用,我必须使用 stackoverflow.com/a/50947068/9020163 对唯一列表中的项目进行排序
  • 按字母顺序排列标签?
  • 完全正确。我对路径的不同“高度”使用不同的颜色。线的每一段可以多次处于同一高度,因此图例显示了多次相同的高度条目。删除重复项会扰乱值。
  • 它以与绘制相同的顺序将项目添加到图例中,但当标签与现有标签匹配时它会跳过添加项目。因此,您也可以通过对绘制操作进行排序来控制顺序。
  • 太棒了!这是最不临时和最通用的解决方案!
【解决方案3】:

这是一个为您的图形添加图例而不重复的功能:

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()

结果


【讨论】:

    【解决方案4】:

    这不是错误。您在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 legend tutorial 上发布的格式。上面代码失败的原因是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()
    

    并编辑handleslabels的内容。

    【讨论】:

      【解决方案5】:

      以下是在已经正常分配标签后删除重复图例条目的方法:

      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。您还可以将其构建到图例或类似内容的包装器中。

      【讨论】:

      • 感谢您,这提供了一个快速修复,而无需修改添加行背后的逻辑。
      【解决方案6】:

      根据之前的答案,我使用如下列表解决了我的类似问题:

      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)
      

      希望对某人有所帮助:)

      【讨论】:

        猜你喜欢
        • 2016-01-30
        • 2021-11-01
        • 2014-09-01
        • 2015-08-05
        • 2018-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多