วิธีการนำตำนานออกจากพล็อต


987

ฉันมีชุดของ 20 แปลง (ไม่ใช่แบบย่อย) ที่จะทำในรูปเดียว ฉันต้องการตำนานที่จะอยู่นอกกรอบ ในเวลาเดียวกันฉันไม่ต้องการเปลี่ยนแกนเนื่องจากขนาดของตัวเลขลดลง กรุณาช่วยฉันสำหรับแบบสอบถามต่อไปนี้:

  1. ฉันต้องการเก็บกล่องคำอธิบายภาพไว้นอกพื้นที่เรื่องย่อ (ฉันต้องการให้ตำนานอยู่ข้างนอกทางด้านขวาของพื้นที่การลงจุด)
  2. มีอยู่หรือไม่ว่าฉันลดขนาดตัวอักษรของข้อความในกล่องคำอธิบายเพื่อให้ขนาดของกล่องคำอธิบายประกอบนั้นเล็ก

14
สำหรับเวอร์ชัน matplotlib ที่ใหม่กว่าคำตอบนี้แสดงวิธีตั้งค่า matplotlib เพื่อกำหนดตำแหน่งที่จะวางคำอธิบายโดยอัตโนมัติโดยไม่ต้องยุ่งเกี่ยวกับพล็อ
dotancohen

2
มีคำถามเดียวกันและพบตัวอย่างที่ยอดเยี่ยมนี้ที่เหมาะกับฉัน "นอกกรอบ": matplotlib.org/1.3.1/users/legend_guide.html
robodasha

1
https://stackoverflow.com/a/43439132/52074คำตอบนี้มีความลึกมากขึ้น และทำเช่นนั้นด้วยรหัสสองบรรทัด! คำตอบที่อัปโหลดเกิน 1,400 เป็นสิ่งที่ดีเช่นกัน แต่ฉันพบว่ามันเป็นคำทั่วไปน้อยกว่า
Trevor Boyd Smith

2
@dotancohen "กำหนดตำแหน่งที่จะวางตำนานโดยอัตโนมัติโดยไม่รบกวนแผนการ"ในบางกรณีเมื่อพล็อตมีเส้นโค้งจำนวนมากเป็นไปไม่ได้ ในกรณีเหล่านั้นผู้ใช้อาจต้องการวางคำอธิบายด้วยตนเองและวางไว้นอกพื้นที่การลงจุด ฉันกำลังมีปัญหากับพล็อตดังกล่าวในขณะนี้
อาลี

btw, รายการตัวเลือก loc: ดีที่สุด; บนขวา; บนด้านซ้าย; ซ้ายล่าง มุมล่างขวา; ขวา; ศูนย์ซ้าย; ศูนย์ขวา; ศูนย์ล่าง; ศูนย์บน; ศูนย์;
Z-Y00

คำตอบ:


57

คุณสามารถทำให้ข้อความคำอธิบายเล็กลงโดยการสร้างคุณสมบัติแบบอักษร:

from matplotlib.font_manager import FontProperties

fontP = FontProperties()
fontP.set_size('small')
legend([plot1], "title", prop=fontP) 
# or add prop=fontP to whatever legend() call you already have

50
สิ่งที่อยู่plot1ในคำตอบนี้หรือไม่?
พอล H

41
สิ่งที่อยู่legendในคำตอบนี้หรือไม่?
ริชาร์ด

28
สิ่งนี้จะตอบในส่วนที่สองของคำถาม แต่ไม่ใช่ส่วนหลัก
Mateen Ulhaq

8
วิธีที่เหมาะสมในการทำเช่นนี้คือplt.legend(fontsize='small')
Mateen Ulhaq

41
คำตอบนี้แย่มากและให้บริบทน้อยมาก!
Tekill

1794

มีหลายวิธีที่จะทำสิ่งที่คุณต้องการ หากต้องการเพิ่มสิ่งที่ @inalis และ @Navi พูดไปแล้วคุณสามารถใช้bbox_to_anchorอาร์กิวเมนต์คำหลักเพื่อวางคำอธิบายแผนภูมิไว้นอกแกนและ / หรือลดขนาดตัวอักษร

ก่อนที่คุณจะพิจารณาลดขนาดตัวอักษร (ซึ่งทำให้อ่านยากมาก ๆ ) ลองเล่นโดยวางคำอธิบายในที่ต่างๆ:

ดังนั้นเรามาเริ่มด้วยตัวอย่างทั่วไป:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend()

plt.show()

ข้อความแสดงแทน

หากเราทำสิ่งเดียวกัน แต่ใช้bbox_to_anchorอาร์กิวเมนต์คำหลักเราสามารถเลื่อนคำอธิบายออกไปเล็กน้อยนอกขอบเขตของแกน:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$' % i)

ax.legend(bbox_to_anchor=(1.1, 1.05))

plt.show()

ข้อความแสดงแทน

