Matplotlib.pyplot.annotate中offset points参数出现异常结果求助
问题:Matplotlib标注箭头长度与方向异常
我使用以下代码生成一幅包含360度范围内24个“小时”标记的正弦曲线图表。每个“小时”都添加了标注,但箭头长度逐渐缩短,甚至方向也不正确。X轴范围为360度,Y轴范围为70度。print语句验证了6点和18点的箭头参数一致且应为垂直方向,但实际图表显示并非如此。使用的Matplotlib版本为3.9.2,Numpy版本为2.1.1。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt arrow_style = {'arrowstyle': '->'} def make_plot(): fig, ax = plt.subplots() ax.axis([0,+360,-35,+35]) ax.set(xlabel = 'X (degrees)', ylabel = 'Y (degrees)', title='Vanishing arrow length example') degree = np.pi / 180.0 # degrees to radians arrow_angle = 72.0 # degrees arrow_length = 27.0 eps = 23.43927945 eps_x = np.linspace(0, 360, 200) eps_y = -eps * np.sin(2 * np.pi * eps_x / 360) ax.plot(eps_x, eps_y, 'r') hr_x = np.linspace(0, 360, 25) hr_y = -eps * np.sin(2 * np.pi * hr_x / 360) i = 0 for x in hr_x: ax.plot(hr_x[i], hr_y[i],'bo') if hr_y[i] > 0 and arrow_angle > 0: arrow_angle = -75 # degrees arrow_x = np.cos(arrow_angle*degree) * arrow_length arrow_y = np.sin(arrow_angle*degree) * arrow_length ax.annotate(str(i), xy=(hr_x[i], hr_y[i]), xytext=(arrow_x, arrow_y), \ xycoords='data', textcoords='offset points', arrowprops=arrow_style) print("arrow_x {:.2f} arrow_y {:.2f} arrow_angle {:.1f} at {} hours" \ .format(arrow_x, arrow_y, arrow_angle, i)) if hr_y[i] <= 0: arrow_angle += 3.0 if hr_y[i] > 0: arrow_angle -= 3.0 i += 1 ax.grid() ax.axhline(0.0) ax.axvline(180.0) plt.show() return fig make_plot().savefig('vanishing_arrow_length.png')
问题原因与修复
核心错误
- 坐标系统混淆:
textcoords='offset points'表示文本位置是相对于xy点的屏幕像素偏移,而你计算的arrow_x/arrow_y是基于数据坐标的偏移,两者单位不匹配,导致箭头长度随图表缩放或坐标范围变化而失真。 - 文本位置设置错误:
xytext=(arrow_x, arrow_y)被设置为绝对屏幕点坐标,而非相对于标注点的偏移,导致箭头方向完全错误。
修改后的代码
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import numpy as np import matplotlib.pyplot as plt arrow_style = {'arrowstyle': '->'} def make_plot(): fig, ax = plt.subplots() ax.axis([0,+360,-35,+35]) ax.set(xlabel = 'X (degrees)', ylabel = 'Y (degrees)', title='Fixed arrow length example') degree = np.pi / 180.0 # degrees to radians arrow_angle = 72.0 # degrees arrow_length = 27.0 eps = 23.43927945 eps_x = np.linspace(0, 360, 200) eps_y = -eps * np.sin(2 * np.pi * eps_x / 360) ax.plot(eps_x, eps_y, 'r') hr_x = np.linspace(0, 360, 25) hr_y = -eps * np.sin(2 * np.pi * hr_x / 360) i = 0 for x in hr_x: ax.plot(hr_x[i], hr_y[i],'bo') # 修正角度切换逻辑:确保在曲线上下方时箭头方向正确 if hr_y[i] > 0 and arrow_angle > 0: arrow_angle = -75 elif hr_y[i] <= 0 and arrow_angle < 0: arrow_angle = 72 # 计算基于数据坐标的箭头偏移 arrow_x = np.cos(arrow_angle * degree) * arrow_length arrow_y = np.sin(arrow_angle * degree) * arrow_length # 文本位置为标注点加上偏移量,使用data坐标系统 text_x = hr_x[i] + arrow_x text_y = hr_y[i] + arrow_y ax.annotate(str(i), xy=(hr_x[i], hr_y[i]), xytext=(text_x, text_y), xycoords='data', textcoords='data', arrowprops=arrow_style) print("arrow_x {:.2f} arrow_y {:.2f} arrow_angle {:.1f} at {} hours" \ .format(arrow_x, arrow_y, arrow_angle, i)) # 逐步调整箭头角度 if hr_y[i] <= 0: arrow_angle += 3.0 else: arrow_angle -= 3.0 i += 1 ax.grid() ax.axhline(0.0) ax.axvline(180.0) plt.show() return fig make_plot().savefig('fixed_arrow_length.png')
关键修改说明
- 将
textcoords改为'data',使文本位置和标注点使用同一数据坐标系统,确保箭头长度和方向不受图表缩放影响。 - 调整
xytext的计算方式:text_x = hr_x[i] + arrow_x,让文本位置是标注点加上箭头方向的偏移量,而非绝对坐标。 - 优化角度切换逻辑,避免在曲线上下方切换时出现角度突变,确保箭头方向平滑过渡。
修改后,6点和18点的箭头会正确显示为垂直方向,所有箭头长度保持一致,方向符合预期。
内容的提问来源于stack exchange,提问作者Aendie
相关产品推荐
相关产品推荐

