Pembaruan: Pengguna cphyc telah dengan ramah membuat repositori Github untuk kode dalam jawaban ini (lihat di sini ), dan memaketkan kode ke dalam paket yang dapat diinstal menggunakan pip install matplotlib-label-lines
.
Gambar yang indah:
Di matplotlib
dalamnya cukup mudah untuk memberi label pada plot kontur (baik secara otomatis atau dengan menempatkan label secara manual dengan klik mouse). Tampaknya belum ada kemampuan yang setara untuk melabeli seri data dengan cara ini! Mungkin ada beberapa alasan semantik untuk tidak menyertakan fitur ini yang saya lewatkan.
Terlepas dari itu, saya telah menulis modul berikut yang memungkinkan pelabelan plot semi-otomatis. Ini hanya membutuhkan numpy
dan beberapa fungsi dari math
perpustakaan standar .
Deskripsi
Perilaku default dari labelLines
fungsi ini adalah memberi jarak label secara merata di sepanjang x
sumbu (tentu saja secara otomatis menempatkan pada nilai yang benar y
). Jika mau, Anda bisa meneruskan larik koordinat x dari masing-masing label. Anda bahkan dapat mengubah lokasi satu label (seperti yang ditunjukkan di plot kanan bawah) dan memberi jarak pada label lainnya secara merata jika Anda mau.
Selain itu, label_lines
fungsi tersebut tidak memperhitungkan baris yang belum diberi label pada plot
perintah (atau lebih akurat jika label berisi '_line'
).
Argumen kata kunci diteruskan ke labelLines
atau labelLine
diteruskan ke text
pemanggilan fungsi (beberapa argumen kata kunci disetel jika kode pemanggil memilih untuk tidak menentukan).
Masalah
- Kotak pembatas anotasi terkadang mengganggu kurva lain yang tidak diinginkan. Seperti yang ditunjukkan oleh anotasi
1
dan 10
di plot kiri atas. Saya bahkan tidak yakin ini bisa dihindari.
- Akan lebih baik untuk menentukan
y
posisi kadang-kadang.
- Ini masih merupakan proses berulang untuk mendapatkan anotasi di lokasi yang tepat
- Ini hanya bekerja jika nilai
x
-axis adalah float
s
Gotchas
- Secara default,
labelLines
fungsi mengasumsikan bahwa semua rangkaian data menjangkau rentang yang ditentukan oleh batas sumbu. Perhatikan kurva biru di kiri atas gambar cantik. Jika hanya ada data yang tersedia untuk x
rentang tersebut 0.5
- 1
maka kami tidak mungkin menempatkan label di lokasi yang diinginkan (yang sedikit kurang dari 0.2
). Lihat pertanyaan ini untuk contoh yang sangat buruk. Saat ini, kode tidak dengan cerdas mengidentifikasi skenario ini dan mengatur ulang label, namun ada solusi yang masuk akal. Fungsi labelLines mengambil xvals
argumen; daftar- x
nilai yang ditentukan oleh pengguna, bukan distribusi linier default di seluruh lebar. Sehingga pengguna bisa memutuskan yang manax
-nilai yang akan digunakan untuk penempatan label dari setiap seri data.
Juga, saya yakin ini adalah jawaban pertama untuk menyelesaikan tujuan bonus dalam menyelaraskan label dengan kurva tempat mereka berada. :)
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)
Uji kode untuk menghasilkan gambar cantik di atas:
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();
Ini menempatkan salah satu label di sudut kiri atas. Ada ide tentang cara memperbaikinya? Sepertinya masalahnya mungkin garis-garisnya terlalu berdekatan.