记一下这个让我掉头发掉了一下午的matplotlib
小坑。
当使用ax.annotate
或者ax.text
为图片添加文字时,可以传入Text
对象的属性作为参数,因此可以指定rotation=<degree>
来让文字旋转。但是当你的横纵坐标比例差别很大的时候,这个旋转会与我们的预期完全不符。如图所示
图中是一条45度的直线,我们想让注解文字也有45度的旋转,但是如果直接写:
ax.text(1,1, 'text not rotated correctly',rotation=45, rotation_mode='anchor')
结果就是图中那行令人费解的文字。
原因是matplotlib
旋转文字的机制是直接在屏幕显示坐标系中做旋转,而不是在真实数据坐标系中做旋转。也就是说由于横纵坐标比例不统一,实际上是45度的直线在显示中并非45度,而我们直接画一个在屏幕显示的中旋转45度的文本,就没有办法让它和这条直线平行。
解决方案是通过matplotlib
的Transformation
机制,把数据坐标系中的角度转换到屏幕坐标系中来。
提供数据坐标系->显示坐标系映射的是ax.transData
实例,实例的transform_angles
函数提供角度转换,函数的语法如下:
transform_angles(self, angles, pts, radians=False, pushoff=1e-05)
参数如下:
- angles,必选,要转换的角度值。文档上说是(N,) array-like,但是列表是不行的,因为必须支持
shape
属性,所以老老实实传个np.array
进来。 - pts, 必选,角度值的锚点。(N, 2) array-like,同上。
- radians,可选,Boolean。是否是弧度值,默认是
False
,也就是角度值。 - pushoff,可选,float。用来计算转换角度的步长。
本来这个pts
和pushoff
连个参数很让我疑惑,因为转换角度而已,我还要告诉你这个角度在哪个点上?不过想想也就明白了,因为如果你的坐标系是对数坐标系,那么不同位置上同一个数据角度显示出来是不同的。
总之,想要达到正确的效果,只需要转换一下角度:
trans_angle = ax.transData.transform_angles(np.array([45]),np.array([[5,5]])[0]
ax.text(5, 5, 'text rotated correctly', rotation=trans_angle, rotation_mode='anchor')
注意:文字最好在所有图画完之后再添加,因为如果不事先就把xlim
和ylim
用代码写死,图中每添加一个新的artist
就随时有可能让你的xlim
和ylim
发生变化,这样你之前转换过的角度就不再正确了。
参考链接:
Text Rotation Relative To Linematplotlib.org Transformations Tutorialmatplotlib.org