matplotlib 正确旋转和对齐注记标签

eni9jsuy  于 2023-06-23  发布在  其他
关注(0)|答案(3)|浏览(108)

下面的代码在平面上放置一些点,并从中心到每个点绘制一条线。对于每个点,都有一个标签,并希望将标签放在该点之后。因此,从中心开始,我们看到一条线,然后是一个点,然后是一个文本。我想把标签与直线的斜率相同。
目前,我有这个代码,但正如你所看到的,旋转的文本没有正确对齐。我该怎么弥补?

import matplotlib.pyplot as plt
import numpy as np
from math import *
a = np.array([
[-0.108,0.414],
[0.755,-0.152],
[0.871,-0.039],
],)
labels = ["XXXXXXX", "YYYYYY", "ZZZZZZZ"]

x, y = a.T
plt.scatter(x, y)
plt.xlim(-1,1)
plt.ylim(-1,1)

ax = plt.axes()
for i in range(a.shape[0]):
   px = a[i,0]
   py = a[i,1]
   ax.arrow(0, 0, px, py, head_width=0, head_length=0.1, length_includes_head=True)
   angle = degrees(atan(py/px))
   ax.annotate(labels[i], (px, py), rotation=angle)

plt.grid(True)
plt.show()

更新:
我使用了提出的解决方案here和修改

text_plot_location = np.array([0.51,0.51])
trans_angle = plt.gca().transData.transform_angles(np.array((45,)),text_plot_location.reshape((1,2)))[0]
ax.annotate(labels[i], (px, py), rotation=text_plot_location)

但是,我得到这个错误TypeError: unhashable type: 'numpy.ndarray'

wnavrhmk

wnavrhmk1#

不是很理想,但更接近你想要的。缺点是文本偏移量的任意值为30点,该值适用于给定的标签,但需要针对更长或更短的标签进行调整。

import matplotlib.pyplot as plt
import numpy as np
from math import *
a = np.array([[-0.108,0.414],[0.755,-0.152],[0.871,-0.039]])
labels = ["XXXXXXX", "YYYYYY", "ZZZZZZZ"]

x, y = a.T
plt.scatter(x, y)
plt.xlim(-1,1)
plt.ylim(-1,1)

ax = plt.axes()
for i in range(a.shape[0]):
   px = a[i,0]
   py = a[i,1]
   ax.arrow(0, 0, px, py, head_width=0, head_length=0.1, length_includes_head=True)
   angle = atan(py/px)
   d = (-1 if px < 0 else 1) * 30
   ax.annotate(labels[i], (px, py), rotation=degrees(angle), textcoords="offset points", 
               xytext=(d*cos(angle), d*sin(angle)), 
               verticalalignment='center', horizontalalignment='center')

plt.grid(True)
plt.show()

xsuvu9jc

xsuvu9jc2#

@mapf的链接稍微干净一点,但这是我想出来的:

import matplotlib.pyplot as plt
import numpy as np
a = np.array([
[-0.108,0.414],
[0.755,-0.152],
[0.871,-0.039],
],)
labels = ["XXXXXXX", "YYYYYY", "ZZZZZZZ"]

x, y = a.T

fig, ax = plt.subplots()
ax.scatter(x, y)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
line, = ax.plot(*a.T)
for jdx, (label, point) in enumerate(zip(labels, a)):
    # find closest point
    tmp = np.linalg.norm(a - point, axis = 1)
    idx = np.argsort(tmp)[1]
    other = a[idx]
    
    # compute angle
    deg = np.angle(complex(*(point - other)))
    deg = np.rad2deg(deg)
    ax.annotate(label, point, rotation = deg,
            ha = 'left', va = 'baseline',
            transform = ax.transData)
ax.grid(True)
fig.show()

我不知道为什么Angular 不完全匹配的线。

vltsax25

vltsax253#

您在更新中犯了一个简单的错误。您需要将trans_angle传递给rotation而不是text_plot_location关键字,但是,我不确定结果是否是您想要的。

import matplotlib.pyplot as plt
import numpy as np
from math import *
a = np.array([
[-0.108,0.414],
[0.755,-0.152],
[0.871,-0.039],
],)
labels = ["XXXXXXX", "YYYYYY", "ZZZZZZZ"]

x, y = a.T
plt.scatter(x, y)
plt.xlim(-1,1)
plt.ylim(-1,1)

ax = plt.axes()
for i in range(a.shape[0]):
    px = a[i, 0]
    py = a[i, 1]
    ax.arrow(0, 0, px, py, head_width=0, head_length=0.1,
             length_includes_head=True)
    text_plot_location = np.array([0.51, 0.51])
    angle = degrees(atan(py / px))
    trans_angle = plt.gca().transData.transform_angles(
        np.array((angle,)), text_plot_location.reshape((1, 2))
    )[0]
    ax.annotate(labels[i], (px, py), rotation=trans_angle)

plt.grid(True)
plt.show()

相关问题