Как построить градиентную цветную линию в matplotlib?

Чтобы сформулировать это в общем виде, я ищу способ соединить несколько точек с помощью градиентной цветной линии с помощью matplotlib, и я нигде не нахожу это. Чтобы быть более точным, я строю двухмерную случайную прогулку с одной цветной линией. Но, поскольку точки имеют соответствующую последовательность, я хотел бы взглянуть на график и посмотреть, куда переместились данные. Градиентная цветная линия поможет. Или линия с постепенно меняющейся прозрачностью.

Я просто пытаюсь улучшить визуализацию своих данных. Посмотрите на это прекрасное изображение, созданное пакетом R. из ggplot2. Я ищу то же самое в matplotlib. Благодарю.

введите описание изображения здесь

7 ответов

Решение

Недавно я ответил на вопрос с похожим запросом ( создав более 20 уникальных цветов легенды с помощью matplotlib). Там я показал, что вы можете отобразить цикл цветов, который вам нужен для нанесения линий на карту цветов. Вы можете использовать ту же процедуру, чтобы получить определенный цвет для каждой пары очков.

Вы должны тщательно выбирать цветовую карту, потому что цветовые переходы вдоль вашей линии могут выглядеть резко, если цветная карта красочная.

Кроме того, вы можете изменить альфа каждого отрезка, в диапазоне от 0 до 1.

В приведенный ниже пример кода включена подпрограмма (highResPoints), чтобы увеличить количество точек, которые имеет ваш случайный ход, потому что, если у вас слишком мало очков, переходы могут показаться радикальными. Этот фрагмент кода был вдохновлен другим недавним ответом, который я предоставил: /questions/34485188/kak-mne-ukazat-strelovidnyij-stil-linii-v-matplotlib/34485189#34485189

import numpy as np
import matplotlib.pyplot as plt

def highResPoints(x,y,factor=10):
    '''
    Take points listed in two vectors and return them at a higher
    resultion. Create at least factor*len(x) new points that include the
    original points and those spaced in between.

    Returns new x and y arrays as a tuple (x,y).
    '''

    # r is the distance spanned between pairs of points
    r = [0]
    for i in range(1,len(x)):
        dx = x[i]-x[i-1]
        dy = y[i]-y[i-1]
        r.append(np.sqrt(dx*dx+dy*dy))
    r = np.array(r)

    # rtot is a cumulative sum of r, it's used to save time
    rtot = []
    for i in range(len(r)):
        rtot.append(r[0:i].sum())
    rtot.append(r.sum())

    dr = rtot[-1]/(NPOINTS*RESFACT-1)
    xmod=[x[0]]
    ymod=[y[0]]
    rPos = 0 # current point on walk along data
    rcount = 1 
    while rPos < r.sum():
        x1,x2 = x[rcount-1],x[rcount]
        y1,y2 = y[rcount-1],y[rcount]
        dpos = rPos-rtot[rcount] 
        theta = np.arctan2((x2-x1),(y2-y1))
        rx = np.sin(theta)*dpos+x1
        ry = np.cos(theta)*dpos+y1
        xmod.append(rx)
        ymod.append(ry)
        rPos+=dr
        while rPos > rtot[rcount+1]:
            rPos = rtot[rcount+1]
            rcount+=1
            if rcount>rtot[-1]:
                break

    return xmod,ymod


#CONSTANTS
NPOINTS = 10
COLOR='blue'
RESFACT=10
MAP='winter' # choose carefully, or color transitions will not appear smoooth

# create random data
np.random.seed(101)
x = np.random.rand(NPOINTS)
y = np.random.rand(NPOINTS)

fig = plt.figure()
ax1 = fig.add_subplot(221) # regular resolution color map
ax2 = fig.add_subplot(222) # regular resolution alpha
ax3 = fig.add_subplot(223) # high resolution color map
ax4 = fig.add_subplot(224) # high resolution alpha

# Choose a color map, loop through the colors, and assign them to the color 
# cycle. You need NPOINTS-1 colors, because you'll plot that many lines 
# between pairs. In other words, your line is not cyclic, so there's 
# no line from end to beginning
cm = plt.get_cmap(MAP)
ax1.set_color_cycle([cm(1.*i/(NPOINTS-1)) for i in range(NPOINTS-1)])
for i in range(NPOINTS-1):
    ax1.plot(x[i:i+2],y[i:i+2])


ax1.text(.05,1.05,'Reg. Res - Color Map')
ax1.set_ylim(0,1.2)

# same approach, but fixed color and 
# alpha is scale from 0 to 1 in NPOINTS steps
for i in range(NPOINTS-1):
    ax2.plot(x[i:i+2],y[i:i+2],alpha=float(i)/(NPOINTS-1),color=COLOR)

ax2.text(.05,1.05,'Reg. Res - alpha')
ax2.set_ylim(0,1.2)

