matplotlibで多くのサブプロットの単一の凡例を作成するにはどうすればよいですか?


166

同じタイプの情報をプロットしていますが、matplotlibを使用して複数の国のサブプロットをさまざまな国でプロットしています。つまり、3x3グリッドに9つのプロットがあり、すべてがラインに対して同じです(もちろん、ラインごとに異なる値です)。

ただし、図に1つの凡例(9つのサブプロットすべてに同じ線分があるため)を1回だけ配置する方法はわかりません。

それ、どうやったら出来るの?

回答:


160

引数get_legend_handles_labels()から必要なものすべてを収集する、最後の軸(それらを反復する場合)で呼び出すことができる素晴らしい関数もありlabel=ます。

handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc='upper center')

13
これが一番の答えになるはずです。
naught101

1
これは確かにはるかに有用な答えです!それは、私にとってより複雑なケースでも同じように機能しました。
gmaravel

1
完璧な答え!
Dorgham 2018

4
サブプロットの凡例を削除するにはどうすればよいですか?
BND 2019

5
この素晴らしい答えに追加するだけです。あなたのプロットと必要性に関する二のy軸を持っている場合はそれらを両方使用をマージするには、この:handles, labels = [(a + b) for a, b in zip(ax1.get_legend_handles_labels(), ax2.get_legend_handles_labels())]
ビル・

114

figlegendはあなたが探しているものかもしれません:http ://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

ここに例:http : //matplotlib.org/examples/pylab_examples/figlegend_demo.html

もう一つの例:

plt.figlegend( lines, labels, loc = 'lower center', ncol=5, labelspacing=0. )

または:

fig.legend( lines, labels, loc = (0.5, 0), ncol=5 )

1
凡例に入れたい行を知っていますが、lines変数を引数に入れるにはどうすればよいlegendですか?
patapouf_ai 2017

1
@patapouf_ai linesは、返される結果のリストですaxes.plot()(つまり、各axes.plotルーチンまたは同様のルーチンが「行」を返します)。リンクされた例も参照してください。

17

figure取得されたもののように、多くの軸を持つで単一の凡例を自動配置するsubplots()には、次の解決策が非常にうまく機能します。

plt.legend( lines, labels, loc = 'lower center', bbox_to_anchor = (0,-0.1,1,1),
            bbox_transform = plt.gcf().transFigure )

bbox_to_anchorし、bbox_transform=plt.gcf().transFigureあなたがあなたのサイズの新しいバウンディングボックスを定義するfigureための基準となりますloc。使用して(0,-0.1,1,1)、他のアーティストの上に配置される凡例を防ぐために、わずかに下向きに動くにこのboudingボックスをオンにします。

OBS:使用した後、使用するfig.set_size_inches()前にこのソリューションを使用してくださいfig.tight_layout()


1
または単純化してloc='upper center', bbox_to_anchor=(0.5, 0), bbox_transform=plt.gcf().transFigure、それは確かに重複しません。
Davor Josipovic

2
理由はまだわかりませんが、Evertのソリューションはうまくいきませんでした。伝説が途絶え続けました。このソリューションは(davorのコメントとともに)非常にきれいに機能しました。凡例は予想どおりに配置され、完全に表示されました。ありがとう!
sudo make install

16

ループの外で、凡例を1回要求するだけです。

たとえば、この例では、同じ線と1つの凡例を持つ4つのサブプロットがあります。

from matplotlib.pyplot import *

ficheiros = ['120318.nc', '120319.nc', '120320.nc', '120321.nc']

fig = figure()
fig.suptitle('concentration profile analysis')

for a in range(len(ficheiros)):
    # dados is here defined
    level = dados.variables['level'][:]

    ax = fig.add_subplot(2,2,a+1)
    xticks(range(8), ['0h','3h','6h','9h','12h','15h','18h','21h']) 
    ax.set_xlabel('time (hours)')
    ax.set_ylabel('CONC ($\mu g. m^{-3}$)')

    for index in range(len(level)):
        conc = dados.variables['CONC'][4:12,index] * 1e9
        ax.plot(conc,label=str(level[index])+'m')

    dados.close()