ในทำนองเดียวกันคุณสามารถทำให้แนวนอนเป็นตำนานมากขึ้นและ / หรือวางไว้ที่ด้านบนของรูป (ฉันยังเปิดมุมโค้งมนและเงาที่เรียบง่าย):

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05),
          ncol=3, fancybox=True, shadow=True)
plt.show()

ข้อความแสดงแทน

อีกทางหนึ่งคุณสามารถลดความกว้างของพล็อตปัจจุบันและทำให้คำอธิบายแผนภูมิอยู่นอกแกนของรูป (หมายเหตุ: หากคุณใช้tight_layout ()จากนั้นออก ax.set_position ():

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis by 20%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

# Put a legend to the right of the current axis
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.show()

ข้อความแสดงแทน

และในทำนองเดียวกันคุณสามารถย่อพล็อตในแนวตั้งและวางแนวนอนที่ด้านล่าง:

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    line, = ax.plot(x, i * x, label='$y = %ix$'%i)

# Shrink current axis's height by 10% on the bottom
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
                 box.width, box.height * 0.9])

# Put a legend below current axis
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05),
          fancybox=True, shadow=True, ncol=5)

plt.show()

ข้อความแสดงแทน

มีลักษณะที่เป็นคู่มือตำนาน matplotlib คุณอาจจะดูplt.figlegend()ด้วย


10
หากใครมีปัญหาเกี่ยวกับเรื่องนี้โปรดทราบว่า tight_layout () จะทำให้เกิดปัญหา!
Matthew G.

4
คำตอบที่ดี! ลิงก์ที่ถูกต้องไปยังเอกสารคือmatplotlib.org/users/legend_guide.html#legend-location
meduz

4
เป็นความสมบูรณ์คุณสามารถพูดได้ว่าโดยทั่วไป (สรุปจากเอกสาร) " bbox_to_anchorเป็น tuple 4 ทุ่น (x, y, ความกว้าง, ความสูงของ bbox) หรือ tuple 2 ทุ่น (x, y) ในแกนปกติ พิกัด "
meduz

5
ฉันต้องยอมรับว่านี่อาจเป็นคำตอบที่ฉันเยี่ยมชมบ่อยที่สุด ฉันไม่สามารถช่วยกลับมาซ้ำแล้วซ้ำอีกเพราะไม่มีวิธีที่บุคคลที่เหมาะสมสามารถจดจำตรรกะนี้ได้โดยไม่ต้องดูที่นี่
KT

9
โปรดทราบว่าหากคุณต้องการบันทึกรูปลงในไฟล์คุณควรเก็บวัตถุที่ถูกส่งคืนเช่นlegend = ax.legend()และในภายหลังfig.savefig(bbox_extra_artists=(legend,))
coldfix

791

การวางคำอธิบายภาพ ( bbox_to_anchor)

ตำนานอยู่ในตำแหน่งภายในกรอบของแกนโดยใช้อาร์กิวเมนต์loc เช่นวางตำนานในมุมขวาบนของกล่อง bounding ซึ่งโดยค่าเริ่มต้น extents จากถึงในพิกัดแกน (หรือในสัญกรณ์กล่อง bounding )plt.legend
loc="upper right"(0,0)(1,1)(x0,y0, width, height)=(0,0,1,1)

หากต้องการวางคำอธิบายแผนภูมิไว้นอกกล่องขอบเขตแกนจะมีการระบุ(x0,y0)พิกัดของแกนของมุมล่างซ้ายของตำนาน

plt.legend(loc=(1.04,0))

อย่างไรก็ตามวิธีการที่หลากหลายมากขึ้นคือการระบุช่องว่างที่ตำนานควรจะวางด้วยตนเองโดยใช้bbox_to_anchorอาร์กิวเมนต์ หนึ่งสามารถ จำกัด ตัวเองเพื่อจัดหาเพียง(x0,y0)ส่วนหนึ่งของ bbox สิ่งนี้จะสร้าง zero span box ซึ่งคำอธิบายจะขยายไปในทิศทางที่กำหนดโดยlocอาร์กิวเมนต์ เช่น

plt.legend (bbox_to_anchor = (1.04,1), loc = "ซ้ายบน")

วางคำอธิบายแผนภูมิไว้นอกแกนดังนั้นมุมซ้ายบนของคำอธิบายแผนภูมิจะอยู่ในตำแหน่ง(1.04,1)ในพิกัดแกน

ตัวอย่างเพิ่มเติมจะได้รับด้านล่างซึ่งมีอิทธิพลซึ่งกันและกันระหว่างการขัดแย้งที่แตกต่างกันเช่นmodeและncolsจะแสดง

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

l1 = plt.legend(bbox_to_anchor=(1.04,1), borderaxespad=0)
l2 = plt.legend(bbox_to_anchor=(1.04,0), loc="lower left", borderaxespad=0)
l3 = plt.legend(bbox_to_anchor=(1.04,0.5), loc="center left", borderaxespad=0)
l4 = plt.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", borderaxespad=0, ncol=3)
l5 = plt.legend(bbox_to_anchor=(1,0), loc="lower right", 
                bbox_transform=fig.transFigure, ncol=3)
