xlabel / ylabel ทั่วไปสำหรับ subplots matplotlib


143

ฉันมีแผนต่อไปนี้:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

และตอนนี้ฉันต้องการให้ป้ายกำกับแกน x ทั่วไปและป้ายแกน y ด้วย "ทั่วไป" ฉันหมายความว่าควรมีป้ายกำกับแกน x ขนาดใหญ่หนึ่งตารางด้านล่างกริดย่อยทั้งหมดและป้ายแกน y ขนาดใหญ่หนึ่งรายการทางด้านขวา ฉันไม่พบอะไรเกี่ยวกับเรื่องนี้ในเอกสารประกอบสำหรับplt.subplotsและ googlings ของฉันแนะนำว่าฉันต้องplt.subplot(111)เริ่มต้นด้วยการเริ่มต้นใหญ่- แต่ฉันจะใส่ย่อย 5 * 2 ลงในนั้นได้plt.subplotsอย่างไร


2
ด้วยการอัปเดตคำถามและความคิดเห็นที่เหลือในคำตอบด้านล่างนี้เป็นข้อมูลซ้ำซ้อนของstackoverflow.com/questions/6963035/…
ติดยาเสพติด

ไม่ใช่เพราะคำถามของฉันมีไว้สำหรับ plt.subplots () และคำถามที่คุณเชื่อมโยงไปยังใช้ add_subplot - ฉันไม่สามารถใช้วิธีการนั้นได้จนกว่าฉันจะเปลี่ยนเป็น add_subplot ซึ่งฉันต้องการหลีกเลี่ยง ฉันสามารถใช้โซลูชัน plt.text ซึ่งเป็นโซลูชันทางเลือกในลิงก์ของคุณ แต่ไม่ใช่โซลูชันที่สวยงามที่สุด
jolindbe

ในการอธิบายอย่างละเอียดเท่าที่ฉันเข้าใจแล้ว plt.subplots ไม่สามารถสร้างชุดของ subplots ภายในสภาพแวดล้อมของแกนที่มีอยู่ แต่จะสร้างรูปใหม่เสมอ ขวา?
jolindbe

วิธีแก้ปัญหาที่หรูหราที่สุดสามารถพบได้ที่นี่: stackoverflow.com/questions/6963035/…
Mr.H

ลิงก์ของคุณได้รับจากผู้ใช้ติดยาเสพติดมานานกว่า 4 ปีที่ผ่านมา (เพียงแค่ความคิดเห็นเล็กน้อยจากคุณ) ดังที่ฉันได้กล่าวก่อนหน้านี้โซลูชันนั้นเกี่ยวข้องกับ add_subplot ไม่ใช่ plt.subplots ()
jolindbe

คำตอบ:


206

ดูเหมือนว่าสิ่งที่คุณต้องการจริง มันใช้แนวทางเดียวกันกับคำตอบนี้กับกรณีเฉพาะของคุณ:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))

fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

แปลงหลายแปลงที่มีป้ายชื่อแกนทั่วไป


4
โปรดทราบว่า 0.5 สำหรับพิกัด x ของป้ายกำกับ x ไม่ได้ใส่ป้ายกำกับไว้ที่กึ่งกลางของแผนย่อยศูนย์กลาง คุณจะต้องมีขนาดใหญ่กว่านั้นเพื่อให้บัญชี yticklabels
dbliss

2
มีลักษณะที่คำตอบนี้plt.textสำหรับวิธีการที่ไม่ได้ใช้งาน คุณสร้างแผนย่อยของคุณ แต่จากนั้นเพิ่มพล็อตบิตหนึ่งทำให้มองไม่เห็นและทำเครื่องหมาย x และ y
James Owers

ขอบคุณทำงานโดยทั่วไป ทางออกใดที่จะแตกเมื่อใช้tight_layout?
serv-inc

3
@ serv-inc ด้วยการtight_layoutแทนที่0.04ด้วย0ดูเหมือนว่าจะทำงาน
divenex

3
การใช้fig.textไม่ใช่ความคิดที่ดี สิ่งนี้ยุ่งเหยิงขึ้นเช่นplt.tight_layout()
สงบสุข

55

ตั้งแต่ฉันคิดว่ามันเพียงพอที่เกี่ยวข้องและสง่างาม (ไม่จำเป็นต้องระบุพิกัดกับข้อความสถานที่), ฉันจะคัดลอก (ที่มีการปรับตัวขึ้นเล็กน้อย) คำตอบสำหรับคำถามที่เกี่ยวข้องอีก

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

สิ่งนี้ส่งผลต่อไปนี้ (ด้วย matplotlib เวอร์ชั่น 2.2.0):

แผนย่อย 5 แถวและ 2 คอลัมน์พร้อมป้ายกำกับแกน x และ y


4
เนื่องจากความเรียบง่ายนี้ควรเป็นคำตอบที่ยอมรับได้ ตรงไปตรงมามาก ยังคงเกี่ยวข้องกับ matplotlib v3.x
Kyle Swanson

