ฉันจะสร้างตำนานเดี่ยวสำหรับแผนการย่อยจำนวนมากด้วย matplotlib ได้อย่างไร


166

ฉันกำลังพล็อตข้อมูลประเภทเดียวกัน แต่สำหรับประเทศต่าง ๆ ที่มีแผนย่อยจำนวนมากที่มี matplotlib นั่นคือฉันมี 9 แปลงบนตาราง 3x3 ทั้งหมดมีเหมือนกันสำหรับเส้น (แน่นอนค่าที่แตกต่างกันต่อบรรทัด)

อย่างไรก็ตามฉันยังไม่ได้คิดวิธีที่จะนำตำนานเดียว (เนื่องจากทั้ง 9 subplots มีบรรทัดเดียวกัน) บนภาพเพียงครั้งเดียว

ฉันจะทำอย่างไร

คำตอบ:


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
หรือ simpy loc='upper center', bbox_to_anchor=(0.5, 0), bbox_transform=plt.gcf().transFigureและมันจะไม่ทับซ้อนกันอย่างแน่นอน
Davor Josipovic

2
ฉันยังไม่แน่ใจว่าทำไม แต่โซลูชันของ Evert ไม่ได้ผลสำหรับฉัน - ตำนานยังคงถูกตัดออก วิธีการแก้ปัญหานี้ (พร้อมกับความคิดเห็นของ davor) ทำงานได้อย่างหมดจดมาก - ตำนานถูกวางไว้ตามที่คาดหวังและมองเห็นได้อย่างสมบูรณ์ ขอบคุณ!
sudo ทำการติดตั้ง

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
figlegendดูเหมือนจะเป็นทางออกที่ดีกว่ามาก)
ล่า

11
ปัญหาของfig.legend()มันคือว่ามันต้องมีการระบุสำหรับทุกบรรทัด (แปลง) ... เป็นสำหรับแต่ละแผนย่อยฉันใช้วนรอบเพื่อสร้างบรรทัดวิธีเดียวที่ฉันคิดว่าจะเอาชนะได้คือสร้างรายการว่างเปล่าก่อน ลูปที่สองจากนั้นผนวกบรรทัดตามที่สร้าง ... จากนั้นฉันจะใช้รายการนี้เป็นอาร์กิวเมนต์ของfig.legend()ฟังก์ชัน
ล่า

คำถามที่คล้ายกันที่นี่
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, [])เป็นวิธีที่ไม่มีประสิทธิภาพจริง ๆ ในการทำให้รายการต่าง ๆ เรียบ แต่ฉันชอบความกะทัดรัดของมัน②โดยปกติแล้วจะเป็นเส้นโค้งเล็กน้อยในไม่กี่ย่อยและ③ Matplotlib และประสิทธิภาพ? ;-)


3

ในขณะที่ค่อนข้างช้าเกมฉันจะให้วิธีแก้ปัญหาที่นี่อีกเพราะนี่ยังคงเป็นหนึ่งในลิงค์แรกที่ปรากฏบน Google การใช้ matplotlib 2.2.2 สามารถทำได้โดยใช้คุณสมบัติ gridspec ในตัวอย่างด้านล่างเป้าหมายคือการจัดเรียงสี่แบบย่อยในแบบ 2x2 พร้อมกับตำนานที่แสดงที่ด้านล่าง แกน 'faux' ถูกสร้างขึ้นที่ด้านล่างเพื่อวางตำนานในจุดที่แน่นอน จากนั้นแกน 'มารยาท' จะถูกปิดดังนั้นตำนานแสดงให้เห็นเท่านั้น ผล: 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ยังมีหนึ่งคำอธิบายสำหรับแผนการย่อยทั้งหมดของฉัน
ผู้ใช้

มันเร็วกว่าที่จะรวมการจับฉลากอัตโนมัติและทำด้วยมือ: 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.