matplotlib을 사용하여 많은 하위 플롯에 대한 단일 범례를 어떻게 만드나요?


166

matplotlib을 사용하여 여러 하위 플롯을 사용하여 동일한 유형의 정보를 다른 국가에 표시하고 있습니다. 즉, 3x3 그리드에 9 개의 플롯이 있으며 모두 라인에 대해 동일합니다 (물론 라인마다 다른 값).

그러나 그림에 단일 범례 (9 개의 서브 플로트가 모두 동일한 선을 갖기 때문에)를 한 번만 배치하는 방법을 알지 못했습니다.

어떻게합니까?

답변:


161

인수 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

4
서브 플로트의 범례를 어떻게 제거합니까?
— BND

5
이 위대한 답변에 추가하십시오. 플롯에 보조 y 축이 있고 병합해야하는 경우 다음을 사용하십시오.handles, labels = [(a + b) for a, b in zip(ax1.get_legend_handles_labels(), ax2.get_legend_handles_labels())]
— Bill

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

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)하면 범례 상자가 약간 아래쪽으로 이동 하여 다른 아티스트에게 범례가 배치되지 않습니다.

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의 솔루션은 나에게 효과가 없었습니다. 전설은 계속 끊어졌습니다. 이 솔루션 (dvor의 의견과 함께)은 매우 깨끗하게 작동했습니다. 전례가 예상대로 완전히 표시되었습니다. 감사!
— sudo make install

16

루프 외부에서 범례를 한 번만 요청하면됩니다.

예를 들어,이 경우 동일한 줄과 단일 범례가있는 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
figlegendEvert가 제안한 것처럼 훨씬 더 나은 해결책 인 것 같습니다.)
— carla

11
문제는 fig.legend()모든 라인 (플로트)에 대한 식별이 필요하다는 것입니다 ... 각 서브 플롯에 대해 루프를 사용하여 라인을 생성하고 있습니다.이를 극복하기 위해 알아 낸 유일한 해결책은 전에 빈 목록을 만드는 것입니다 두 번째 루프를 만든 다음 줄을 만들 때 줄을 추가하십시오 ... 그런 다음이 목록을 fig.legend()함수 의 인수로 사용합니다 .
— carla

비슷한 질문이 있습니다
— emmmphd

무엇입니까 dados?
— Shyamkkhadka

1
@Shyamkkhadka dados는 원래 스크립트 에서 netCDF4 파일의 데이터 세트였습니다 (목록에 정의 된 각 파일에 대해 ficheiros). 각 루프에서 다른 파일을 읽고 서브 플롯을 그림에 추가합니다.
— carla

14

여러 서브 플로트의 많은 커브를 참조하는 단일 범례로 이미지를 표시하는 응답이 없으므로 궁금한 점이 있습니다.

여기에 이미지 설명을 입력하십시오

이제 코드를보고 싶 습니까?

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

두 줄

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)

추신 : 나는 sum(list_of_lists, [])목록의 목록을 평평하게하는 것은 실제로 비효율적 인 방법 이라는 것을 알고 있지만, 나는 목록의 압축을 좋아합니다. ;-)


3

게임에 다소 늦었지만 여기에 또 다른 해결책을 제시하겠습니다. 이는 여전히 Google에 표시되는 첫 번째 링크 중 하나입니다. matplotlib 2.2.2를 사용하면 gridspec 기능을 사용하여이 작업을 수행 할 수 있습니다. 아래 예에서 목표는 하단에 표시된 범례와 함께 2x2 방식으로 배열 된 4 개의 서브 플롯을 갖는 것입니다. 범례를 고정 된 지점에 배치하기 위해 하단에 '가짜'축이 생성됩니다. 그런 다음 '가짜'축이 꺼 지므로 범례 만 표시됩니다. 결과 : 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범례를 다음과 같이 설정할 수 있으므로 색상이 다른 네 개의 막대가 있다고 가정하십시오.

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모든 하위 플롯에 대한 하나의 범례를 갖도록 직접 추가했습니다
— 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.