# get higher resolution data
xHiRes,yHiRes = highResPoints(x,y,RESFACT)
npointsHiRes = len(xHiRes)

cm = plt.get_cmap(MAP)

ax3.set_color_cycle([cm(1.*i/(npointsHiRes-1)) 
                     for i in range(npointsHiRes-1)])


for i in range(npointsHiRes-1):
    ax3.plot(xHiRes[i:i+2],yHiRes[i:i+2])

ax3.text(.05,1.05,'Hi Res - Color Map')
ax3.set_ylim(0,1.2)

for i in range(npointsHiRes-1):
    ax4.plot(xHiRes[i:i+2],yHiRes[i:i+2],
             alpha=float(i)/(npointsHiRes-1),
             color=COLOR)
ax4.text(.05,1.05,'High Res - alpha')
ax4.set_ylim(0,1.2)



fig.savefig('gradColorLine.png')
plt.show()

На этом рисунке показаны четыре случая:

Обратите внимание, что если у вас много точек, звоните plt.plot для каждого отрезка может быть довольно медленно. Более эффективно использовать объект LineCollection.

С использованием colorline По рецепту можно сделать следующее:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.collections as mcoll
import matplotlib.path as mpath

def colorline(
    x, y, z=None, cmap=plt.get_cmap('copper'), norm=plt.Normalize(0.0, 1.0),
        linewidth=3, alpha=1.0):
    """
    http://nbviewer.ipython.org/github/dpsanders/matplotlib-examples/blob/master/colorline.ipynb
    http://matplotlib.org/examples/pylab_examples/multicolored_line.html
    Plot a colored line with coordinates x and y
    Optionally specify colors in the array z
    Optionally specify a colormap, a norm function and a line width
    """

    # Default colors equally spaced on [0,1]:
    if z is None:
        z = np.linspace(0.0, 1.0, len(x))

    # Special case if a single number:
    if not hasattr(z, "__iter__"):  # to check for numerical input -- this is a hack
        z = np.array([z])

    z = np.asarray(z)

    segments = make_segments(x, y)
    lc = mcoll.LineCollection(segments, array=z, cmap=cmap, norm=norm,
                              linewidth=linewidth, alpha=alpha)

    ax = plt.gca()
    ax.add_collection(lc)

    return lc


def make_segments(x, y):
    """
    Create list of line segments from x and y coordinates, in the correct format
    for LineCollection: an array of the form numlines x (points per line) x 2 (x
    and y) array
    """

    points = np.array([x, y]).T.reshape(-1, 1, 2)
    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    return segments

N = 10
np.random.seed(101)
x = np.random.rand(N)
y = np.random.rand(N)
fig, ax = plt.subplots()

path = mpath.Path(np.column_stack([x, y]))
verts = path.interpolated(steps=3).vertices
x, y = verts[:, 0], verts[:, 1]
z = np.linspace(0, 1, len(x))
colorline(x, y, z, cmap=plt.get_cmap('jet'), linewidth=2)

plt.show()

Слишком долго для комментария, поэтому просто хотел подтвердить, что LineCollection это намного быстрее, чем цикл for над подсегментами строки.

метод LineCollection намного быстрее в моих руках.

# Setup
x = np.linspace(0,4*np.pi,1000)
y = np.sin(x)
MAP = 'cubehelix'
NPOINTS = len(x)

Мы протестируем итеративное построение на методе LineCollection выше.

%%timeit -n1 -r1
# Using IPython notebook timing magics
fig = plt.figure()
ax1 = fig.add_subplot(111) # regular resolution color map
cm = plt.get_cmap(MAP)
for i in range(10):
    ax1.set_color_cycle([cm(1.*i/(NPOINTS-1)) for i in range(NPOINTS-1)])
    for i in range(NPOINTS-1):
        plt.plot(x[i:i+2],y[i:i+2])

1 loops, best of 1: 13.4 s per loop

%%timeit -n1 -r1 
fig = plt.figure()
ax1 = fig.add_subplot(111) # regular resolution color map
for i in range(10):
    colorline(x,y,cmap='cubehelix', linewidth=1)

1 loops, best of 1: 532 ms per loop

Усиление вашей линии для лучшего цветового градиента, как обеспечивает текущий выбранный ответ, все еще является хорошей идеей, если вы хотите плавный градиент, и у вас есть только несколько точек.

Я добавил свое решение, используя pcolormesh. Каждый отрезок линии рисуется с помощью прямоугольника, который интерполирует цвета на каждом конце. Так что это действительно интерполирует цвет, но мы должны пройти толщину линии.

import numpy as np
import matplotlib.pyplot as plt

