Plot curve with blending line colors with matplotlib/pyplot

后端 未结 1 1860
旧巷少年郎
旧巷少年郎 2020-11-29 13:26

I want to start the curve with one color and progressively blend into another color until the end. The following function in my MCVE works, but surely, there has to be a bet

相关标签:
1条回答
  • 2020-11-29 14:10

    Not sure what "better way" refers to. A solution with less code, which would draw faster is the use of a LineCollection together with a colormap.

    A colormap can be defined by two colors and any colors in between are automatically interpolated.

    cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", [(1, 0, 0), (0, 0, 1)])
    

    A LineCollection can be used to plot a lot of lines at once. Being a ScalarMappable it can use a colormap to colorize each line differently according to some array - in this case one may just use the x values for that purpose.

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.collections import LineCollection
    from matplotlib.colors import LinearSegmentedColormap
    
    x = np.linspace(0, 2*np.pi, 100)
    y = np.sin(x)
    
    cmap = LinearSegmentedColormap.from_list("", [(1, 0, 0), (0, 0, 1)])
    
    points = np.array([x, y]).T.reshape(-1,1,2)
    segments = np.concatenate([points[:-1],points[1:]], axis=1)
    
    lc = LineCollection(segments, cmap=cmap, linewidth=10)
    lc.set_array(x)
    plt.gca().add_collection(lc)
    plt.gca().autoscale()
    plt.show()
    

    The drawback of this solution as can be see in the picture is that the individual lines are not well connected.

    So to circumvent this, one may plot those points overlapping, using

    segments = np.concatenate([points[:-2],points[1:-1], points[2:]], axis=1)
    


    In the above the color is linearly interpolated between the two given colors. The plot therefore looks different than the one from the question using some custom interpolation.

    To obtain the same colors as in the question, you may use the same function to create the colors used in the colormap for the LineCollection. If the aim is to simplify this function you may directly calculate the values as the square root of the color difference in the channels.

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.collections import LineCollection
    from matplotlib.colors import LinearSegmentedColormap
    
    x = np.linspace(0, 2*np.pi, 100)
    y = np.sin(x)
    
    def colorlist2(c1, c2, num):
        l = np.linspace(0,1,num)
        a = np.abs(np.array(c1)-np.array(c2))
        m = np.min([c1,c2], axis=0)
        s  = np.sign(np.array(c2)-np.array(c1)).astype(int)
        s[s==0] =1
        r = np.sqrt(np.c_[(l*a[0]+m[0])[::s[0]],(l*a[1]+m[1])[::s[1]],(l*a[2]+m[2])[::s[2]]])
        return r
    
    cmap = LinearSegmentedColormap.from_list("", colorlist2((1, 0, 0), (0, 0, 1),100))
    
    points = np.array([x, y]).T.reshape(-1,1,2)
    segments = np.concatenate([points[:-2],points[1:-1], points[2:]], axis=1)
    
    lc = LineCollection(segments, cmap=cmap, linewidth=10)
    lc.set_array(x)
    plt.gca().add_collection(lc)
    plt.gca().autoscale()
    plt.show()
    

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