Matplotlib - 在对数刻度上旋转文本,其中角度不正确四舍五入 [英] Matplotlib - rotating text on log scale where angles are incorrectly rounded

查看:91
本文介绍了Matplotlib - 在对数刻度上旋转文本,其中角度不正确四舍五入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将文本旋转到以对数刻度显示的图上.当我计算角度时(基于

 将matplotlib导入为mplrc_fonts = {text.usetex":对,'text.latex.preview':是的,font.size":50,'mathtext.default': '常规','axes.titlesize': 55,"axes.labelsize":55,"legend.fontsize":50,"xtick.labelsize":50,ytick.labelsize":50,'figure.titlesize': 55,'figure.figsize':(10,6.5),#15,9.3'text.latex.preamble':[r"""\usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}"""],font.family":衬线",font.serif":计算机现代罗马",}mpl.rcParams.update(rc_fonts)导入 matplotlib.pylab 作为 plt从mpl_toolkits.axes_grid1.inset_locator导入inset_axes,InsetPosition,mark_inset将numpy导入为npx = np.linspace(0,20,100)y = np.exp(x**2)g = 2 * x * y#渐变.lg = 2 * x#对数刻度上的渐变.plt.clf()plt.plot(x, y)plt.yscale('log')对于 [0,2,4,7,18] 中的 x:angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x ** 2),1))y = np.exp(x ** 2)角度屏幕 = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizo​​ntalalignment='center')plt.ylim(1e0, 1e180)plt.xlim(-1, 20)plt.xlabel(r'$x$')plt.title(r'$\exp(x^2)$', y=1.05)plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')

一些想法?

我试图利用这样的事实:对于很大的函数,我可以使用arctan(x)〜pi/2-arctan(1/x)计算90度的差,而前一个角度使用低角度近似,因此仅为1/x.但是,将其插入 transform_angles 后,该值将四舍五入.

解决方案的一个小技巧

如果我猜测图形的纵横比 (c0.6),然后还调整比例的差异(x in [0:20] 而 log10(y) 在 [0:180] ,在比例上产生9的差异),那么我可以得到以下内容,尽管我认为这不是特别可持续的,尤其是如果我稍后要进行调整的话.

# 9 来自 x 在 [0:20] 中的事实,log10(y) 在 [0, 180] 中.系数 0.6 大致是主绘图形状的纵横比.plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizo​​ntalalignment='center')

解决方案

我更新了

I am trying to have text rotate onto a plot which is shown on log scale. When I compute the angles (based on the solution in this answer) the angles are getting incorrectly rounded to 0 or 90 degrees. This is because the angles are computed on a linear scale first, and then transformed. This calculation in linear space is the cause of the trouble. Even in a situation where I know the gradient, (either in a linear or logarithmic scale), I am not sure how I can put this onto the graph correctly.

MWE

import matplotlib as mpl

rc_fonts = {
    "text.usetex": True,
    'text.latex.preview': True,
    "font.size": 50,
    'mathtext.default': 'regular',
    'axes.titlesize': 55,
    "axes.labelsize": 55,
    "legend.fontsize": 50,
    "xtick.labelsize": 50,
    "ytick.labelsize": 50,
    'figure.titlesize': 55,
    'figure.figsize': (10, 6.5),  # 15, 9.3
    'text.latex.preamble': [
        r"""\usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
        """],
    "font.family": "serif",
    "font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
import numpy as np


x = np.linspace(0, 20, 100)
y = np.exp(x**2)
g = 2*x*y  # Gradient.
lg = 2 * x  # Gradient on a log scale.

plt.clf()
plt.plot(x, y)
plt.yscale('log')
for x in [0,2,4,7,18]:
    angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x**2), 1))
    y = np.exp(x**2)
    angle_screen = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]
    plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizontalalignment='center')
plt.ylim(1e0, 1e180)
plt.xlim(-1, 20)
plt.xlabel(r'$x$')
plt.title(r'$\exp(x^2)$', y=1.05)
plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')

A few ideas?

I had tried to use the fact that for very large functions I can calculate the difference from 90 degrees using arctan(x) ~ pi/2 - arctan(1/x), and the former angle uses the low angle approximation so is just 1/x. However, after plugging this into transform_angles this is rounded incorrectly.

A slight hack of a solution

If I guess the aspect ratio of the figure (c0.6) and then also adjust for the difference in scales (x in [0:20] while log10(y) is in [0:180], giving a difference of 9 in scale), then I can get the following, although I don't think this is particularly sustainable, especially if I want to tweak something later.

# The 9 comes from tha fact that x is in [0:20], log10(y) is in [0, 180]. The factor of 0.6 is roughly the aspect ratio of the main plot shape.
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizontalalignment='center')

解决方案

I updated the solution to the original question with a class RotationAwareAnnotation2, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.

This this case it would look as follows.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms


class RotationAwareAnnotation2(mtext.Annotation):
    def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
        self.ax = ax or plt.gca()
        self.p = p
        if not pa:
            self.pa = xy
        kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
        mtext.Annotation.__init__(self, s, xy, **kwargs)
        self.set_transform(mtransforms.IdentityTransform())
        if 'clip_on' in kwargs:
            self.set_clip_path(self.ax.patch)
        self.ax._add_text(self)

    def calc_angle(self):
        p = self.ax.transData.transform_point(self.p)
        pa = self.ax.transData.transform_point(self.pa)
        ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
        return np.rad2deg(ang)

    def _get_rotation(self):
        return self.calc_angle()

    def _set_rotation(self, rotation):
        pass

    _rotation = property(_get_rotation, _set_rotation)


x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')

annots= []
for xi in [0,2,4,7,18]:
    an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
                                  xytext=(-1,1), textcoords="offset points", 
                                  ha="center", va="baseline", fontsize=40)
    annots.append(an)

ax.set_title(r'$\exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')

plt.show()

这篇关于Matplotlib - 在对数刻度上旋转文本,其中角度不正确四舍五入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