ax.legend(bbox_to_anchor=(1.05, 0), loc='lower left', borderaxespad=0.)
         # it will place the legend on the outer right-hand side of the last axes

show()

3
figlegend、エバートが提案したように、はるかに優れた解決策のようです;)
carla

11
問題はfig.legend()、すべてのライン(プロット)の識別が必要なことです...各サブプロットについて、ループを使用してラインを生成しているため、これを克服するために私が考え出した唯一の解決策は、前に空のリストを作成することです2番目のループ、および作成されている行を追加します...次に、このリストをfig.legend()関数の引数として使用します。
カーラ


何がdadosありますか?
Shyamkkhadka

1
@Shyamkkhadka、私の元のスクリプトでdadosは、netCDF4ファイルからのデータセットでした(リストで定義されているファイルごとにficheiros)。各ループで、異なるファイルが読み込まれ、サブプロットが図に追加されます。
カーラ

13

異なるサブプロットの多くの曲線を参照する単一の凡例を含む画像が表示されないことに気づきました。

ここに画像の説明を入力してください

さて、あなたはコードを見たいと思いませんか?

from numpy import linspace
import matplotlib.pyplot as plt

# Calling the axes.prop_cycle returns an itertoools.cycle

color_cycle = plt.rcParams['axes.prop_cycle']()

# I need some curves to plot

x = linspace(0, 1, 51)
f1 = x*(1-x)   ; lab1 = 'x - x x'
f2 = 0.25-f1   ; lab2 = '1/4 - x + x x' 
f3 = x*x*(1-x) ; lab3 = 'x x - x x x'
f4 = 0.25-f3   ; lab4 = '1/4 - x x + x x x'

# let's plot our curves (note the use of color cycle, otherwise the curves colors in
# the two subplots will be repeated and a single legend becomes difficult to read)
fig, (a13, a24) = plt.subplots(2)

a13.plot(x, f1, label=lab1, **next(color_cycle))
a13.plot(x, f3, label=lab3, **next(color_cycle))
a24.plot(x, f2, label=lab2, **next(color_cycle))
a24.plot(x, f4, label=lab4, **next(color_cycle))

# so far so good, now the trick

lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]

# finally we invoke the legend (that you probably would like to customize...)

fig.legend(lines, labels)
plt.show()

2行

lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]

説明に値する—この目的のために、私は関数のトリッキーな部分をカプセル化しました。たった4行のコードですが、かなりコメントしました

def fig_legend(fig, **kwdargs):

    # generate a sequence of tuples, each contains
    #  - a list of handles (lohand) and
    #  - a list of labels (lolbl)
    tuples_lohand_lolbl = (ax.get_legend_handles_labels() for ax in fig.axes)
    # e.g. a figure with two axes, ax0 with two curves, ax1 with one curve
    # yields:   ([ax0h0, ax0h1], [ax0l0, ax0l1]) and ([ax1h0], [ax1l0])

    # legend needs a list of handles and a list of labels, 
    # so our first step is to transpose our data,
    # generating two tuples of lists of homogeneous stuff(tolohs), i.e
    # we yield ([ax0h0, ax0h1], [ax1h0]) and ([ax0l0, ax0l1], [ax1l0])
    tolohs = zip(*tuples_lohand_lolbl)

    # finally we need to concatenate the individual lists in the two
    # lists of lists: [ax0h0, ax0h1, ax1h0] and [ax0l0, ax0l1, ax1l0]
    # a possible solution is to sum the sublists - we use unpacking
    handles, labels = (sum(list_of_lists, []) for list_of_lists in tolohs)

    # call fig.legend with the keyword arguments, return the legend object

    return fig.legend(handles, labels, **kwdargs)

PS私はそれsum(list_of_lists, [])がリストのリストを平坦化するための本当に非効率的な方法だと認識していますが、①私はそのコンパクトさを気に入っています、②通常、いくつかのサブプロットにいくつかのカーブがあり、③Matplotlibと効率ですか?;-)