def colored_line(x, y, z=None, linewidth=1, MAP='jet'):
    # this uses pcolormesh to make interpolated rectangles
    xl = len(x)
    [xs, ys, zs] = [np.zeros((xl,2)), np.zeros((xl,2)), np.zeros((xl,2))]

    # z is the line length drawn or a list of vals to be plotted
    if z == None:
        z = [0]

    for i in range(xl-1):
        # make a vector to thicken our line points
        dx = x[i+1]-x[i]
        dy = y[i+1]-y[i]
        perp = np.array( [-dy, dx] )
        unit_perp = (perp/np.linalg.norm(perp))*linewidth

        # need to make 4 points for quadrilateral
        xs[i] = [x[i], x[i] + unit_perp[0] ]
        ys[i] = [y[i], y[i] + unit_perp[1] ]
        xs[i+1] = [x[i+1], x[i+1] + unit_perp[0] ]
        ys[i+1] = [y[i+1], y[i+1] + unit_perp[1] ]

        if len(z) == i+1:
            z.append(z[-1] + (dx**2+dy**2)**0.5)     
        # set z values
        zs[i] = [z[i], z[i] ] 
        zs[i+1] = [z[i+1], z[i+1] ]

    fig, ax = plt.subplots()
    cm = plt.get_cmap(MAP)
    ax.pcolormesh(xs, ys, zs, shading='gouraud', cmap=cm)
    plt.axis('scaled')
    plt.show()

# create random data
N = 10
np.random.seed(101)
x = np.random.rand(N)
y = np.random.rand(N)
colored_line(x, y, linewidth = .01)

Основываясь на ответе Янна, я расширил его, чтобы охватить произвольную окраску точек линии. Интерполяция RBG выполняется между одной точкой и следующей вдоль линии. Альфа можно установить отдельно. Мне действительно было нужно это решение для анимации, где часть линии затухает и обновляется динамически, поэтому я дополнительно добавил возможность устанавливать длину и направление затухания. Надеюсь, это кому-то поможет.

См. Прилагаемый пример графика.

import matplotlib.pyplot as plt

import numpy as np
from matplotlib import collections  as mc
from scipy.interpolate import interp1d
from matplotlib.colors import colorConverter

def colored_line_segments(xs,ys,color):
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(len(xs))])        
    segs = []
    seg_colors = []    
    lastColor = [color[0][0],color[0][1],color[0][2]]    
    start = [xs[0],ys[0]]
    end = [xs[0],ys[0]]        
    for x,y,c in zip(xs,ys,color):
        seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip(c,lastColor)])
        lastColor = [c[0],c[1],c[2]]            
        start = [end[0],end[1]]
        end = [x,y]
        segs.append([start,end])
    colors = [(*color,1) for color in seg_colors]
    lc = mc.LineCollection(segs, colors=colors)
    return lc, segs, colors

def segmented_resample(xs,ys,color,n_resample=100):    
    n_points = len(xs)
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(n_points)])   
    n_segs = (n_points-1)*(n_resample-1)        
    xsInterp = np.linspace(0,1,n_resample)
    segs = []
    seg_colors = []
    hiResXs = [xs[0]]
    hiResYs = [ys[0]]
    RGB = color.swapaxes(0,1)
    for i in range(n_points-1):
        fit_xHiRes = interp1d([0,1],xs[i:i+2])
        fit_yHiRes = interp1d(xs[i:i+2],ys[i:i+2])
        
        xHiRes = fit_xHiRes(xsInterp)
        yHiRes = fit_yHiRes(xHiRes)    
        
        hiResXs = hiResXs+list(xHiRes[1:])
        hiResYs = hiResYs+list(yHiRes[1:])
        
        R_HiRes = interp1d([0,1],RGB[0][i:i+2])(xHiRes)        
        G_HiRes = interp1d([0,1],RGB[1][i:i+2])(xHiRes)      
        B_HiRes = interp1d([0,1],RGB[2][i:i+2])(xHiRes)       
                        
        lastColor = [R_HiRes[0],G_HiRes[0],B_HiRes[0]]        
        
        start = [xHiRes[0],yHiRes[0]]
        end = [xHiRes[0],yHiRes[0]]
        
        
        for x,y,r,g,b in zip(xHiRes[1:],yHiRes[1:],R_HiRes[1:],G_HiRes[1:],B_HiRes[1:]):
            seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip((r,g,b),lastColor)])
            lastColor = [r,g,b]            
            start = [end[0],end[1]]
            end = [x,y]
            segs.append([start,end])

    colors = [(*color,1) for color in seg_colors]    
    return segs, colors, [hiResXs,hiResYs]        

