Làm thế nào để tôi tạo một huyền thoại duy nhất cho nhiều subplots với matplotlib?


166

Tôi đang vẽ cùng một loại thông tin, nhưng đối với các quốc gia khác nhau, với nhiều ô phụ với matplotlib. Đó là, tôi có 9 ô trên lưới 3x3, tất cả đều giống nhau cho các dòng (tất nhiên, các giá trị khác nhau trên mỗi dòng).

Tuy nhiên, tôi chưa tìm ra cách đặt một chú giải duy nhất (vì tất cả 9 ô con có cùng một dòng) trên hình chỉ một lần.

Làm thế nào để làm điều đó?

Câu trả lời:


160

Ngoài ra còn có một chức năng hay get_legend_handles_labels()mà bạn có thể gọi trên trục cuối cùng (nếu bạn lặp qua chúng) sẽ thu thập mọi thứ bạn cần từ các label=đối số:

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

13
Đây phải là câu trả lời hàng đầu.
ness101

1
Đây thực sự là một câu trả lời hữu ích hơn nhiều! Nó làm việc như thế trong một trường hợp phức tạp hơn đối với tôi.
gmaravel

1
câu trả lời hoàn hảo!
Dorgham

4
Làm cách nào để xóa truyền thuyết cho các ô phụ?
BND

5
Chỉ để thêm vào câu trả lời tuyệt vời này. Nếu bạn có trục y thứ cấp trên các ô của mình và cần hợp nhất cả hai, hãy sử dụng điều này:handles, labels = [(a + b) for a, b in zip(ax1.get_legend_handles_labels(), ax2.get_legend_handles_labels())]
Bill Bill

114

figlegend có thể là những gì bạn đang tìm kiếm: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

Ví dụ ở đây: http://matplotlib.org/examples/pylab_examples/figlegend_demo.html

Một vi dụ khac:

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

hoặc là:

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

1
Tôi biết các dòng mà tôi muốn đặt trong truyền thuyết, nhưng làm thế nào để tôi có được linesbiến để đưa vào đối số cho legend?
patapouf_ai

1
@patapouf_ai lineslà danh sách các kết quả được trả về axes.plot()(nghĩa là mỗi axes.plothoặc một thói quen tương tự trả về một "dòng"). Xem thêm ví dụ liên kết.

17

Đối với định vị tự động của một chú giải trong một figurecó nhiều trục, giống như các chú giải có được subplots(), giải pháp sau đây hoạt động rất tốt:

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

Với bbox_to_anchorbbox_transform=plt.gcf().transFigurebạn đang xác định một hộp giới hạn mới có kích thước của bạn figuređể làm tài liệu tham khảo loc. Sử dụng (0,-0.1,1,1)di chuyển hộp hoa này hơi hướng xuống dưới để ngăn huyền thoại được đặt trên các nghệ sĩ khác.

OBS: sử dụng giải pháp này SAU KHI bạn sử dụng fig.set_size_inches()và TRƯỚC KHI bạn sử dụngfig.tight_layout()


1
Hoặc simpy loc='upper center', bbox_to_anchor=(0.5, 0), bbox_transform=plt.gcf().transFigurevà nó sẽ không chồng chéo cho chắc chắn.
Davor Josipovic

2
Tôi vẫn không chắc tại sao, nhưng giải pháp của Evert không hiệu quả với tôi - huyền thoại cứ bị cắt đứt. Giải pháp này (cùng với nhận xét của davor) hoạt động rất sạch sẽ - truyền thuyết được đặt như mong đợi và hoàn toàn có thể nhìn thấy. Cảm ơn!
sudo thực hiện cài đặt

16

Bạn chỉ cần yêu cầu truyền thuyết một lần, bên ngoài vòng lặp của bạn.

Ví dụ, trong trường hợp này tôi có 4 ô con, với cùng một dòng và một chú giải duy nhất.

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, như bị chặn bởi Evert, dường như là một giải pháp tốt hơn nhiều;)
carla