3

ゲームにはかなり遅れていますが、これはまだGoogleに表示される最初のリンクの1つであるため、ここで別の解決策を紹介します。matplotlib 2.2.2を使用すると、gridspec機能を使用してこれを実現できます。以下の例の目的は、凡例を下にして4つのサブプロットを2x2の方法で配置することです。凡例を固定位置に配置するために、下部に「偽」軸が作成されます。その後、「偽」軸がオフになり、凡例のみが表示されます。結果:https : //i.stack.imgur.com/5LUWM.png

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

#Gridspec demo
fig = plt.figure()
fig.set_size_inches(8,9)
fig.set_dpi(100)

rows   = 17 #the larger the number here, the smaller the spacing around the legend
start1 = 0
end1   = int((rows-1)/2)
start2 = end1
end2   = int(rows-1)

gspec = gridspec.GridSpec(ncols=4, nrows=rows)

axes = []
axes.append(fig.add_subplot(gspec[start1:end1,0:2]))
axes.append(fig.add_subplot(gspec[start2:end2,0:2]))
axes.append(fig.add_subplot(gspec[start1:end1,2:4]))
axes.append(fig.add_subplot(gspec[start2:end2,2:4]))
axes.append(fig.add_subplot(gspec[end2,0:4]))

line, = axes[0].plot([0,1],[0,1],'b')           #add some data
axes[-1].legend((line,),('Test',),loc='center') #create legend on bottommost axis
axes[-1].set_axis_off()                         #don't show bottommost axis

fig.tight_layout()
plt.show()

3

棒グラフでサブプロットを使用していて、棒ごとに異なる色を使用している場合。自分でアーティファクトを作成する方が速いかもしれませんmpatches

r m c k次のように凡例を設定できるため、色が異なる4つのバーがあるとします。

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
labels = ['Red Bar', 'Magenta Bar', 'Cyan Bar', 'Black Bar']


#####################################
# insert code for the subplots here #
#####################################


# now, create an artist for each color
red_patch = mpatches.Patch(facecolor='r', edgecolor='#000000') #this will create a red bar with black borders, you can leave out edgecolor if you do not want the borders
black_patch = mpatches.Patch(facecolor='k', edgecolor='#000000')
magenta_patch = mpatches.Patch(facecolor='m', edgecolor='#000000')
cyan_patch = mpatches.Patch(facecolor='c', edgecolor='#000000')
fig.legend(handles = [red_patch, magenta_patch, cyan_patch, black_patch],labels=labels,
       loc="center right", 
       borderaxespad=0.1)
plt.subplots_adjust(right=0.85) #adjust the subplot to the right for the legend

1
+1最高!このように使用して、に直接追加し、plt.legendすべてのサブプロットに1つの凡例を作成しました
User

自動ハンドルと手作りラベルを組み合わせる方が高速ですhandles, _ = plt.gca().get_legend_handles_labels()。それからfig.legend(handles, labels)
smcs

1

この回答は、凡例の位置での@Evertの補足です。

凡例とサブプロットのタイトルが重複しているため、@ Evertのソリューションを最初に試したところ失敗しました。

実際、オーバーラップはによって引き起こされfig.tight_layout()、図の凡例を考慮せずにサブプロットのレイアウトを変更します。ただし、fig.tight_layout()必要です。

重複を避けるfig.tight_layout()ために、図の凡例のためにスペースを残すように指示することができますfig.tight_layout(rect=(0,0,1,0.9))

tight_layout()パラメータの説明


1

@gboffiとBen Usmanの答えの上に構築するには:

同じ色とラベルの異なるサブプロットに異なる線がある状況では、次の線に沿って何かを行うことができます

labels_handles = {
  label: handle for ax in fig.axes for handle, label in zip(*ax.get_legend_handles_labels())
}

fig.legend(
  labels_handles.values(),
  labels_handles.keys(),
  loc="upper center",
  bbox_to_anchor=(0.5, 0),
  bbox_transform=plt.gcf().transFigure,
)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.