l6 = plt.legend(bbox_to_anchor=(0.4,0.8), loc="upper right")

รายละเอียดเกี่ยวกับวิธีตีความอาร์กิวเมนต์ 4-tuple ให้bbox_to_anchorเป็นในl4สามารถพบได้ในคำถามนี้ การmode="expand"ขยายตำนานในแนวนอนภายในกล่องขอบเขตที่กำหนดโดย 4-tuple สำหรับตำนานที่ขยายในแนวตั้งให้ดูคำถามนี้นี้

บางครั้งมันอาจมีประโยชน์ในการระบุกล่องขอบเขตในพิกัดรูปแทนพิกัดแกน สิ่งนี้แสดงในตัวอย่างl5จากด้านบนซึ่งbbox_transformมีการใช้อาร์กิวเมนต์เพื่อวางคำอธิบายในมุมซ้ายล่างของรูปภาพ

postprocessing

การวางตำนานไว้นอกแกนมักจะนำไปสู่สถานการณ์ที่ไม่พึงประสงค์ว่าอยู่นอกกรอบรูปทั้งหมดหรือบางส่วน

วิธีแก้ไขปัญหานี้คือ:

  • ปรับค่าพารามิเตอร์แผน
    หนึ่งสามารถปรับค่าพารามิเตอร์แผนดังกล่าวว่าแกนใช้พื้นที่น้อยภายในรูป (และจึงออกจากพื้นที่มากขึ้นเพื่อตำนาน) plt.subplots_adjustโดยใช้ เช่น

    plt.subplots_adjust(right=0.7)

    ออกจากพื้นที่ 30% ทางด้านขวาของรูปซึ่งสามารถวางตำนานได้


  • การใช้เลย์เอาต์แบบplt.tight_layoutรัดกุมช่วยให้สามารถปรับพารามิเตอร์แผนย่อยโดยอัตโนมัติเพื่อให้องค์ประกอบต่างๆในรูปอยู่แนบชิดกับขอบรูป น่าเสียดายที่ตำนานไม่ได้ถูกนำมาพิจารณาในระบบอัตโนมัตินี้ แต่เราสามารถจัดหากล่องสี่เหลี่ยมที่พื้นที่ย่อยทั้งหมด (รวมถึงฉลาก) จะพอดี

    plt.tight_layout(rect=[0,0,0.75,1])
  • การบันทึกรูปภาพด้วยbbox_inches = "tight"
    อาร์กิวเมนต์bbox_inches = "tight"ที่plt.savefigสามารถใช้ในการบันทึกรูปแบบที่ศิลปินทั้งหมดบนผืนผ้าใบ (รวมถึงคำอธิบายภาพ) จะพอดีกับพื้นที่ที่บันทึกไว้ หากจำเป็นขนาดของรูปจะถูกปรับโดยอัตโนมัติ

    plt.savefig("output.png", bbox_inches="tight")
  • การปรับพารามิเตอร์ย่อย
    โดยอัตโนมัติวิธีการปรับตำแหน่งย่อยโดยอัตโนมัติเพื่อให้คำอธิบายแผนภูมิอยู่ในผืนผ้าใบโดยไม่ต้องเปลี่ยนขนาดของตัวเลขในคำตอบนี้:การสร้างรูปที่มีขนาดที่แน่นอนและไม่มีการขยาย (และตำนานนอกแกน)

การเปรียบเทียบระหว่างกรณีที่กล่าวถึงข้างต้น:

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

ทางเลือก

คำอธิบายภาพ
หนึ่งอาจใช้คำอธิบายประกอบกับรูปแทนแกน, matplotlib.figure.Figure.legend. สิ่งนี้ได้กลายเป็นประโยชน์อย่างยิ่งสำหรับเวอร์ชัน matplotlib> = 2.1 โดยไม่จำเป็นต้องมีอาร์กิวเมนต์พิเศษ

fig.legend(loc=7) 