11
vấn đề fig.legend()là nó yêu cầu nhận dạng cho tất cả các dòng (lô) ... vì, đối với mỗi ô phụ, tôi đang sử dụng một vòng lặp để tạo các dòng, giải pháp duy nhất tôi tìm ra để khắc phục điều này là tạo một danh sách trống trước đó vòng lặp thứ hai, và sau đó nối các dòng khi chúng đang được tạo ... Sau đó, tôi sử dụng danh sách này làm đối số cho fig.legend()hàm.
carla

Một câu hỏi tương tự ở đây
emmmphd

Có gì dadosở đây
Shyamkkhadka

1
@Shyamkkhadka, trong tập lệnh gốc của tôi dadoslà tập dữ liệu từ tệp netCDF4 (cho mỗi tệp được xác định trong danh sách ficheiros). Trong mỗi vòng lặp, một tệp khác nhau được đọc và một ô phụ được thêm vào hình.
carla

13

Tôi đã nhận thấy rằng không có câu trả lời nào hiển thị một hình ảnh với một chú giải duy nhất tham chiếu nhiều đường cong trong các ô con khác nhau, vì vậy tôi phải chỉ cho bạn một ... để khiến bạn tò mò ...

nhập mô tả hình ảnh ở đây

Bây giờ, bạn muốn xem mã, phải không?

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

Hai dòng

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

xứng đáng được giải thích - với mục đích này, tôi đã gói gọn phần khó khăn trong một hàm, chỉ có 4 dòng mã nhưng nhận xét rất nhiều

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)

Tái sum(list_of_lists, [])bút ;-)


3

Mặc dù trò chơi khá muộn, tôi sẽ đưa ra một giải pháp khác ở đây vì đây vẫn là một trong những liên kết đầu tiên xuất hiện trên google. Sử dụng matplotlib 2.2.2, điều này có thể đạt được bằng cách sử dụng tính năng gridspec. Trong ví dụ dưới đây, mục tiêu là có bốn ô con được sắp xếp theo kiểu 2x2 với chú thích được hiển thị ở phía dưới. Trục 'giả' được tạo ở phía dưới để đặt chú giải vào một vị trí cố định. Trục 'giả' sau đó bị tắt để chỉ có truyền thuyết hiển thị. Kết quả: 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

nếu bạn đang sử dụng các ô con với biểu đồ thanh, với màu khác nhau cho mỗi thanh. có thể nhanh hơn để tự tạo ra các đồ tạo tác bằng cách sử dụngmpatches

Giả sử bạn có bốn thanh với các màu khác nhau như r m c kbạn có thể đặt chú thích như sau

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 Tốt nhất! Tôi đã sử dụng nó theo cách này thêm trực tiếp vào plt.legendđể có một chú giải cho tất cả các ô con của tôi
Người dùng

Kết hợp tay cầm tự động và nhãn thủ công sẽ nhanh hơn: handles, _ = plt.gca().get_legend_handles_labels()sau đófig.legend(handles, labels)
smcs

1

Câu trả lời này là phần bổ sung cho @ Evert trên vị trí huyền thoại.

Lần thử đầu tiên của tôi vào giải pháp của @ Evert đã thất bại do sự chồng chéo của truyền thuyết và tiêu đề của subplot.

Trong thực tế, sự chồng chéo được gây ra bởi fig.tight_layout(), điều này làm thay đổi bố cục của các ô con mà không xem xét chú thích hình. Tuy nhiên, fig.tight_layout()là cần thiết.

Để tránh sự chồng chéo, chúng ta có thể yêu fig.tight_layout()cầu chừa khoảng trống cho huyền thoại của nhân vật fig.tight_layout(rect=(0,0,1,0.9)).

Mô tả các tham số chặt chẽ_layout () .


1

Để xây dựng câu trả lời của @ gboffi's và Ben Usman:

Trong tình huống một người có các dòng khác nhau trong các ô con khác nhau có cùng màu và nhãn, người ta có thể làm gì đó dọc theo dòng

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,
)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.