oli*_*rsm 3 python logarithm matplotlib rotation
我正在处理对数刻度的数据,并希望旋转它以适合一条线。我知道该模型,但不确定我应该插入哪个角度transform_angles以恢复正确的旋转。经过一些试验和错误后,我知道对于我需要的轴限制,答案大约是 10 度。
import matplotlib.pylab as plt
import numpy as np
plt.clf()
plt.yscale('log')
plt.ylim((1e-11, 1e-1)) # Other data is usually plotted and these are the ranges I need.
plt.xlim((-0.2, 7.2))
x_fit = np.linspace(0.8, 3.2, 1000)
y_ols = (lambda x: np.exp(np.log(2)*(-20.8 + -1.23 * x)))(x_fit) # I get these numbers from OLS fitting.
plt.plot(x_fit, y_ols, 'b-', dashes='', label='__nolegend__')
plt.gca().text(np.min(x_fit), 1.2*y_ols[0], r'$O(2^{{ {:.3}x }})$'.format(-1.23), rotation=-10).set_bbox(dict(facecolor='w', alpha=0.7, edgecolor='k', linewidth=0)) # There are several others lines which have been omitted.
Run Code Online (Sandbox Code Playgroud)
类似的问题(在调整大小后保持文本在数据坐标系中旋转?)只使用线性轴,matplotlib 演示也是如此。
对剧情的评论回答评论
- 在我的完整图中,我使用具有该
twinx()功能的双轴(均在对数刻度上)。所有数据都绘制在ax1其上使用 log-10 标度(如图所示)。(我可以更明确地写yscale('log', basey=10)......)。最终我想要一个 base-10 轴。- 制作中使用的模型
y_ols来自对一些原始数据的回归拟合,需要以 2 为基数。在对数尺度上,很容易在任何所需的基数中恢复梯度。
使用 的混合np.gradient 和角度(以弧度为单位)恢复对数刻度上的梯度很容易np.arctan,但我似乎无法恢复接近 10 度(0.17 弧度)的数字。
transData.transform_angles(np.array((np.mean(np.gradient(np.log10(y_ols), np.mean(np.diff(x_fit)))),)), np.array([np.min(x_fit), 1.2*y_ols[0]]).reshape((1, 2)), radians=True)[0]
Run Code Online (Sandbox Code Playgroud)
给出-1.6弧度(大约 -90 度),而我需要一个更接近0.17弧度的数字。也许我应该使用不同的基础,或者我做错了(因此是帖子)。
从代码中可以看出,我在使用1.2*y_ols[0]. 如果解决方案需要考虑到这一点,那就更好了。
请注意,我提供了一个通用类来实现这一点,作为对原始问题的回答。这将在轴限制更改或缩放事件等上更新自身。它也适用于对数刻度。
您首先计算数据坐标中的角度。这可以很容易地用numpy.arctan2前两个数据(或任何其他接近数据对)的差异作为参数来完成。
然后使用ax.transData.transform_angles将数据坐标中给出的角度转换为屏幕坐标中的角度。
下面是在线性和对数尺度上针对同一案例的示例(从另一个答案中获取数据)。
import matplotlib.pyplot as plt
import numpy as np
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6, 4), sharex=True)
ax2.set_yscale('log')
ax2.set(ylim=(1e-11, 1e-1), xlim=(-0.2, 7.2))
x = np.linspace(0.8, 6.2, 100)
y = (lambda x: 10**(( -2 - x)))(x)
# angle in data coordinates
angle_data = np.rad2deg(np.arctan2(y[1]-y[0], x[1]-x[0]))
# Apply the exact same code to linear and log axes
for ax in (ax1, ax2):
ax.plot(x, y, 'b-')
# angle in screen coordinates
angle_screen = ax.transData.transform_angles(np.array((angle_data,)),
np.array([x[0], y[0]]).reshape((1, 2)))[0]
# using `annotate` allows to specify an offset in units of points
ax.annotate("Text", xy=(x[0],y[0]), xytext=(2,2), textcoords="offset points",
rotation_mode='anchor', rotation=angle_screen)
plt.show()
Run Code Online (Sandbox Code Playgroud)