เพื่อสร้างตำนานสำหรับศิลปินทุกคนในแกนที่แตกต่างกันของรูป ตำนานถูกวางโดยใช้การlocโต้แย้งคล้ายกับที่วางไว้ในแกน แต่ในการอ้างอิงถึงทั้งร่าง - ดังนั้นมันจะอยู่นอกแกนค่อนข้างอัตโนมัติ สิ่งที่เหลืออยู่คือการปรับเปลี่ยนแผนการย่อยโดยที่ไม่มีการซ้อนทับกันระหว่างตำนานและแกน ตรงนี้จุด"ปรับพารามิเตอร์ย่อย" จากด้านบนจะเป็นประโยชน์ ตัวอย่าง:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi)
colors=["#7aa0c4","#ca82e1" ,"#8bcd50","#e18882"]
fig, axes = plt.subplots(ncols=2)
for i in range(4):
    axes[i//2].plot(x,np.sin(x+i), color=colors[i],label="y=sin(x+{})".format(i))

fig.legend(loc=7)
fig.tight_layout()
fig.subplots_adjust(right=0.75)   
plt.show()

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

คำอธิบายแผนภูมิภายในแกนย่อยแบบแยกย่อย
ทางเลือกในการใช้งานbbox_to_anchorคือการวางคำอธิบายแผนภูมิลงในแกนย่อยแบบแยกย่อย ( lax) เนื่องจากพล็อตย่อยตำนานควรเล็กกว่าพล็อตเราอาจใช้gridspec_kw={"width_ratios":[4,1]}ในการสร้างแกน เราสามารถซ่อนแกนlax.axis("off")แต่ยังคงนำเป็นตำนานใน. จับตำนานและป้ายชื่อจะต้องได้รับจากพล็อตจริงผ่านทางh,l = ax.get_legend_handles_labels()และจากนั้นจะจ่ายให้กับตำนานในแผน,lax lax.legend(h,l)ตัวอย่างที่สมบูรณ์อยู่ด้านล่าง

import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = 6,2

fig, (ax,lax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[4,1]})
ax.plot(x,y, label="y=sin(x)")
....

h,l = ax.get_legend_handles_labels()
lax.legend(h,l, borderaxespad=0)
lax.axis("off")

plt.tight_layout()
plt.show()

สิ่งนี้สร้างพล็อตที่มีรูปร่างคล้ายกับพล็อตจากด้านบน

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

นอกจากนี้เรายังสามารถใช้ขวานแรกเพื่อวางตำนาน แต่ใช้bbox_transformขวานตำนาน

ax.legend(bbox_to_anchor=(0,0,1,1), bbox_transform=lax.transAxes)
lax.axis("off")

ในวิธีการนี้เราไม่จำเป็นต้องได้รับการจัดการตำนานภายนอก แต่เราจำเป็นต้องระบุ bbox_to_anchorอาร์กิวเมนต์

อ่านเพิ่มเติมและบันทึก:

  • พิจารณาคู่มือคำอธิบายของ matplotlib ด้วยตัวอย่างของสิ่งอื่น ๆ ที่คุณต้องการทำกับตำนาน
  • ตัวอย่างโค้ดสำหรับการวางตำนานสำหรับแผนภูมิวงกลมอาจพบได้โดยตรงในคำตอบของคำถามนี้: Python - Legend ซ้อนทับกับแผนภูมิวงกลม
  • locอาร์กิวเมนต์สามารถใช้ตัวเลขแทนสตริงซึ่งโทรสั้น แต่พวกเขาไม่ได้แมปกับแต่ละอื่น ๆ มากสังหรณ์ใจ นี่คือการทำแผนที่สำหรับการอ้างอิง:

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


fig.tight_layoutวางตำนานในการทำงานร่วมกันโดยเฉพาะแผนแกนอย่างมากด้วย หากคุณกำลังใช้plt.subplotsในการสร้างแกนเพียงแค่ให้มันเหมือนกับgridspec_kw={'height_ratios': [100, 1]}(หรือ width_ratios) ดังนั้นแกนของตำนานนั้นเล็กมาก ... จากนั้นfig.tight_layout()จะขยายให้พอดี อย่างน้อยสำหรับ matplotlib 3.0.3
travc

161

เพียงแค่โทรlegend()ออกหลังจากplot()โทรแบบนี้:

# matplotlib
plt.plot(...)
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))

# Pandas
df.myCol.plot().legend(loc='center left', bbox_to_anchor=(1, 0.5))

ผลลัพธ์จะมีลักษณะเช่นนี้:

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


4
ทำงานเมื่อผ่านพารามิเตอร์เดียวกันกับ matplotlib.pyplot.legend เช่นกัน
kidmose

8
สิ่งนี้ตัดคำในตำนานให้คนอื่นหรือไม่?
user1717828

85

ที่จะวางตำนานนอกพล็อตในพื้นที่การใช้งานlocและคำหลักของbbox_to_anchor legend()ตัวอย่างเช่นรหัสต่อไปนี้จะวางคำอธิบายสัญลักษณ์ทางด้านขวาของพื้นที่การลงจุด:

legend(loc="upper left", bbox_to_anchor=(1,1))

สำหรับข้อมูลเพิ่มเติมให้ดูที่คำแนะนำในตำนาน


10
โอเค - ฉันชอบการนำไปใช้ แต่เมื่อฉันไปบันทึกรูป (โดยไม่ต้องปรับขนาดด้วยตนเองในหน้าต่างซึ่งฉันไม่ต้องการทำทุกครั้ง) ตำนานกำลังถูกสับออก ความคิดใด ๆ เกี่ยวกับวิธีที่ฉันจะแก้ไขได้อย่างไร
astromax

@astromax ฉันไม่แน่ใจ แต่อาจจะลองโทรplt.tight_layout()?
Christian Alis

81

คำตอบสั้น ๆ : คุณสามารถใช้bbox_to_anchor+ +bbox_extra_artistsbbox_inches='tight'


