How to plot a gradient color line in matplotlib?
为了以一般的形式陈述它,我正在寻找一种使用matplotlib将具有渐变色线的多个点连接起来的方法,但是在任何地方都找不到。
更具体地说,我正在绘制一条带有一条色线的2D随机游动图。 但是,由于这些点具有相关的顺序,因此我想查看该图并查看数据已移至何处。 渐变色线可以解决问题。 或透明度逐渐变化的线。
我只是想改善数据的虚拟化。 看看R的ggplot2软件包产生的这张精美图片。我正在matplotlib中寻找相同的图片。 谢谢。
我最近以类似的要求回答了一个问题(使用matplotlib创建了20多种独特的图例颜色)。我在那里展示了您可以映射将线条绘制到颜色图所需的颜色循环。您可以使用相同的过程为每对点获取特定的颜色。
您应该仔细选择颜色图,因为如果颜色图是彩色的,则沿线的颜色过渡可能会显得很剧烈。
或者,您可以更改每个线段的Alpha,范围从0到1。
下面的代码示例中包含一个例程(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 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() |
此图显示了四种情况:
请注意,如果您有很多点,则对每个线段调用
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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方法在我手中要快得多。
1 2 3 4 5 | # Setup x = np.linspace(0,4*np.pi,1000) y = np.sin(x) MAP = 'cubehelix' NPOINTS = len(x) |
我们将针对上面的LineCollection方法测试迭代绘图。
1 2 3 4 5 6 7 8 9 | %%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 2 3 4 5 | %%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) |
如果您想要平滑的渐变并且只有几个点,那么按照当前选择的答案提供的方法,对线进行向上采样以获得更好的颜色渐变仍然是一个好主意。
我已经使用pcolormesh添加了解决方案
每个线段都使用一个矩形绘制,该矩形在两端的颜色之间进行插值。因此它确实是在插值颜色,但是我们必须传递线条的粗细。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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) |
我正在使用@alexbw代码来绘制抛物线。效果很好。我可以更改功能的颜色集。为了进行计算,我花了大约1分钟30秒。我使用的是Intel i5,图形2GB,8GB内存。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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 |
结果是:
https://i.stack.imgur.com/gL9DG.png