def fadeCollection(xs,ys,color,fade_len=20,n_resample=100,direction='Head'):      
    segs, colors, hiResData = segmented_resample(xs,ys,color,n_resample)    
    n_segs = len(segs)   
    if fade_len>len(segs):
        fade_len=n_segs    
    if direction=='Head':
        #Head fade
        alphas = np.concatenate((np.zeros(n_segs-fade_len),np.linspace(0,1,fade_len)))
    else:        
        #Tail fade
        alphas = np.concatenate((np.linspace(1,0,fade_len),np.zeros(n_segs-fade_len)))
    colors = [(*color[:-1],alpha) for color,alpha in zip(colors,alphas)]
    lc = mc.LineCollection(segs, colors=colors)
    return segs, colors, hiResData 

    
if __name__ == "__main__":

    NPOINTS = 10
    RESAMPLE = 10
    N_FADE = int(RESAMPLE*NPOINTS*0.5)
    N_SEGS = (NPOINTS-1)*(RESAMPLE-1)  

    SHOW_POINTS_AXI_12 = True
    SHOW_POINTS_AXI_34 = False

    np.random.seed(11)
    xs = np.random.rand(NPOINTS)
    ys = np.random.rand(NPOINTS)

    COLOR='b'
    MARKER_COLOR = 'k'
    MARKER = '+'

    CMAP = plt.get_cmap('hsv')
    COLORS = np.array([CMAP(i)[:-1] for i in np.linspace(0,1,NPOINTS)])

    fig = plt.figure(figsize=(12,8),dpi=100)
    ax1 = fig.add_subplot(221) # original data
    lc, segs, colors = colored_line_segments(xs,ys,COLORS)
    if SHOW_POINTS_AXI_12: ax1.scatter(xs,ys,marker=MARKER,color=MARKER_COLOR)
    ax1.add_collection(lc)
    ax1.text(.05,1.05,'Original Data')
    ax1.set_ylim(0,1.2)

    ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1) # resampled data
    segs, colors, hiResData   = segmented_resample(xs,ys,COLORS,RESAMPLE)
    if SHOW_POINTS_AXI_12: ax2.scatter(hiResData[0],hiResData[1],marker=MARKER,color=MARKER_COLOR)
    ax2.add_collection(mc.LineCollection(segs, colors=colors))
    ax2.text(.05,1.05,'Original Data - Resampled')
    ax2.set_ylim(0,1.2)

    ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1) # resampled with linear alpha fade start to finish

    segs, colors, hiResData = fadeCollection(xs,ys,COLORS,fade_len=RESAMPLE*NPOINTS,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax3.scatter(hiResData[0],hiResData[1],marker=MARKER,color=MARKER_COLOR)
    ax3.add_collection(mc.LineCollection(segs, colors=colors))
    ax3.text(.05,1.05,'Resampled - w/Full length fade')
    ax3.set_ylim(0,1.2)

    ax4 = fig.add_subplot(224, sharex=ax1, sharey=ax1) # resampled with linear alpha fade N_FADE long
    segs, colors, hiResData = fadeCollection(xs,ys,COLORS,fade_len=N_FADE,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax4.scatter(hiResData[0],hiResData[1],marker=MARKER,color=MARKER_COLOR)
    ax4.add_collection(mc.LineCollection(segs, colors=colors))
    ax4.text(.05,1.05,'Resampled - w/{} point fade'.format(N_FADE))
    ax4.set_ylim(0,1.2)

    fig.savefig('fadeSegmentedColorLine.png')
    plt.show()

UPDATE: The way segment colors would not reproduce the underlying point colors bugged me so I added a flag to change the segment color interpolation to either be mid or forward. Because there are n-1 segments and n points you cant have segment colors perfectly match but now they at least match at one end. This also eliminates the smearing caused by RGB channel averaging as done previously, I suppose in some instances you might want the smoother version so its still there.

import matplotlib.pyplot as plt

import numpy as np
from matplotlib import collections  as mc
from scipy.interpolate import interp1d
from matplotlib.colors import colorConverter

def colored_line_segments(xs,ys,color,mid_colors=False):
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(len(xs))])        
    segs = []
    seg_colors = []    
    lastColor = [color[0][0],color[0][1],color[0][2]]    
    start = [xs[0],ys[0]]
    end = [xs[0],ys[0]]        
    for x,y,c in zip(xs,ys,color):
        if mid_colors:
            seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip(c,lastColor)])        
        else:   
            seg_colors.append(c)        
        lastColor = [c[0],c[1],c[2]]            
        start = [end[0],end[1]]
        end = [x,y]
        segs.append([start,end])
    colors = [(*color,1) for color in seg_colors]
    lc = mc.LineCollection(segs, colors=colors)
    return lc, segs, colors

