web-dev-qa-db-ja.com

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

私はこのプロットで終わる

enter image description here

明らかに、アイテムはプロットで複製されます。このエラーを修正するにはどうすればよいですか?

27
user2881553

docs が言うように、見逃しがちですが:

Label属性が空の文字列であるか、「_」で始まる場合、それらのアーティストは無視されます。

したがって、ループ内で同様のラインをプロットしていて、凡例に1つのサンプルラインだけが必要な場合、通常は次のようにします。

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

ここで、iはループインデックスです。

それらを個別に作成するのと同じように見るのはあまりいいことではありませんが、ラベルのロジックを可能な限り線画に近づけたいと思うことがよくあります。

matplotlib開発者自身が"_nolegend_"は明示的です。)

43
DSM

これは、ラベルを既に正常に割り当てた後で、重複する凡例エントリを削除する方法です。

_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
_

そしてここに結果があります: enter image description here 左側は上記のスクリプトの結果です。右側では、凡例の呼び出しがaxes.legend()に置き換えられています。

利点は、ほとんどのコードを実行してラベルを通常どおりに割り当てるだけで、インラインループやifsを気にする必要がないことです。これを凡例などのラッパーに組み込むこともできます。

7
EL_DON

これはエラーではありません。 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の内容を編集します。

2
wflynny

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

enter image description here

1
Fons