คำตอบอีกต่อไป: คุณสามารถใช้ bbox_to_anchorเพื่อระบุตำแหน่งของกล่องคำอธิบายด้วยตนเองเนื่องจากคนอื่น ๆ ได้ชี้ให้เห็นในคำตอบ

อย่างไรก็ตามปัญหาปกติคือกล่องคำอธิบายภาพถูกครอบตัดเช่น:

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')

fig.savefig('image_output.png', dpi=300, format='png')

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

เพื่อป้องกันไม่ให้กล่องคำอธิบายภาพถูกครอบตัดเมื่อคุณบันทึกรูปคุณสามารถใช้พารามิเตอร์bbox_extra_artistsและbbox_inchesขอsavefigให้ใส่องค์ประกอบที่ครอบตัดไว้ในภาพที่บันทึกไว้:

fig.savefig('image_output.png', bbox_extra_artists=(lgd,), bbox_inches='tight')

ตัวอย่าง (ฉันเปลี่ยนบรรทัดสุดท้ายเพื่อเพิ่มพารามิเตอร์ 2 ตัวเท่านั้นfig.savefig()):

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# Plot
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(all_x, all_y)

# Add legend, title and axis labels
lgd = ax.legend( [ 'Lag ' + str(lag) for lag in all_x], loc='center right', bbox_to_anchor=(1.3, 0.5))
ax.set_title('Title')
ax.set_xlabel('x label')
ax.set_ylabel('y label')    

fig.savefig('image_output.png', dpi=300, format='png', bbox_extra_artists=(lgd,), bbox_inches='tight')

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

ผมหวังว่า matplotlib กำเนิดจะช่วยให้ตำแหน่งที่อยู่นอกกล่องตำนานเป็นMatlab ไม่ :

figure
x = 0:.2:12;
plot(x,besselj(1,x),x,besselj(2,x),x,besselj(3,x));
hleg = legend('First','Second','Third',...
              'Location','NorthEastOutside')
% Make the text of the legend italic and color it brown
set(hleg,'FontAngle','italic','TextColor',[.3,.2,.1])

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


6
ขอบคุณมาก! พารามิเตอร์ "bbox_to_anchor", "bbox_extra_artist" และ "" bbox_inches = 'tight "เป็นสิ่งที่ฉันต้องการเพื่อให้ทำงานได้อย่างถูกต้องดีเลิศ!
Demitrian

6
ขอบคุณ แต่จริง ๆ แล้วbbox_inches='tight'ทำงานได้อย่างสมบูรณ์แบบสำหรับฉันแม้ไม่มี bbox_extra_artist
avtomaton

1
@ avtomaton ขอบคุณที่รู้ดีคุณใช้ matplotlib รุ่นใด
Franck Dernoncourt

1
@FranckDernoncourt python3, matplotlib เวอร์ชั่น 1.5.3
avtomaton

68

นอกจากคำตอบที่ยอดเยี่ยมทั้งหมดที่นี่แล้วเวอร์ชั่นใหม่matplotlibและpylabสามารถกำหนดตำแหน่งที่จะวางคำอธิบายได้โดยอัตโนมัติโดยไม่ต้องยุ่งเกี่ยวกับพล็อตหากเป็นไปได้

pylab.legend(loc='best')

สิ่งนี้จะวางคำอธิบายแผนภูมิออกจากข้อมูลโดยอัตโนมัติถ้าเป็นไปได้! เปรียบเทียบการใช้ loc = 'best'

อย่างไรก็ตามหากไม่มีสถานที่ที่จะนำตำนานโดยไม่ทับซ้อนข้อมูลคุณจะต้องลองคำตอบอย่างใดอย่างหนึ่ง การใช้loc="best"จะไม่ทำให้เกิดตำนานนอกพล็อต


2
ขอบคุณสำหรับการชี้ให้เห็น! ฉันค้นหาสิ่งนี้เมื่อสองสามปีก่อนและไม่พบมันและมันเป็นสิ่งที่ทำให้ชีวิตของฉันง่ายขึ้น
Edgar H

4
ตัวเลือกนี้มีประโยชน์ แต่ไม่ตอบคำถามดังนั้นฉันจึงลงคะแนน เท่าที่ฉันจะบอกได้ดีที่สุดอย่าวางตำนานไว้นอกพล็อต
Tommy

3
@ ทอมมี่: ในความคิดเห็นของ OP (ซึ่งดูเหมือนว่าจะหายไปในตอนนี้) มันชัดเจนว่า OP ต้องการให้ตำนานไม่ครอบคลุมข้อมูลกราฟและเขาคิดว่านอกพล็อตเป็นวิธีเดียวที่จะทำ คุณสามารถเห็นสิ่งนี้ในคำตอบจาก mefathy, Mateo Sanchez, Bastiaan และ radtek สหกรณ์ถาม X แต่เขาอยาก Y
dotancohen

1
จริงๆแล้วไม่ใช่ เขา / เธอโดยเฉพาะขอให้ตำนานอยู่นอกพล็อต มันอยู่ในชื่อของคำถาม;) "วิธีที่จะนำตำนานออกจากพล็อต"
durbachit

