Ale*_*ary 77 charts matplotlib coordinates
在Matplotlib中,制作一个传奇(example_legend()下图)并不太难,但我认为将标签放在正在绘制的曲线上是更好的风格(example_inline()如下图所示).这可能非常繁琐,因为我必须手动指定坐标,如果我重新格式化绘图,我可能需要重新定位标签.有没有办法在Matplotlib中自动生成曲线上的标签?能够以与曲线角度对应的角度定向文本的加分点.
import numpy as np
import matplotlib.pyplot as plt
def example_legend():
plt.clf()
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
plt.plot(x, y1, label='sin')
plt.plot(x, y2, label='cos')
plt.legend()
Run Code Online (Sandbox Code Playgroud)

def example_inline():
plt.clf()
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
plt.plot(x, y1, label='sin')
plt.plot(x, y2, label='cos')
plt.text(0.08, 0.2, 'sin')
plt.text(0.9, 0.2, 'cos')
Run Code Online (Sandbox Code Playgroud)

Nau*_*ile 63
更新:用户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值为floats时有效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)
Run Code Online (Sandbox Code Playgroud)
测试代码以生成上面的漂亮图片:
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()
Run Code Online (Sandbox Code Playgroud)
Ioa*_*dis 43
@Jan Kuiken的答案肯定是经过深思熟虑和彻底的,但有一些警告:
一种更简单的方法是注释每个图的最后一点.为了强调,这一点也可以圈出来.这可以通过一个额外的线来完成:
from matplotlib import pyplot as plt
for i, (x, y) in enumerate(samples):
plt.plot(x, y)
plt.text(x[-1], y[-1], 'sample {i}'.format(i=i))
Run Code Online (Sandbox Code Playgroud)
一种变体就是使用ax.annotate.
Jan*_*ken 25
不错的问题,前一段时间我对此进行了一些实验,但没有使用它,因为它仍然没有防弹.我将绘图区域划分为32x32网格,并根据以下规则计算每行标签最佳位置的"潜在字段":
代码是这样的:
import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage
def my_legend(axis = None):
if axis == None:
axis = plt.gca()
N = 32
Nlines = len(axis.lines)
print Nlines
xmin, xmax = axis.get_xlim()
ymin, ymax = axis.get_ylim()
# the 'point of presence' matrix
pop = np.zeros((Nlines, N, N), dtype=np.float)
for l in range(Nlines):
# get xy data and scale it to the NxN squares
xy = axis.lines[l].get_xydata()
xy = (xy - [xmin,ymin]) / ([xmax-xmin, ymax-ymin]) * N
xy = xy.astype(np.int32)
# mask stuff outside plot
mask = (xy[:,0] >= 0) & (xy[:,0] < N) & (xy[:,1] >= 0) & (xy[:,1] < N)
xy = xy[mask]
# add to pop
for p in xy:
pop[l][tuple(p)] = 1.0
# find whitespace, nice place for labels
ws = 1.0 - (np.sum(pop, axis=0) > 0) * 1.0
# don't use the borders
ws[:,0] = 0
ws[:,N-1] = 0
ws[0,:] = 0
ws[N-1,:] = 0
# blur the pop's
for l in range(Nlines):
pop[l] = ndimage.gaussian_filter(pop[l], sigma=N/5)
for l in range(Nlines):
# positive weights for current line, negative weight for others....
w = -0.3 * np.ones(Nlines, dtype=np.float)
w[l] = 0.5
# calculate a field
p = ws + np.sum(w[:, np.newaxis, np.newaxis] * pop, axis=0)
plt.figure()
plt.imshow(p, interpolation='nearest')
plt.title(axis.lines[l].get_label())
pos = np.argmax(p) # note, argmax flattens the array first
best_x, best_y = (pos / N, pos % N)
x = xmin + (xmax-xmin) * best_x / N
y = ymin + (ymax-ymin) * best_y / N
axis.text(x, y, axis.lines[l].get_label(),
horizontalalignment='center',
verticalalignment='center')
plt.close('all')
x = np.linspace(0, 1, 101)
y1 = np.sin(x * np.pi / 2)
y2 = np.cos(x * np.pi / 2)
y3 = x * x
plt.plot(x, y1, 'b', label='blue')
plt.plot(x, y2, 'r', label='red')
plt.plot(x, y3, 'g', label='green')
my_legend()
plt.show()
Run Code Online (Sandbox Code Playgroud)
由此产生的情节:

Nic*_*mer 16
matplotx(我写的)可以将line_labels()标签绘制在线条的右侧。当太多线路集中在一个位置时,它也足够聪明,可以避免重叠。(有关示例,请参阅星图。)它通过解决标签目标位置上的特定非负最小二乘问题来实现这一点。无论如何,在许多情况下,一开始就没有重叠,例如下面的示例,这甚至没有必要。
import matplotlib.pyplot as plt
import matplotx
import numpy as np
# create data
rng = np.random.default_rng(0)
offsets = [1.0, 1.50, 1.60]
labels = ["no balancing", "CRV-27", "CRV-27*"]
x0 = np.linspace(0.0, 3.0, 100)
y = [offset * x0 / (x0 + 1) + 0.1 * rng.random(len(x0)) for offset in offsets]
# plot
with plt.style.context(matplotx.styles.dufte):
for yy, label in zip(y, labels):
plt.plot(x0, yy, label=label)
plt.xlabel("distance [m]")
matplotx.ylabel_top("voltage [V]") # move ylabel to the top, rotate
matplotx.line_labels() # line labels to the right
plt.show()
# plt.savefig("out.png", bbox_inches="tight")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
57065 次 |
| 最近记录: |