【问题标题】:How to annotate text along curved lines in Python?如何在 Python 中沿曲线注释文本?
【发布时间】:2015-06-28 05:02:09
【问题描述】:

我正在尝试注释图中的文本,以便它们遵循线条的曲率。我有以下情节:

这就是我想要获得的,如果我为注释固定一个特定的 y 值,对于每条曲线,它应该将注释沿着曲线放置在所需的斜率处(即它应该遵循曲线的曲率)为如下图:

没有注释的绘图的可重现代码是:

import numpy as np
import matplotlib.pyplot as plt

x = np.array([[53.4, 57.6, 65.6, 72.9],
            [60.8, 66.5, 73.1, 83.3],
            [72.8, 80.3, 87.2, 99.3],
            [90.2, 99.7, 109.1, 121.9],
            [113.6, 125.6, 139.8, 152]])

y = np.array([[5.7, 6.4, 7.2, 7.8],
            [5.9, 6.5, 7.2, 7.9],
            [6.0, 6.7, 7.3, 8.0],
            [6.3, 7.0, 7.6, 8.2],
            [6.7, 7.5, 8.2, 8.7]])

plt.figure(figsize=(5.15, 5.15))
plt.subplot(111)
for i in range(len(x)):
    plt.plot(x[i, :] ,y[i, :])
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

如何使用 matplotlib 在 Python 中放置此类文本?

【问题讨论】:

    标签: python matplotlib graph plot annotations


    【解决方案1】:

    抱歉有错别字..(如果有的话) 这是我的答案...这是对@paddyg's 答案的轻微调整,是的,在显示时用鼠标调整图表大小会导致对齐问题。

    您将不得不调整偏移量和图表尺寸

    输出:

    图片 1 有窗口 size(10,5),offset(0,1)

    图片 2 有窗口 size(10,5),offset(-4,0)

    import matplotlib.pyplot as plt
    import math
    
    # I've used 3 plots here:
    x_range = [i for i in range(0,101)]
    y1_range = [i/1 for i in range(0,101)]
    y2_range = [i/2 for i in range(0,101)]
    y3_range = [i/3 for i in range(0,101)]
    
    # Size of the ouput figure
    x_fig = 10
    y_fig = 5
    plt.figure(figsize=(x_fig, y_fig))
    # The height offset form the midpoint of the line segments
    offset = [0,1]
    
    def angle(rng,point,x_fig,y_fig):
        '''
        This will return the alignment angle
        '''
        # print("rng = ", rng)
        x_scale = x_fig /(rng[1] - rng[0])
        y_scale = y_fig /(rng[3] - rng[2])
        assert len(point) == 2, 'point is not properly defined'
        x = point[0]
        y = point[1]
        rot = ((math.atan2( y*y_scale , x*x_scale ))*180)/math.pi
        print(rot,point)
        return rot
    
    def center_point(start,end,height_offset):
        '''
        This will return the location of text placement
        '''
        x_mid = (end[0]-start[0])/2
        y_mid = (end[1]-start[1])/2
        return[x_mid+height_offset[0],y_mid+height_offset[1]]
    
    # Plotting graphs...
    plt.plot(x_range,y1_range,color='pink',label = 'y1 - graph')
    plt.plot(x_range,y2_range,color='blue',label = 'y2 - graph')
    plt.plot(x_range,y3_range,color='red',label =  'y3 - graph')
    
    rng = plt.axis() # This gives the axes range
    rot_y1 = angle(rng,[x_range[-1],y1_range[-1]],x_fig,y_fig)
    pos_y1 = center_point([x_range[0],y1_range[0]],[x_range[-1],y1_range[-1]],offset)
    
    rot_y2 = angle(rng,[x_range[-1],y2_range[-1]],x_fig,y_fig)
    pos_y2 = center_point([x_range[0],y2_range[0]],[x_range[-1],y2_range[-1]],offset)
    
    rot_y3 = angle(rng,[x_range[-1],y3_range[-1]],x_fig,y_fig)
    pos_y3 = center_point([x_range[0],y3_range[0]],[x_range[-1],y3_range[-1]],offset)
    
    # Annotating them...(Of course this can be done in a loop as @paddyg's answer suggets.)
    plt.annotate('Y1 is here', xy =(0, 0),
                    xytext =(pos_y1[0], pos_y1[1]), 
                    rotation=rot_y1)
    plt.annotate('Y2 is here', xy =(0, 0),
                    xytext =(pos_y2[0], pos_y2[1]), 
                    rotation=rot_y2)
    plt.annotate('Y3 is here', xy =(0, 0),
                    xytext =(pos_y3[0], pos_y3[1]),
                    rotation=rot_y3)
    
    
    plt.legend()
    plt.xlabel('X - Values')
    plt.ylabel('Y - Values')
    plt.title('Align the names correctly!')
    
    
    plt.savefig('demo.png', transparent=True) # Do you want to save a backgroundless .png of the graph?
    plt.show()
    

    【讨论】:

      【解决方案2】:

      您可以获取以度为单位的渐变,并在 matplotlib.text.Text 中使用旋转参数

      rotn = np.degrees(np.arctan2(y[:,1:]-y[:,:-1], x[:,1:]-x[:,:-1]))
      

      编辑:所以它比我建议的要复杂一些,因为绘图区域被缩放以匹配数据并具有边距等,但你明白了

      ...
      plt.figure(figsize=(7.15, 5.15)) #NB I've changed the x size to check it didn't distort
      plt.subplot(111)
      for i in range(len(x)):
          plt.plot(x[i, :] ,y[i, :])
      
      rng = plt.axis()
      x_scale = 7.15 * 0.78 / (rng[1] - rng[0])         
      y_scale = 5.15 * 0.80 / (rng[3] - rng[2])          
      rotn = np.degrees(np.arctan2((y[:,1:]-y[:,:-1]) * y_scale,
                                    x[:,1:]-x[:,:-1]) * x_scale)
      labls = ['first', 'second', 'third', 'fourth', 'fifth']
      for i in range(len(x)):
          plt.annotate(labls[i], xy=(x[i,2], y[i,2]), rotation=rotn[i,2])
      
      plt.xlabel('X')
      

      RE-EDIT 注意到缩放错误,但只是巧合!由于缩放,标签的 xy 值也有点近似。

      【讨论】:

      • 您是如何获得 7.15、5.15 和 1.5 的值的?
      • 好点。我对复制粘贴我测试过的代码很吝啬!我现在将重新编辑以添加缺少的位。减去 1.5 是得到实际图形面积的近似值。 matplotlib 中可能有一种方法可以为您完成。或者,它可能会使用比例,在这种情况下你可以 * 0.7 或其他
      • PS 刚刚检查了 matplotlib,子图的边距似乎为 0.12l、0.9r、0.1b、0.9t,使用 7.15*0.78 和 5.15*0.8 似乎效果很好
      猜你喜欢
      • 1970-01-01
      • 2012-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-18
      • 2014-07-19
      • 1970-01-01
      相关资源
      最近更新 更多