def segmented_resample(xs,ys,color,n_resample=100,mid_colors=False):    
    n_points = len(xs)
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(n_points)])   
    n_segs = (n_points-1)*(n_resample-1)        
    xsInterp = np.linspace(0,1,n_resample)
    segs = []
    seg_colors = []
    hiResXs = [xs[0]]
    hiResYs = [ys[0]]
    RGB = color.swapaxes(0,1)
    for i in range(n_points-1):
        fit_xHiRes = interp1d([0,1],xs[i:i+2])
        fit_yHiRes = interp1d(xs[i:i+2],ys[i:i+2])
        
        xHiRes = fit_xHiRes(xsInterp)
        yHiRes = fit_yHiRes(xHiRes)    
        
        hiResXs = hiResXs+list(xHiRes[1:])
        hiResYs = hiResYs+list(yHiRes[1:])
        
        R_HiRes = interp1d([0,1],RGB[0][i:i+2])(xHiRes)        
        G_HiRes = interp1d([0,1],RGB[1][i:i+2])(xHiRes)      
        B_HiRes = interp1d([0,1],RGB[2][i:i+2])(xHiRes)       
                        
        lastColor = [R_HiRes[0],G_HiRes[0],B_HiRes[0]]        
        
        start = [xHiRes[0],yHiRes[0]]
        end = [xHiRes[0],yHiRes[0]]
        if mid_colors: seg_colors.append([R_HiRes[0],G_HiRes[0],B_HiRes[0]])
        for x,y,r,g,b in zip(xHiRes[1:],yHiRes[1:],R_HiRes[1:],G_HiRes[1:],B_HiRes[1:]):
            if mid_colors:
                seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip((r,g,b),lastColor)])
            else:            
                seg_colors.append([r,g,b])
            
            lastColor = [r,g,b]            
            start = [end[0],end[1]]
            end = [x,y]
            segs.append([start,end])

    colors = [(*color,1) for color in seg_colors]    
    return segs, colors, [hiResXs,hiResYs]        

def faded_segment_resample(xs,ys,color,fade_len=20,n_resample=100,direction='Head'):      
    segs, colors, hiResData = segmented_resample(xs,ys,color,n_resample)    
    n_segs = len(segs)   
    if fade_len>len(segs):
        fade_len=n_segs    
    if direction=='Head':
        #Head fade
        alphas = np.concatenate((np.zeros(n_segs-fade_len),np.linspace(0,1,fade_len)))
    else:        
        #Tail fade
        alphas = np.concatenate((np.linspace(1,0,fade_len),np.zeros(n_segs-fade_len)))
    colors = [(*color[:-1],alpha) for color,alpha in zip(colors,alphas)]
    lc = mc.LineCollection(segs, colors=colors)
    return segs, colors, hiResData 

    
if __name__ == "__main__":

    NPOINTS = 10
    RESAMPLE = 10
    N_FADE = int(RESAMPLE*NPOINTS*0.5)
    N_SEGS = (NPOINTS-1)*(RESAMPLE-1)  

    SHOW_POINTS_AXI_12 = True
    SHOW_POINTS_AXI_34 = True

    np.random.seed(11)
    xs = np.random.rand(NPOINTS)
    ys = np.random.rand(NPOINTS)

    COLOR='b'

    MARKER = '.'
    #MARKER_COLOR = 'k'
    CMAP = plt.get_cmap('hsv')
    COLORS = np.array([CMAP(i)[:-1] for i in np.linspace(0,1,NPOINTS)])
    MARKER_COLOR = COLORS
    
    N_SCATTER = (NPOINTS-1)*(RESAMPLE-1)+1
    COLORS_LONG = np.array([CMAP(i)[:-1] for i in np.linspace(1/N_SCATTER,1,N_SCATTER)])

    fig = plt.figure(figsize=(12,8),dpi=100)
    ax1 = fig.add_subplot(221) # original data
    lc, segs, colors = colored_line_segments(xs,ys,COLORS,True)
    if SHOW_POINTS_AXI_12: ax1.scatter(xs,ys,marker=MARKER,color=COLORS)
    ax1.add_collection(lc)
    ax1.text(.05,1.05,'Original Data')
    ax1.set_ylim(0,1.2)

    ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1) # resampled data
    segs, colors, hiResData   = segmented_resample(xs,ys,COLORS,RESAMPLE)
    if SHOW_POINTS_AXI_12: ax2.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax2.add_collection(mc.LineCollection(segs, colors=colors))
    ax2.text(.05,1.05,'Original Data - Resampled')
    ax2.set_ylim(0,1.2)

    ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1) # resampled with linear alpha fade start to finish

    segs, colors, hiResData = faded_segment_resample(xs,ys,COLORS,fade_len=RESAMPLE*NPOINTS,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax3.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax3.add_collection(mc.LineCollection(segs, colors=colors))
    ax3.text(.05,1.05,'Resampled - w/Full length fade')
    ax3.set_ylim(0,1.2)

    ax4 = fig.add_subplot(224, sharex=ax1, sharey=ax1) # resampled with linear alpha fade N_FADE long
    segs, colors, hiResData = faded_segment_resample(xs,ys,COLORS,fade_len=N_FADE,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax4.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax4.add_collection(mc.LineCollection(segs, colors=colors))
    ax4.text(.05,1.05,'Resampled - w/{} point fade'.format(N_FADE))
    ax4.set_ylim(0,1.2)

    fig.savefig('fadeSegmentedColorLine.png')
    plt.show()

Update 2: Promise this is the last one..but I extended it to 3d and corrected some errors that were not apparent because the test data being used was within the range 0,1

import numpy as np
from matplotlib.collections import LineCollection as lc
from mpl_toolkits.mplot3d.art3d import Line3DCollection as lc3d

from scipy.interpolate import interp1d
from matplotlib.colors import colorConverter

def colored_line_segments(xs,ys,zs=None,color='k',mid_colors=False):
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(len(xs))])   
    segs = []
    seg_colors = []    
    lastColor = [color[0][0],color[0][1],color[0][2]]        
    start = [xs[0],ys[0]]
    end = [xs[0],ys[0]]        
    if not zs is None:
        start.append(zs[0])
        end.append(zs[0])     
    else:
        zs = [zs]*len(xs)            
    for x,y,z,c in zip(xs,ys,zs,color):
        if mid_colors:
            seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip(c,lastColor)])        
        else:   
            seg_colors.append(c)        
        lastColor = c[:-1]           
        if not z is None:
            start = [end[0],end[1],end[2]]
            end = [x,y,z]
        else:
            start = [end[0],end[1]]
            end = [x,y]                 
        segs.append([start,end])               
    colors = [(*color,1) for color in seg_colors]    
    return segs, colors

