Print string over plotted line (mimic contour plot labels)

后端 未结 2 1219
时光说笑
时光说笑 2020-12-03 11:34

The contour plot demo shows how you can plot the curves with the level value plotted over them, see below.

\"ent

相关标签:
2条回答
  • 2020-12-03 11:56

    Based on Jakob's code, here is a function that rotates the text in data space, puts labels near a given x or y data coordinate, and works also with log plots.

    def label_line(line, label_text, near_i=None, near_x=None, near_y=None, rotation_offset=0, offset=(0,0)):
        """call 
            l, = plt.loglog(x, y)
            label_line(l, "text", near_x=0.32)
        """
        def put_label(i):
            """put label at given index"""
            i = min(i, len(x)-2)
            dx = sx[i+1] - sx[i]
            dy = sy[i+1] - sy[i]
            rotation = np.rad2deg(math.atan2(dy, dx)) + rotation_offset
            pos = [(x[i] + x[i+1])/2. + offset[0], (y[i] + y[i+1])/2 + offset[1]]
            plt.text(pos[0], pos[1], label_text, size=9, rotation=rotation, color = line.get_color(),
            ha="center", va="center", bbox = dict(ec='1',fc='1'))
    
        x = line.get_xdata()
        y = line.get_ydata()
        ax = line.get_axes()
        if ax.get_xscale() == 'log':
            sx = np.log10(x)    # screen space
        else:
            sx = x
        if ax.get_yscale() == 'log':
            sy = np.log10(y)
        else:
            sy = y
    
        # find index
        if near_i is not None:
            i = near_i
            if i < 0: # sanitize negative i
                i = len(x) + i
            put_label(i)
        elif near_x is not None:
            for i in range(len(x)-2):
                if (x[i] < near_x and x[i+1] >= near_x) or (x[i+1] < near_x and x[i] >= near_x):
                    put_label(i)
        elif near_y is not None:
            for i in range(len(y)-2):
                if (y[i] < near_y and y[i+1] >= near_y) or (y[i+1] < near_y and y[i] >= near_y):
                    put_label(i)
        else:
            raise ValueError("Need one of near_i, near_x, near_y")
    
    0 讨论(0)
  • 2020-12-03 12:11

    You could simply add some text (MPL Gallery) like

    import matplotlib.pyplot as plt 
    import numpy as np
    x = [1.81,1.715,1.78,1.613,1.629,1.714,1.62,1.738,1.495,1.669,1.57,1.877,1.385]
    y = [0.924,0.915,0.914,0.91,0.909,0.905,0.905,0.893,0.886,0.881,0.873,0.873,0.844]
    
    # This is the string that should show somewhere over the plotted line.
    line_string = 'name of line'
    
    # plotting
    fig, ax = plt.subplots(1,1)
    l, = ax.plot(x,y)
    pos = [(x[-2]+x[-1])/2., (y[-2]+y[-1])/2.]
    # transform data points to screen space
    xscreen = ax.transData.transform(zip(x[-2::],y[-2::]))
    rot = np.rad2deg(np.arctan2(*np.abs(np.gradient(xscreen)[0][0][::-1])))
    ltex = plt.text(pos[0], pos[1], line_string, size=9, rotation=rot, color = l.get_color(),
         ha="center", va="center",bbox = dict(ec='1',fc='1'))
    
    def updaterot(event):
        """Event to update the rotation of the labels"""
        xs = ax.transData.transform(zip(x[-2::],y[-2::]))
        rot = np.rad2deg(np.arctan2(*np.abs(np.gradient(xs)[0][0][::-1])))
        ltex.set_rotation(rot)
    
    fig.canvas.mpl_connect('button_release_event', updaterot)
    plt.show()
    

    which gives
    enter image description here

    This way you have maximum control.
    Note, the rotation is in degrees and in screen not data space.

    Update:

    As I recently needed automatic label rotations which update on zooming and panning, thus I updated my answer to account for these needs. Now the label rotation is updated on every mouse button release (the draw_event alone was not triggered when zooming). This approach uses matplotlib transformations to link the data and screen space as discussed in this tutorial.

    0 讨论(0)
提交回复
热议问题