Programming

matplotlib을 사용하여 많은 하위 플롯에 대해 단일 범례를 어떻게 만들 수 있습니까?

procodes 2020. 7. 3. 22:09
반응형

matplotlib을 사용하여 많은 하위 플롯에 대해 단일 범례를 어떻게 만들 수 있습니까?


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

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

어떻게합니까?


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 )

인수 get_legend_handles_labels()에서 필요한 모든 것을 수집하는 마지막 축에서 호출 할 수 있는 멋진 기능 이 있습니다 (반복하면) label=.

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

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

예를 들어,이 경우 동일한 줄과 단일 범례가있는 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()

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


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

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

자, 당신 은 코드를보고 습니까?

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, [])목록의 목록을 평평하게하는 것은 실제로 비효율적 인 방법 이라는 것을 알고 있지만, 나는 목록의 압축을 좋아합니다. ;-)


While rather late to the game, I'll give another solution here as this is still one of the first links to show up on google. Using matplotlib 2.2.2, this can be achieved using the gridspec feature. In the example below the aim is to have four subplots arranged in a 2x2 fashion with the legend shown at the bottom. A 'faux' axis is created at the bottom to place the legend in a fixed spot. The 'faux' axis is then turned off so only the legend shows. Result: 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()

This answer is a complement to @Evert's on the legend position.

My first try on @Evert's solution failed due to overlaps of the legend and the subplot's title.

실제로 겹침은에 의해 발생하며 fig.tight_layout(), 이는 그림 범례를 고려하지 않고 하위 그림의 레이아웃을 변경합니다. 그러나 fig.tight_layout()필요합니다.

겹침을 피하기 위해로 fig.tight_layout()그림의 범례를위한 공간을 남겨 두 라고 할 수 있습니다 fig.tight_layout(rect=(0,0,1,0.9)).

tight_layout () 매개 변수에 대한 설명입니다 .


막 대형 차트가있는 하위 그림을 사용하는 경우 각 막대마다 다른 색상을 사용합니다. 직접 인공물을 만드는 것이 더 빠를 수 있습니다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

참고 : https://stackoverflow.com/questions/9834452/how-do-i-make-a-single-legend-for-many-subplots-with-matplotlib

반응형