def segmented_resample(xs,ys,zs=None,color='k',n_resample=100,mid_colors=False):    
    n_points = len(xs)
    if isinstance(color,str):
        color = colorConverter.to_rgba(color)[:-1]
        color = np.array([color for i in range(n_points)])   
    n_segs = (n_points-1)*(n_resample-1)        
    xsInterp = np.linspace(0,1,n_resample)    
    segs = []
    seg_colors = []
    hiResXs = [xs[0]]
    hiResYs = [ys[0]]    
    if not zs is None:
        hiResZs = [zs[0]]        
    RGB = color.swapaxes(0,1)
    for i in range(n_points-1):        
        fit_xHiRes = interp1d([0,1],xs[i:i+2])
        fit_yHiRes = interp1d([0,1],ys[i:i+2])        
        xHiRes = fit_xHiRes(xsInterp)
        yHiRes = fit_yHiRes(xsInterp)    
        hiResXs = hiResXs+list(xHiRes[1:])
        hiResYs = hiResYs+list(yHiRes[1:])   
        R_HiRes = interp1d([0,1],RGB[0][i:i+2])(xsInterp)        
        G_HiRes = interp1d([0,1],RGB[1][i:i+2])(xsInterp)      
        B_HiRes = interp1d([0,1],RGB[2][i:i+2])(xsInterp)                               
        lastColor = [R_HiRes[0],G_HiRes[0],B_HiRes[0]]                
        start = [xHiRes[0],yHiRes[0]]
        end = [xHiRes[0],yHiRes[0]]           
        if not zs is None:
            fit_zHiRes = interp1d([0,1],zs[i:i+2])             
            zHiRes = fit_zHiRes(xsInterp)             
            hiResZs = hiResZs+list(zHiRes[1:]) 
            start.append(zHiRes[0])
            end.append(zHiRes[0])                
        else:
            zHiRes = [zs]*len(xHiRes) 
            
        if mid_colors: seg_colors.append([R_HiRes[0],G_HiRes[0],B_HiRes[0]])        
        for x,y,z,r,g,b in zip(xHiRes[1:],yHiRes[1:],zHiRes[1:],R_HiRes[1:],G_HiRes[1:],B_HiRes[1:]):
            if mid_colors:
                seg_colors.append([(chan+lastChan)*.5 for chan,lastChan in zip((r,g,b),lastColor)])
            else:            
                seg_colors.append([r,g,b])            
            lastColor = [r,g,b]            
            if not z is None:
                start = [end[0],end[1],end[2]]
                end = [x,y,z]  
            else:
                start = [end[0],end[1]]
                end = [x,y]                
            segs.append([start,end])

    colors = [(*color,1) for color in seg_colors]    
    data = [hiResXs,hiResYs] 
    if not zs is None:
        data = [hiResXs,hiResYs,hiResZs] 
    return segs, colors, data      

def faded_segment_resample(xs,ys,zs=None,color='k',fade_len=20,n_resample=100,direction='Head'):      
    segs, colors, hiResData = segmented_resample(xs,ys,zs,color,n_resample)    
    n_segs = len(segs)   
    if fade_len>len(segs):
        fade_len=n_segs    
    if direction=='Head':
        #Head fade
        alphas = np.concatenate((np.zeros(n_segs-fade_len),np.linspace(0,1,fade_len)))
    else:        
        #Tail fade
        alphas = np.concatenate((np.linspace(1,0,fade_len),np.zeros(n_segs-fade_len)))
    colors = [(*color[:-1],alpha) for color,alpha in zip(colors,alphas)]
    return segs, colors, hiResData 


