ปรับปรุง:ผู้ใช้cphycได้สร้างความกรุณาที่เก็บ Github รหัสในคำตอบนี้ (ดูที่นี่ ) pip install matplotlib-label-lines
และรวมรหัสลงในแพคเกจซึ่งอาจถูกติดตั้งโดยใช้
ภาพสวย:
ในmatplotlib
การติดป้ายโครงร่างโครงร่างนั้นค่อนข้างง่าย (โดยอัตโนมัติหรือวางป้ายกำกับด้วยตนเองด้วยการคลิกเมาส์) ดูเหมือนว่าจะไม่มีความสามารถเทียบเท่ากับชุดข้อมูลในรูปแบบนี้! อาจมีเหตุผลเชิงความหมายบางประการที่ไม่รวมคุณลักษณะนี้ซึ่งฉันขาดหายไป
ไม่ว่าฉันจะเขียนโมดูลต่อไปนี้ซึ่งใช้การอนุญาตใด ๆ สำหรับการติดฉลากพล็อตกึ่งอัตโนมัติ ต้องใช้เพียงnumpy
สองสามฟังก์ชันจากmath
ไลบรารีมาตรฐาน
คำอธิบาย
ลักษณะการทำงานเริ่มต้นของlabelLines
ฟังก์ชันคือการเว้นวรรคป้ายกำกับให้เท่า ๆ กันตามแนวx
แกน (วางตามy
ค่า - ค่าที่ถูกต้องโดยอัตโนมัติ) หากคุณต้องการคุณสามารถส่งอาร์เรย์ของพิกัด x ของแต่ละป้ายกำกับ คุณยังสามารถปรับแต่งตำแหน่งของป้ายกำกับหนึ่งป้าย (ดังแสดงในพล็อตด้านล่างขวา) และเว้นระยะห่างเท่า ๆ กันหากต้องการ
นอกจากนี้ไฟล์ label_lines
ฟังก์ชั่นนี้ไม่ได้อธิบายถึงบรรทัดที่ไม่มีเลเบลที่กำหนดไว้ในplot
คำสั่ง (หรือถูกต้องมากขึ้นหากมีป้ายกำกับ'_line'
)
อาร์กิวเมนต์คำหลักถูกส่งไปยัง labelLines
หรือlabelLine
ถูกส่งต่อไปยังการtext
เรียกใช้ฟังก์ชัน (อาร์กิวเมนต์คำสำคัญบางตัวถูกตั้งค่าหากรหัสการเรียกใช้ไม่ระบุ)
ปัญหา
- บางครั้งกรอบคำอธิบายประกอบจะรบกวนเส้นโค้งอื่น ๆ อย่างไม่น่าพึงปรารถนา ดังที่แสดงโดย
1
10
คำอธิบายประกอบและในพล็อตด้านซ้ายบน ฉันไม่แน่ใจว่าสิ่งนี้สามารถหลีกเลี่ยงได้
- จะเป็นการดีหากระบุก
y
ตำแหน่งแทนในบางครั้ง
- ยังคงเป็นกระบวนการซ้ำเพื่อรับคำอธิบายประกอบในตำแหน่งที่ถูกต้อง
- ใช้ได้เฉพาะเมื่อ
x
ค่า -axis เป็นfloat
s
Gotchas
- โดยค่าเริ่มต้น
labelLines
ฟังก์ชันจะถือว่าชุดข้อมูลทั้งหมดครอบคลุมช่วงที่ระบุโดยขีด จำกัด ของแกน ลองดูเส้นโค้งสีน้ำเงินในพล็อตด้านซ้ายบนของภาพสวย ๆ หากมีเฉพาะข้อมูลสำหรับx
ช่วง0.5
-1
แล้วเราก็ไม่อาจวางป้ายชื่อที่ตำแหน่งที่ต้องการ (ซึ่งน้อยกว่า0.2
) ดูคำถามนี้สำหรับตัวอย่างที่น่ารังเกียจโดยเฉพาะ ในตอนนี้รหัสไม่ได้ระบุสถานการณ์นี้อย่างชาญฉลาดและจัดเรียงป้ายกำกับใหม่อย่างไรก็ตามมีวิธีแก้ปัญหาที่สมเหตุสมผล ฟังก์ชัน labelLines รับxvals
อาร์กิวเมนต์ รายการของ - x
ค่าที่ผู้ใช้ระบุแทนการแจกแจงเชิงเส้นเริ่มต้นตามความกว้าง ดังนั้นผู้ใช้สามารถตัดสินใจได้ว่าx
- ค่าที่จะใช้สำหรับตำแหน่งป้ายกำกับของชุดข้อมูลแต่ละชุด
นอกจากนี้ฉันเชื่อว่านี่เป็นคำตอบแรกที่จะทำให้ไฟล์ บรรลุวัตถุประสงค์โบนัสในการจัดแนวฉลากให้ตรงกับเส้นโค้ง :)
label_lines.py:
from math import atan2,degrees
import numpy as np
#Label line with line2D label data
def labelLine(line,x,label=None,align=True,**kwargs):
ax = line.axes
xdata = line.get_xdata()
ydata = line.get_ydata()
if (x < xdata[0]) or (x > xdata[-1]):
print('x label location is outside data range!')
return
#Find corresponding y co-ordinate and angle of the line
ip = 1
for i in range(len(xdata)):
if x < xdata[i]:
ip = i
break
y = ydata[ip-1] + (ydata[ip]-ydata[ip-1])*(x-xdata[ip-1])/(xdata[ip]-xdata[ip-1])
if not label:
label = line.get_label()
if align:
#Compute the slope
dx = xdata[ip] - xdata[ip-1]
dy = ydata[ip] - ydata[ip-1]
ang = degrees(atan2(dy,dx))
#Transform to screen co-ordinates
pt = np.array([x,y]).reshape((1,2))
trans_angle = ax.transData.transform_angles(np.array((ang,)),pt)[0]
else:
trans_angle = 0
#Set a bunch of keyword arguments
if 'color' not in kwargs:
kwargs['color'] = line.get_color()
if ('horizontalalignment' not in kwargs) and ('ha' not in kwargs):
kwargs['ha'] = 'center'
if ('verticalalignment' not in kwargs) and ('va' not in kwargs):
kwargs['va'] = 'center'
if 'backgroundcolor' not in kwargs:
kwargs['backgroundcolor'] = ax.get_facecolor()
if 'clip_on' not in kwargs:
kwargs['clip_on'] = True
if 'zorder' not in kwargs:
kwargs['zorder'] = 2.5
ax.text(x,y,label,rotation=trans_angle,**kwargs)
def labelLines(lines,align=True,xvals=None,**kwargs):
ax = lines[0].axes
labLines = []
labels = []
#Take only the lines which have labels other than the default ones
for line in lines:
label = line.get_label()
if "_line" not in label:
labLines.append(line)
labels.append(label)
if xvals is None:
xmin,xmax = ax.get_xlim()
xvals = np.linspace(xmin,xmax,len(labLines)+2)[1:-1]
for line,x,label in zip(labLines,xvals,labels):
labelLine(line,x,label,align,**kwargs)
รหัสทดสอบเพื่อสร้างภาพสวย ๆ ด้านบน:
from matplotlib import pyplot as plt
from scipy.stats import loglaplace,chi2
from labellines import *
X = np.linspace(0,1,500)
A = [1,2,5,10,20]
funcs = [np.arctan,np.sin,loglaplace(4).pdf,chi2(5).pdf]
plt.subplot(221)
for a in A:
plt.plot(X,np.arctan(a*X),label=str(a))
labelLines(plt.gca().get_lines(),zorder=2.5)
plt.subplot(222)
for a in A:
plt.plot(X,np.sin(a*X),label=str(a))
labelLines(plt.gca().get_lines(),align=False,fontsize=14)
plt.subplot(223)
for a in A:
plt.plot(X,loglaplace(4).pdf(a*X),label=str(a))
xvals = [0.8,0.55,0.22,0.104,0.045]
labelLines(plt.gca().get_lines(),align=False,xvals=xvals,color='k')
plt.subplot(224)
for a in A:
plt.plot(X,chi2(5).pdf(a*X),label=str(a))
lines = plt.gca().get_lines()
l1=lines[-1]
labelLine(l1,0.6,label=r'$Re=${}'.format(l1.get_label()),ha='left',va='bottom',align = False)
labelLines(lines[:-1],align=False)
plt.show()
plt.plot(x2, 3*x2**2, label="3x*x"); plt.plot(x2, 2*x2**2, label="2x*x"); plt.plot(x2, 0.5*x2**2, label="0.5x*x"); plt.plot(x2, -1*x2**2, label="-x*x"); plt.plot(x2, -2.5*x2**2, label="-2.5*x*x"); my_legend();
สิ่งนี้ทำให้หนึ่งในป้ายกำกับที่มุมบนซ้าย มีแนวคิดในการแก้ไขปัญหานี้อย่างไร ดูเหมือนว่าปัญหาอาจเกิดจากเส้นอยู่ใกล้กันเกินไป