Matplotlib:如何以图元单位而非数据单位设置箭头大小?
在Matplotlib中用图元单位指定箭头头部大小的解决方案
问题根源
Matplotlib里箭头的width、head_width这类参数默认是数据单位,所以子图轴范围、纵横比不一样时,箭头外观就会乱——要么大小不一,要么换个方向就变形。你之前用set_data改宽度的路子走不通,因为Arrow的set_data是用来改位置数据的,根本不是调尺寸的,直接调用肯定会搞乱箭头形状。
两种可行解决办法
办法1:直接用Axes坐标创建箭头
把箭头的起点、终点、尺寸都绑定到Axes单位(比如0.01代表子图宽度的1%),不管子图数据范围怎么变,箭头大小都是相对于子图的比例,外观自然一致:
import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4)) # 子图1:X轴0-1 ax1.set_xlim(0, 1) ax1.set_ylim(0, 1) # 用Axes坐标画箭头:起点(0.2,0.5),终点(0.8,0.5),宽度是子图宽度的1% ax1.arrow(0.2, 0.5, 0.6, 0, width=0.01, head_width=0.03, head_length=0.04, transform=ax1.transAxes) # 子图2:X轴0-10 ax2.set_xlim(0, 10) ax2.set_ylim(0, 1) # 完全复用上面的参数,箭头和子图1一模一样 ax2.arrow(0.2, 0.5, 0.6, 0, width=0.01, head_width=0.03, head_length=0.04, transform=ax2.transAxes) plt.tight_layout() plt.show()
办法2:把已有的箭头转成Axes单位
如果已经用数据单位画了箭头,可以通过坐标转换把尺寸改成基于Axes的比例:
import matplotlib.pyplot as plt def fix_arrow_size(ax, arrow): # 获取数据坐标转Axes坐标的变换规则 data2axes = ax.transAxes.inverted() # 拿到箭头的原始数据坐标 x0, y0 = arrow.get_xy() # 把尺寸转成Axes比例,再转回数据单位 ax_width = 0.01 # 目标宽度:子图宽度的1% ax_head_width = 0.03 ax_head_length = 0.04 # Axes单位转数据单位 data_width = (ax.transAxes.transform((ax_width, 0)) - ax.transAxes.transform((0, 0)))[0] data_head_width = (ax.transAxes.transform((ax_head_width, 0)) - ax.transAxes.transform((0, 0)))[0] data_head_length = (ax.transAxes.transform((0, ax_head_length)) - ax.transAxes.transform((0, 0)))[1] # 更新箭头参数 arrow.set_width(data_width) arrow.set_head_width(data_head_width) arrow.set_head_length(data_head_length) # 测试代码 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4)) ax1.set_xlim(0,1) ax1.set_ylim(0,1) arrow1 = ax1.arrow(0.2,0.5,0.6,0, width=0.05) ax2.set_xlim(0,10) ax2.set_ylim(0,1) arrow2 = ax2.arrow(2,0.5,6,0, width=0.5) # 调整箭头尺寸 fix_arrow_size(ax1, arrow1) fix_arrow_size(ax2, arrow2) plt.tight_layout() plt.show()
重要提醒
- 别再用
set_data调箭头宽度:这个方法是用来改箭头位置的,不是尺寸,乱调用必出问题。 - 优先选办法1:直接用
transform=ax.transAxes画箭头,一步到位,不用后续转换,还能自动适配子图纵横比,不会因为箭头方向改变而变形。
内容的提问来源于stack exchange,提问作者keflavich
相关产品推荐
相关产品推荐