def test2d():
    NPOINTS = 10
    RESAMPLE = 10
    N_FADE = int(RESAMPLE*NPOINTS*0.5)
    N_SEGS = (NPOINTS-1)*(RESAMPLE-1)  

    SHOW_POINTS_AXI_12 = True
    SHOW_POINTS_AXI_34 = True

    np.random.seed(11)
    xs = np.random.rand(NPOINTS)
    ys = np.random.rand(NPOINTS)
    
    MARKER = '.'
    CMAP = plt.get_cmap('hsv')
    COLORS = np.array([CMAP(i)[:-1] for i in np.linspace(0,1,NPOINTS)])
    MARKER_COLOR = COLORS
    
    N_SCATTER = (NPOINTS-1)*(RESAMPLE-1)+1
    COLORS_LONG = np.array([CMAP(i)[:-1] for i in np.linspace(1/N_SCATTER,1,N_SCATTER)])

    fig = plt.figure(figsize=(12,8),dpi=100)
    ax1 = fig.add_subplot(221) # original data
    segs, colors = colored_line_segments(xs,ys,color=COLORS,mid_colors=True)
    if SHOW_POINTS_AXI_12: ax1.scatter(xs,ys,marker=MARKER,color=COLORS)
    ax1.add_collection(lc(segs, colors=colors))
    ax1.text(.05,1.05,'Original Data')
    ax1.set_ylim(0,1.2)

    ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1) # resampled data
    segs, colors, hiResData   = segmented_resample(xs,ys,color=COLORS,n_resample=RESAMPLE)
    if SHOW_POINTS_AXI_12: ax2.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax2.add_collection(lc(segs, colors=colors))
    ax2.text(.05,1.05,'Original Data - Resampled')
    ax2.set_ylim(0,1.2)

    ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1) # resampled with linear alpha fade start to finish

    segs, colors, hiResData = faded_segment_resample(xs,ys,color=COLORS,fade_len=RESAMPLE*NPOINTS,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax3.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax3.add_collection(lc(segs, colors=colors))
    ax3.text(.05,1.05,'Resampled - w/Full length fade')
    ax3.set_ylim(0,1.2)

    ax4 = fig.add_subplot(224, sharex=ax1, sharey=ax1) # resampled with linear alpha fade N_FADE long
    segs, colors, hiResData = faded_segment_resample(xs,ys,color=COLORS,fade_len=N_FADE,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax4.scatter(hiResData[0],hiResData[1],marker=MARKER,color=COLORS_LONG)
    ax4.add_collection(lc(segs, colors=colors))
    ax4.text(.05,1.05,'Resampled - w/{} point fade'.format(N_FADE))
    ax4.set_ylim(0,1.2)

    fig.savefig('2d_fadeSegmentedColorLine.png')
    plt.show()
    
    
def test3d():
    def set_view(axi):
        axi.set_xlim(-.65,.65)
        axi.set_ylim(-.65,.75)
        axi.set_zlim(-.65,.65)
        axi.view_init(elev=45, azim= 45)
    
    NPOINTS = 40
    RESAMPLE = 2
    N_FADE = int(RESAMPLE*NPOINTS*0.5)
    
    N_FADE = 20
    
    N_SEGS = (NPOINTS-1)*(RESAMPLE-1)  

    SHOW_POINTS_AXI_12 = True
    SHOW_POINTS_AXI_34 = False

    alpha = np.linspace(.5,1.5,NPOINTS)*np.pi
    theta = np.linspace(.25,1.5,NPOINTS)*np.pi
    rad = np.linspace(0,1,NPOINTS)        
    xs = rad*np.sin(theta)*np.cos(alpha)
    ys = rad*np.sin(theta)*np.sin(alpha)
    zs = rad*np.cos(theta)
    
    MARKER = '.'
    CMAP = plt.get_cmap('hsv')
    COLORS = np.array([CMAP(i)[:-1] for i in np.linspace(0,1,NPOINTS)])
    MARKER_COLOR = COLORS
    
    N_SCATTER = (NPOINTS-1)*(RESAMPLE-1)+1
    COLORS_LONG = np.array([CMAP(i)[:-1] for i in np.linspace(1/N_SCATTER,1,N_SCATTER)])

    fig = plt.figure(figsize=(12,8),dpi=100)
    ax1 = fig.add_subplot(221,projection='3d') # original data
    segs, colors = colored_line_segments(xs,ys,zs,color=COLORS,mid_colors=True)
    if SHOW_POINTS_AXI_12: ax1.scatter(xs,ys,zs,marker=MARKER,color=COLORS)
    ax1.add_collection(lc3d(segs, colors=colors))

    ax2 = fig.add_subplot(222, projection='3d', sharex=ax1, sharey=ax1) # resampled data
    segs, colors, hiResData   = segmented_resample(xs,ys,zs,color=COLORS,n_resample=RESAMPLE)
    if SHOW_POINTS_AXI_12: ax2.scatter(hiResData[0],hiResData[1],hiResData[2],marker=MARKER,color=COLORS_LONG)
    ax2.add_collection(lc3d(segs, colors=colors))

    ax3 = fig.add_subplot(223,projection='3d', sharex=ax1, sharey=ax1) # resampled with linear alpha fade start to finish
    segs, colors, hiResData = faded_segment_resample(xs,ys,zs,color=COLORS,fade_len=RESAMPLE*NPOINTS,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax3.scatter(hiResData[0],hiResData[1],hiResData[2],marker=MARKER,color=COLORS_LONG)
    ax3.add_collection(lc3d(segs, colors=colors))

    ax4 = fig.add_subplot(224,projection='3d', sharex=ax1, sharey=ax1) # resampled with linear alpha fade N_FADE long
    segs, colors, hiResData = faded_segment_resample(xs,ys,zs,color=COLORS,fade_len=N_FADE,n_resample=RESAMPLE,direction='Head')
    if SHOW_POINTS_AXI_34: ax4.scatter(hiResData[0],hiResData[1],hiResData[2],marker=MARKER,color=COLORS_LONG)
    ax4.add_collection(lc3d(segs, colors=colors))
    
    labels = ('Original Data',
              'Original Data - Resampled',
              'Resampled - w/Full length fade',
              'Resampled - w/{} point fade'.format(N_FADE) )
                            
    for ax,label in zip((ax1,ax2,ax3,ax4),labels):
        set_view(ax)
        ax.text(.6,-.6,1.55,label)

    fig.savefig('3d_fadeSegmentedColorLine.png')
    plt.show()    
    
if __name__ == "__main__":
    import matplotlib.pyplot as plt
    test2d()
    test3d()

Я использовал код @alexbw для построения параболы. Это работает очень хорошо. Я могу изменить набор цветов для функции. Для вычислений это заняло у меня около 1 минуты и 30 секунд. Я использовал Intel i5, графику 2 ГБ, оперативной памяти 8 ГБ.

Код следующий:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.collections as mcoll
import matplotlib.path as mpath

x = np.arange(-8, 4, 0.01)
y = 1 + 0.5 * x**2

MAP = 'jet'
NPOINTS = len(x)

fig = plt.figure()
ax1 = fig.add_subplot(111) 
cm = plt.get_cmap(MAP)
for i in range(10):
    ax1.set_color_cycle([cm(1.0*i/(NPOINTS-1)) for i in range(NPOINTS-1)])
    for i in range(NPOINTS-1):
        plt.plot(x[i:i+2],y[i:i+2])

plt.title('Inner minimization', fontsize=25)
plt.xlabel(r'Friction torque $[Nm]$', fontsize=25)
plt.ylabel(r'Accelerations energy $[\frac{Nm}{s^2}]$', fontsize=25)
plt.show() # Show the figure

И результат:

Вот довольно короткое решение с использованием sliding_windowрецепт из itertoolsдокументы.

      from collections import deque
from itertools import islice
from matplotlib import collections as mc
from matplotlib.colors import colorConverter
import numpy as np

def sliding_window(iterable, n):
  """
  sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
  
  recipe from python docs
  """
  it = iter(iterable)
  window = deque(islice(it, n), maxlen=n)
  if len(window) == n:
      yield tuple(window)
  for x in it:
      window.append(x)
      yield tuple(window)

def color_gradient(x, y, c1, c2):
  """
  Creates a line collection with a gradient from colors c1 to c2,
  from data x and y.
  """
  n = len(x)
  if len(y) != n:
    raise ValueError('x and y data lengths differ')
  return mc.LineCollection(sliding_window(zip(x, y), 2),
                           colors=np.linspace(colorConverter.to_rgb(c1),
                                              colorConverter.to_rgb(c2), n - 1))

Этот только линейно интерполирует значения RGB между двумя цветами (c1иc2). Их можно указать так же, как вы обычно делаете с matplotlib, и функция преобразует их в RGB, используяcolorConverterа затем с помощьюnp.linspaceинтерполировать. Каждый сегмент будет одного цвета, так что лучше всего будет, если у вас много маленьких сегментов. Я использовал ответ Dizzixx в качестве справки, но сильно упростил его. Сработало для моих целей, что тоже было случайным блужданием.

Пример использования:

      plt.gca().add_collection(color_gradient(x_data, y_data,
                                        'black', 'red'))
Другие вопросы по тегам