4
สิ่งนี้ไม่รับประกันว่าคำอธิบายจะไม่ปิดบังข้อมูล เพียงแค่พล็อตที่มีความหนาแน่นสูง - ไม่มีที่ที่จะทำให้เกิดตำนาน ตัวอย่างเช่นลองใช้ ... จากการนำเข้า numpy, sin, pi import matplotlib.pyplot เป็น plt t = arange (0.0, 100.0, 0.01) fig = plt.figure (1) ax1 = fig.add_subplot (211) ax1 scatter (t, sin (2 * pi * t), label = 'test') ax1.grid (True) # ax1.set_ylim ((- 2, 2)) ax1.set_ylabel ('1 Hz') ax1.set_title ( 'คลื่นไซน์หรือสอง') สำหรับป้ายกำกับใน ax1.get_xticklabels (): label.set_color ('r') plt.legend (loc = 'best') plt.show ()
adam.r

56

คำตอบสั้น ๆ : เรียกใช้ draggable บนคำอธิบายแผนภูมิและเคลื่อนย้ายมันได้ทุกที่ที่คุณต้องการ:

ax.legend().draggable()

คำตอบยาว : หากคุณต้องการที่จะวางคำอธิบาย / โต้ตอบด้วยตนเองมากกว่าที่จะเขียนโปรแกรมคุณสามารถสลับโหมดที่สามารถลากได้ของตำนานเพื่อให้คุณสามารถลากมันไปได้ทุกที่ที่คุณต้องการ ตรวจสอบตัวอย่างด้านล่าง:

import matplotlib.pylab as plt
import numpy as np
#define the figure and get an axes instance
fig = plt.figure()
ax = fig.add_subplot(111)
#plot the data
x = np.arange(-5, 6)
ax.plot(x, x*x, label='y = x^2')
ax.plot(x, x*x*x, label='y = x^3')
ax.legend().draggable()
plt.show()

ไม่แน่ใจว่าฉันเข้าใจสิ่งนี้อย่างเต็มที่ ฉันจะ "ลาก" ตำนานไปยังทุกที่ที่ฉันต้องการได้อย่างไร ฉันใช้ Python 3.6 และ Jupyter Notebook
sb2020

14

ไม่ใช่สิ่งที่คุณขอ แต่ฉันพบว่ามันเป็นทางเลือกสำหรับปัญหาเดียวกัน ทำให้ตำนานกึ่งโปร่งใสเช่น: พล็อต matplotlib ที่มีคำอธิบายกึ่งโปร่งใสและกล่องข้อความกึ่งโปร่งใส

ทำสิ่งนี้ด้วย:

fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot(x,y,label=label,color=color)
# Make the legend transparent:
ax.legend(loc=2,fontsize=10,fancybox=True).get_frame().set_alpha(0.5)
# Make a transparent text box
ax.text(0.02,0.02,yourstring, verticalalignment='bottom',
                     horizontalalignment='left',
                     fontsize=10,
                     bbox={'facecolor':'white', 'alpha':0.6, 'pad':10},
                     transform=self.ax.transAxes)

8

ตามที่ระบุไว้คุณสามารถวางตำนานในพล็อตหรือปิดมันลงไปที่ขอบเล็กน้อยเช่นกัน นี่คือตัวอย่างการใช้Plotly Python API ที่สร้างด้วยIPython Notebookโน๊ตบุ๊คฉันอยู่ในทีม

ในการเริ่มต้นคุณจะต้องติดตั้งแพ็คเกจที่จำเป็น:

import plotly
import math
import random
import numpy as np

จากนั้นติดตั้ง Plotly:

un='IPython.Demo'
k='1fw3zw2o13'
py = plotly.plotly(username=un, key=k)


def sin(x,n):
sine = 0
for i in range(n):
    sign = (-1)**i
    sine = sine + ((x**(2.0*i+1))/math.factorial(2*i+1))*sign
return sine

x = np.arange(-12,12,0.1)

anno = {
'text': '$\\sum_{k=0}^{\\infty} \\frac {(-1)^k x^{1+2k}}{(1 + 2k)!}$',
'x': 0.3, 'y': 0.6,'xref': "paper", 'yref': "paper",'showarrow': False,
'font':{'size':24}
}

l = {
'annotations': [anno], 
'title': 'Taylor series of sine',
'xaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'yaxis':{'ticks':'','linecolor':'white','showgrid':False,'zeroline':False},
'legend':{'font':{'size':16},'bordercolor':'white','bgcolor':'#fcfcfc'}
}

py.iplot([{'x':x, 'y':sin(x,1), 'line':{'color':'#e377c2'}, 'name':'$x\\\\$'},\
      {'x':x, 'y':sin(x,2), 'line':{'color':'#7f7f7f'},'name':'$ x-\\frac{x^3}{6}$'},\
      {'x':x, 'y':sin(x,3), 'line':{'color':'#bcbd22'},'name':'$ x-\\frac{x^3}{6}+\\frac{x^5}{120}$'},\
      {'x':x, 'y':sin(x,4), 'line':{'color':'#17becf'},'name':'$ x-\\frac{x^5}{120}$'}], layout=l)