ฉันอยากรู้ว่ามันจะใช้กับวัตถุรูปหลาย ๆ รูปได้อย่างไร? fig.xlabel ("foo") ไม่ทำงาน
สยองขวัญ Vacui

FYI: ตอนนี้ผู้คนใช้ชุดรูปแบบสีเข้มใน StackOverflow ฉลากสามารถอ่านได้แทบจะดีกว่าที่จะส่งออก png ของคุณด้วยพื้นหลังสีขาว
xyzzyqed

@xyzzyqed ฉันไม่ทราบว่ามีสิ่งต่าง ๆ เช่น "ธีม" ใน stackoverflow และฉันจำไม่ได้ว่าฉันส่งออกรูปภาพอย่างไร ฉันจะควบคุมพื้นหลังเมื่อส่งออกได้อย่างไร
bli

2
ปัญหาเดียวของการแก้ปัญหานี้คือมันไม่ทำงานเมื่อใช้constrained_layout=Trueเพราะมันสร้างป้ายกำกับที่ทับซ้อนกัน ในกรณีนี้คุณจะต้องปรับขอบเขตของแผนการย่อยด้วยตนเอง
baccandr

35

โดยที่sharex=True, sharey=Trueคุณไม่ได้รับ:

ป้อนคำอธิบายรูปภาพที่นี่

ด้วยคุณควรได้รับดีกว่า:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))

plt.tight_layout()

ป้อนคำอธิบายรูปภาพที่นี่

แต่ถ้าคุณต้องการเพิ่มป้ายกำกับเพิ่มเติมคุณควรเพิ่มป้ายกำกับลงในพล็อตขอบเท่านั้น:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))
        if i == len(axes2d) - 1:
            cell.set_xlabel("noise column: {0:d}".format(j + 1))
        if j == 0:
            cell.set_ylabel("noise row: {0:d}".format(i + 1))

plt.tight_layout()

ป้อนคำอธิบายรูปภาพที่นี่

การเพิ่มป้ายกำกับสำหรับแต่ละพล็อตจะทำให้เสีย (อาจมีวิธีการตรวจจับป้ายกำกับซ้ำโดยอัตโนมัติ แต่ฉันไม่ทราบเลย)


นี่เป็นเรื่องที่ยากกว่ามากเช่นหากไม่ทราบจำนวนพล็อต (เช่นคุณมีฟังก์ชั่นการวางนัยทั่วไปที่ใช้กับพล็อตย่อยจำนวนเท่าใดก็ได้)
naught101

15

ตั้งแต่คำสั่ง:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

คุณใช้คืนค่า tuple ซึ่งประกอบด้วยตัวเลขและรายการอินสแตนซ์ของแกนมันก็เพียงพอแล้วที่จะทำสิ่งที่ชอบ (คิดว่าฉันเปลี่ยนfig,axไปแล้วfig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

for ax in axes:
    ax.set_xlabel('Common x-label')
    ax.set_ylabel('Common y-label')

หากคุณต้องการเปลี่ยนรายละเอียดบางอย่างในแผนย่อยที่เฉพาะเจาะจงคุณสามารถเข้าถึงได้ผ่านทางaxes[i]ที่iวนซ้ำกว่าแผนการย่อยของคุณ

มันอาจจะมีประโยชน์มากในการรวม

fig.tight_layout()

ที่ท้ายไฟล์ก่อนหน้าplt.show()เพื่อหลีกเลี่ยงการซ้อนทับฉลาก


5
ฉันขอโทษที่ฉันยังไม่ชัดเจนด้านบน ด้วย "ทั่วไป" ฉันหมายถึงป้าย x หนึ่งป้ายด้านล่างแปลงทั้งหมดและป้ายกำกับเดียวที่ด้านซ้ายของแปลงฉันได้อัปเดตคำถามเพื่อสะท้อนสิ่งนี้
jolindbe

2
@JohanLindberg: เกี่ยวกับความคิดเห็นของคุณที่นี่และสูงกว่า: แน่นอนplt.subplots()จะสร้างอินสแตนซ์รูปใหม่ หากคุณต้องการที่จะยึดคำสั่งนี้คุณสามารถเพิ่มได้อย่างง่ายดายbig_ax = fig.add_subplot(111)เพราะคุณมีรูปและสามารถเพิ่มแกนอื่น หลังจากนั้นคุณสามารถปรับเปลี่ยนbig_axวิธีการแสดงในลิงค์จาก Hooked
Marius

ขอบคุณสำหรับคำแนะนำของคุณ แต่ถ้าฉันทำอย่างนั้นฉันจะต้องเพิ่ม big_ax หลังจาก plt.subplots () และฉันได้รับแผนย่อยนั้นเหนือสิ่งอื่นใด - ฉันจะทำให้มันโปร่งใสหรือส่งไปด้านหลังได้ไหม? แม้ว่าฉันจะตั้งค่าสีทั้งหมดเป็น none เช่นเดียวกับในลิงก์ของ Hooked แต่ก็ยังคงเป็นกล่องสีขาวซึ่งครอบคลุมพื้นที่ย่อยทั้งหมดของฉัน
jolindbe

2
@JohanLindberg คุณพูดถูกฉันยังไม่ได้ตรวจสอบ แต่คุณสามารถกำหนดสีพื้นหลังของแกนใหญ่noneด้วยการทำ: big_ax.set_axis_bgcolor('none')คุณควรให้ labelcolor none(เมื่อเทียบกับตัวอย่างการเชื่อมโยงโดยติดยาเสพติด):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
Marius

2
ฉันได้รับข้อผิดพลาด: ในงบAttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel' ax.set_xlabel('Common x-label')คุณคิดออกไหม
hengxin

5

มันจะดูดีขึ้นถ้าคุณจองพื้นที่สำหรับป้ายกำกับทั่วไปด้วยการทำป้ายกำกับที่มองไม่เห็นสำหรับแผนย่อยที่มุมซ้ายล่าง นอกจากนี้ยังเป็นการดีที่จะส่งผ่านแบบอักษรจาก rcParams ด้วยวิธีนี้ป้ายกำกับทั่วไปจะเปลี่ยนขนาดด้วยการตั้งค่า rc ของคุณและแกนจะถูกปรับเพื่อให้มีพื้นที่ว่างสำหรับป้ายกำกับทั่วไป

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


1
ใช้ดีมากของฉลากที่มองไม่เห็น! ขอบคุณ
colelemonz

3

ฉันพบปัญหาที่คล้ายกันในขณะที่วางแผนตารางกราฟ กราฟประกอบด้วยสองส่วน (บนและล่าง) ฉลาก y ควรจะอยู่กึ่งกลางทั้งสองส่วน

ฉันไม่ต้องการใช้โซลูชันที่ขึ้นอยู่กับการรู้ตำแหน่งในรูปด้านนอก (เช่น fig.text ()) ดังนั้นฉันจึงจัดการตำแหน่ง y ของฟังก์ชัน set_ylabel () โดยปกติแล้วคือ 0.5 ตรงกลางของพล็อตที่เพิ่มเข้ามา เมื่อระยะห่างระหว่างส่วน (hspace) ในรหัสของฉันเป็นศูนย์ฉันสามารถคำนวณค่ากลางของสองส่วนที่สัมพันธ์กับส่วนบน

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

# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
               subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)

# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])

# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)

# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)

plt.show()

ภาพ

ข้อเสีย:

  • ระยะทางแนวนอนของพล็อตขึ้นอยู่กับส่วนบนเห็บด้านล่างอาจขยายเข้าไปในฉลาก

  • สูตรไม่ได้คำนึงถึงช่องว่างระหว่างชิ้นส่วนเข้าด้วยกัน

  • โยนข้อยกเว้นเมื่อความสูงของส่วนบนเป็น 0

อาจมีวิธีการแก้ปัญหาทั่วไปที่ใช้ในการขยายระหว่างตัวเลขเข้าบัญชี


เฮ้ฉันคิดหาวิธีที่จะทำสิ่งนี้เป็นอย่างมากในเส้นเลือดของคำตอบของคุณ แต่อาจแก้ปัญหาเหล่านี้ได้บ้าง ดูstackoverflow.com/a/44020303/4970632 (ด้านล่าง)
Luke Davis

2

ปรับปรุง:

คุณลักษณะนี้เป็นส่วนหนึ่งของแพ็คเกจ proplot matplotlibที่ฉันเพิ่งเปิดตัวใน pypi ตามค่าเริ่มต้นเมื่อคุณสร้างตัวเลขฉลากจะ "แชร์" ระหว่างแกน


คำตอบเดิม:

ฉันค้นพบวิธีการที่แข็งแกร่งกว่านี้:

หากคุณรู้bottomและtopkwargs ที่GridSpecเริ่มต้นหรือคุณรู้ตำแหน่งขอบของแกนในFigureพิกัดคุณสามารถระบุตำแหน่ง ylabel ในFigureพิกัดด้วยเวทมนตร์ "เปลี่ยน" แฟนซี ตัวอย่างเช่น:

import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
       mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
       )) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel('Hello, world!')

... และคุณจะเห็นว่าฉลากยังคงปรับซ้าย - ขวาอย่างเหมาะสมเพื่อป้องกันการซ้อนทับกับ ticklabels เหมือนปกติ - แต่ตอนนี้มันจะปรับให้อยู่ระหว่างพล็อตย่อยที่ต้องการเสมอ

นอกจากนี้ถ้าคุณไม่ได้ใช้set_position, ylabel จะปรากฏขึ้นโดยค่าเริ่มต้นตรงครึ่งทางขึ้นรูป ฉันเดาว่านี่เป็นเพราะเมื่อฉลากถูกดึงออกมาในที่สุดmatplotlibใช้ 0.5 สำหรับy-coordinate โดยไม่ตรวจสอบว่าการแปลงพิกัดพื้นฐานมีการเปลี่ยนแปลงหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.