업데이트 : 사용자 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();
이렇게하면 레이블 중 하나가 왼쪽 상단 모서리에 배치됩니다. 이 문제를 해결하는 방법에 대한 아이디어가 있습니까? 문제는 선이 너무 가깝다는 것입니다.