如何使用适当的文本旋转来注释回归线
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',
)
我有 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',
)