สิ่งนี้จะสร้างกราฟของคุณและช่วยให้คุณมีโอกาสรักษาตำนานไว้ในเนื้อเรื่อง ค่าเริ่มต้นสำหรับคำอธิบายหากไม่ได้ตั้งค่าไว้ให้วางลงในพล็อตตามที่แสดงไว้ที่นี่

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

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

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

คุณสามารถย้ายและจัดรูปแบบคำอธิบายแผนภูมิและกราฟใหม่ด้วยรหัสหรือด้วย GUI ในการเลื่อนคำอธิบายคุณมีตัวเลือกต่อไปนี้เพื่อจัดตำแหน่งคำอธิบายแผนภูมิภายในกราฟโดยการกำหนดค่า x และ y เป็น <= 1 เช่น:

  • {"x" : 0,"y" : 0} -- ล่างซ้าย
  • {"x" : 1, "y" : 0} - ล่างขวา
  • {"x" : 1, "y" : 1} - ขวาบน
  • {"x" : 0, "y" : 1} -- บนซ้าย
  • {"x" :.5, "y" : 0} - Bottom Center
  • {"x": .5, "y" : 1} - ศูนย์สุดยอด

ในกรณีนี้เราเลือกมุมขวาบนlegendstyle = {"x" : 1, "y" : 1}ซึ่งอธิบายไว้ในเอกสารประกอบ :

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


3

มีบางอย่างในสายงานนี้ที่เหมาะกับฉัน เริ่มต้นด้วยรหัสเล็กน้อยที่นำมาจาก Joe วิธีนี้แก้ไขความกว้างของหน้าต่างเพื่อให้พอดีกับคำอธิบายแผนภูมิทางด้านขวาของรูปภาพโดยอัตโนมัติ

import matplotlib.pyplot as plt
import numpy as np

plt.ion()

x = np.arange(10)

fig = plt.figure()
ax = plt.subplot(111)

for i in xrange(5):
    ax.plot(x, i * x, label='$y = %ix$'%i)

# Put a legend to the right of the current axis
leg = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

plt.draw()

# Get the ax dimensions.
box = ax.get_position()
xlocs = (box.x0,box.x1)
ylocs = (box.y0,box.y1)

# Get the figure size in inches and the dpi.
w, h = fig.get_size_inches()
dpi = fig.get_dpi()

# Get the legend size, calculate new window width and change the figure size.
legWidth = leg.get_window_extent().width
winWidthNew = w*dpi+legWidth
fig.set_size_inches(winWidthNew/dpi,h)

# Adjust the window size to fit the figure.
mgr = plt.get_current_fig_manager()
mgr.window.wm_geometry("%ix%i"%(winWidthNew,mgr.window.winfo_height()))

# Rescale the ax to keep its original size.
factor = w*dpi/winWidthNew
x0 = xlocs[0]*factor
x1 = xlocs[1]*factor
width = box.width*factor
ax.set_position([x0,ylocs[0],x1-x0,ylocs[1]-ylocs[0]])

plt.draw()

ฉันพบว่ามันมีประโยชน์มากและใช้งานได้สำหรับฉัน โปรดทราบว่าหากคุณอยู่ในแบ็กเอนด์ wx (เช่นใช้ windows) ให้แทนที่ mgr.window.wm_geometry ("% ix% i"% (winWidthNew, mgr.window.winfo_height ()) ด้วย mgr.window.SetClientSizeWH , winHeightNew) หรือสิ่งที่ชอบ
Ezekiel Kruglick

หากคุณกำลังใช้แบ็กเอนด์ Qt4Agg (ซึ่งเป็นค่าเริ่มต้นในการติดตั้งลินุกซ์ของฉัน matplotlib) แล้วแทนที่บรรทัดด้วยmgr.window.wm_geometry(...) mgr.window.setFixedWidth(winWidthNew)
Filip S.

และอย่างที่ฉันเพิ่งค้นพบถ้าคุณใช้แบ็กเอนด์ที่ไม่แสดงหน้าต่างใด ๆ นั่นหมายถึงการบันทึกตรงไปยังไฟล์ (เช่นแบ็กเอนด์ SVG และ AGG) เพียงข้ามหน้าต่างปรับขนาดทั้งหมด fig.set_size_inches(...)ดูแลการปรับขนาดที่คุณต้องการ
Filip S.

3

มันคุ้มค่าที่จะรีเฟรชคำถามนี้เนื่องจาก Matplotlib เวอร์ชันใหม่ทำให้การวางตำแหน่งของตำนานนอกพล็อตได้ง่ายขึ้นมาก ฉันผลิตตัวอย่างนี้ด้วยเวอร์ชัน Matplotlib3.1.1ผมผลิตตัวอย่างนี้กับรุ่น

