如何使用适当的文本旋转来注释回归线

How to annotate a regression line with the proper text rotation

我有 following snippet of code 通过图形上的点集合绘制一条最佳拟合线,并用相应的 R2 值对其进行注释:

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats

x = 50 * np.random.rand(20) + 50
y = 200 * np.random.rand(20)
plt.plot(x, y, 'o')

# k, n = np.polyfit(x, y, 1)
k, n, r, _, _ = scipy.stats.linregress(x, y)
line = plt.axline((0, n), slope=k, color='blue')
xy = line.get_xydata()
plt.annotate(
    f'$R^2={r**2:.3f}$',
    (xy[0] + xy[-1]) // 2,
    xycoords='axes fraction',
    ha='center', va='center_baseline',
    rotation=k, rotation_mode='anchor',
)

plt.show()

我在 annotate 中尝试了各种不同的 (x,y) 对、不同的 xycoords 和其他关键字参数,但我无法让注释正确出现在我想要的位置它。如何让文本注释以适当的旋转显示在线条上方,位于线条的中点或两端?

1。标注坐标

我们不能在这里使用 xydata 计算坐标,因为 axline() 只是 returns 虚拟 xydata(可能是由于 matplotlib 内部绘制无限线的方式):

print(line.get_xydata())
# array([[0., 0.],
#        [1., 1.]])

相反,我们可以根据 xlim():

计算文本坐标
xmin, xmax = plt.xlim()
xtext = (xmin + xmax) // 2
ytext = k*xtext + n

请注意,这些是数据坐标,因此应与 xycoords='data' 一起使用,而不是 'axes fraction'


2。注释角度

我们不能仅从线点计算角度,因为角度还将取决于轴限制和图形尺寸(例如,想象 6x4 图形与 2x8 图形中所需的旋转角度)。

相反,我们应该 以获得正确的视觉旋转:

rs = np.random.RandomState(0)
x = 50 * rs.rand(20) + 50
y = 200 * rs.rand(20)
plt.plot(x, y, 'o')

# save ax and fig scales
xmin, xmax = plt.xlim()
ymin, ymax = plt.ylim()
xfig, yfig = plt.gcf().get_size_inches()

k, n, r, _, _ = scipy.stats.linregress(x, y)
plt.axline((0, n), slope=k, color='blue')

# restore x and y limits after axline
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)

# find text coordinates at midpoint of regression line
xtext = (xmin + xmax) // 2
ytext = k*xtext + n

# find run and rise of (xtext, ytext) vs (0, n)
dx = xtext
dy = ytext - n

# normalize to ax and fig scales
xnorm = dx * xfig / (xmax - xmin)
ynorm = dy * yfig / (ymax - ymin)

# find normalized annotation angle in radians
rotation = np.rad2deg(np.arctan2(ynorm, xnorm))

plt.annotate(
    f'$R^2={r**2:.3f}$',
    (xtext, ytext), xycoords='data',
    ha='center', va='bottom',
    rotation=rotation, rotation_mode='anchor',
)