ผู้ใช้สามารถส่งผ่าน 2 tuple ของพิกัดไปยังlocพารามิเตอร์เพื่อจัดวางคำอธิบายแผนภูมิได้ทุกที่ในกล่องขอบเขต gotcha เพียงอย่างเดียวคือคุณต้องเรียกใช้plt.tight_layout()เพื่อรับ matplotlib เพื่อคำนวณขนาดพล็อตใหม่เพื่อให้มองเห็นคำอธิบาย:

import matplotlib.pyplot as plt

plt.plot([0, 1], [0, 1], label="Label 1")
plt.plot([0, 1], [0, 2], label='Label 2')

plt.legend(loc=(1.05, 0.5))
plt.tight_layout()

สิ่งนี้นำไปสู่พล็อตต่อไปนี้:

พล็อตกับตำนานนอก

อ้างอิง:


2

figlegendนอกจากนี้คุณยังสามารถลอง เป็นไปได้ที่จะสร้างตำนานที่เป็นอิสระจากวัตถุ Axes ใด ๆ อย่างไรก็ตามคุณอาจต้องสร้างเส้นทาง "จำลอง" บางอย่างเพื่อให้แน่ใจว่าการจัดรูปแบบสำหรับวัตถุได้รับการส่งผ่านอย่างถูกต้อง


1

นี่คือตัวอย่างจาก matplotlib กวดวิชาพบว่าที่นี่ นี่เป็นหนึ่งในตัวอย่างที่ง่ายกว่า แต่ฉันเพิ่มความโปร่งใสให้กับคำอธิบายและเพิ่ม plt.show () เพื่อให้คุณสามารถวางสิ่งนี้ลงในเชลล์เชิงโต้ตอบและได้ผลลัพธ์:

import matplotlib.pyplot as plt
p1, = plt.plot([1, 2, 3])
p2, = plt.plot([3, 2, 1])
p3, = plt.plot([2, 3, 1])
plt.legend([p2, p1, p3], ["line 1", "line 2", "line 3"]).get_frame().set_alpha(0.5)
plt.show()

1

วิธีแก้ปัญหาที่เหมาะกับฉันเมื่อฉันมีตำนานที่ยิ่งใหญ่คือการใช้เลย์เอาต์รูปภาพที่ว่างเปล่าเป็นพิเศษ ในตัวอย่างต่อไปนี้ฉันสร้าง 4 แถวและที่ด้านล่างฉันพล็อตรูปภาพโดยมีออฟเซ็ตสำหรับคำอธิบาย (bbox_to_anchor) ที่ด้านบนจะไม่ถูกตัด

f = plt.figure()
ax = f.add_subplot(414)
lgd = ax.legend(loc='upper left', bbox_to_anchor=(0, 4), mode="expand", borderaxespad=0.3)
ax.autoscale_view()
plt.savefig(fig_name, format='svg', dpi=1200, bbox_extra_artists=(lgd,), bbox_inches='tight')

1

นี่เป็นอีกวิธีการหนึ่งซึ่งคล้ายกับการเพิ่มbbox_extra_artistsและbbox_inchesที่คุณไม่จำเป็นต้องมีศิลปินพิเศษในขอบเขตของการsavefigโทร ฉันทำสิ่งนี้ตั้งแต่ฉันสร้างพล็อตส่วนใหญ่ภายในฟังก์ชั่น

แทนที่จะเพิ่มการเพิ่มทั้งหมดของคุณลงในกล่องขอบเมื่อคุณต้องการเขียนมันออกมาคุณสามารถเพิ่มพวกมันล่วงหน้าให้กับFigureศิลปินของ ใช้สิ่งที่คล้ายกับคำตอบของ Franck Dernoncourt ด้านบน :

import matplotlib.pyplot as plt

# data 
all_x = [10,20,30]
all_y = [[1,3], [1.5,2.9],[3,2]]

# plotting function
def gen_plot(x, y):
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.plot(all_x, all_y)
    lgd = ax.legend( [ "Lag " + str(lag) for lag in all_x], loc="center right", bbox_to_anchor=(1.3, 0.5))
    fig.artists.append(lgd) # Here's the change
    ax.set_title("Title")
    ax.set_xlabel("x label")
    ax.set_ylabel("y label")
    return fig

# plotting
fig = gen_plot(all_x, all_y)

# No need for `bbox_extra_artists`
fig.savefig("image_output.png", dpi=300, format="png", bbox_inches="tight")

นี่คือพล็อตที่สร้างขึ้น


-1

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

from matplotlib as plt
from matplotlib.font_manager import FontProperties
...
...
t = A[:,0]
sensors = A[:,index_lst]

for i in range(sensors.shape[1]):
    plt.plot(t,sensors[:,i])

plt.xlabel('s')
plt.ylabel('°C')
lgd = plt.legend(b,loc='center left', bbox_to_anchor=(1, 0.5),fancybox = True, shadow = True)

คลิกเพื่อดูพล็อต

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