En matplotlib, ¿cómo puedo trazar una línea de varios colores, como un arco iris?

Me gustaría trazar líneas paralelas con diferentes colores. Por ejemplo, en lugar de una sola línea roja de grosor 6, me gustaría tener dos líneas paralelas de grosor 3, una roja y una azul. Cualquier pensamiento sería apreciado.
Merci

Incluso con la compensación inteligente (s. Abajo), todavía hay un problema en una vista que tiene angularjs agudos entre puntos consecutivos.

Vista ampliada de la compensación inteligente: problema con ángulos agudos en línea

Líneas de superposición de espesor variable: la funcionalidad de ancho de línea incorporada clasifica esto automáticamente, de alguna manera

Trazar líneas paralelas no es una tarea fácil. El uso de un simple desplazamiento uniforme, por supuesto, no mostrará el resultado deseado. Esto se muestra en la imagen de la izquierda a continuación.
Tal desplazamiento simple se puede producir en matplotlib como se muestra en el tutorial de transformación .

introduzca la descripción de la imagen aquí

Método 1

Una mejor solución puede ser usar la idea esbozada en el lado derecho. Para calcular el desplazamiento del punto n podemos usar el vector normal para la línea entre el punto n-1 y el punto n+1 y usar la misma distancia a lo largo de este vector normal para calcular el punto de desplazamiento.

La ventaja de este método es que tenemos el mismo número de puntos en la línea original que en la línea de desplazamiento. La desventaja es que no es completamente exacto, como se puede ver en la imagen.

Este método se implementa en la función offset en el código siguiente.
Para hacer esto útil para un gráfico de matplotlib, debemos considerar que el ancho de línea debe ser independiente de las unidades de datos. El ancho de línea se suele dar en unidades de puntos, y el desplazamiento se daría mejor en la misma unidad, de modo que, por ejemplo, se pueda cumplir el requisito de la pregunta (“dos líneas paralelas de ancho 3”). Por lo tanto, la idea es transformar las coordenadas de los datos para mostrar las coordenadas, utilizando ax.transData.transform . Además, el desplazamiento en puntos o se puede transformar en las mismas unidades: al usar ppp y el estándar de ppi = 72, el desplazamiento en las coordenadas de la pantalla es o*dpi/ppi . Después de que se haya aplicado el desplazamiento en las coordenadas de la pantalla, la transformación inversa ( ax.transData.inverted().transform ) permite una ax.transData.inverted().transform inversa.

Ahora hay otra dimensión del problema: ¿Cómo asegurar que el desplazamiento siga siendo el mismo independientemente del zoom y el tamaño de la figura? Este último punto puede abordarse recalculando el desplazamiento cada vez que se realiza un zoom de evento de cambio de tamaño.

Aquí es cómo se vería una curva de arco iris producida por este método.

introduzca la descripción de la imagen aquí

Y aquí está el código para producir la imagen.

 import numpy as np import matplotlib.pyplot as plt dpi = 100 def offset(x,y, o): """ Offset coordinates given by array x,y by o """ X = np.c_[x,y].T m = np.array([[0,-1],[1,0]]) R = np.zeros_like(X) S = X[:,2:]-X[:,:-2] R[:,1:-1] = np.dot(m, S) R[:,0] = np.dot(m, X[:,1]-X[:,0]) R[:,-1] = np.dot(m, X[:,-1]-X[:,-2]) On = R/np.sqrt(R[0,:]**2+R[1,:]**2)*o Out = On+X return Out[0,:], Out[1,:] def offset_curve(ax, x,y, o): """ Offset array x,y in data coordinates by o in points """ trans = ax.transData.transform inv = ax.transData.inverted().transform X = np.c_[x,y] Xt = trans(X) xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72. ) Xto = np.c_[xto, yto] Xo = inv(Xto) return Xo[:,0], Xo[:,1] # some single points y = np.array([1,2,2,3,3,0]) x = np.arange(len(y)) #or try a sinus x = np.linspace(0,9) y=np.sin(x)*x/3. fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi) cols = ["#fff40b", "#00e103", "#ff9921", "#3a00ef", "#ff2121", "#af00e7"] lw = 2. lines = [] for i in range(len(cols)): l, = plt.plot(x,y, lw=lw, color=cols[i]) lines.append(l) def plot_rainbow(event=None): xr = range(6); yr = range(6); xr[0],yr[0] = offset_curve(ax, x,y, lw/2.) xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.) xr[2],yr[2] = offset_curve(ax, xr[0],yr[0], lw) xr[3],yr[3] = offset_curve(ax, xr[1],yr[1], -lw) xr[4],yr[4] = offset_curve(ax, xr[2],yr[2], lw) xr[5],yr[5] = offset_curve(ax, xr[3],yr[3], -lw) for i in range(6): lines[i].set_data(xr[i], yr[i]) plot_rainbow() fig.canvas.mpl_connect("resize_event", plot_rainbow) fig.canvas.mpl_connect("button_release_event", plot_rainbow) plt.savefig(__file__+".png", dpi=dpi) plt.show() 

Método 2

Para evitar líneas superpuestas, uno tiene que usar una solución más complicada. Primero se podría compensar cada punto normal a los dos segmentos de línea de los que forma parte (puntos verdes en la imagen de abajo). Luego calcula la línea a través de esos puntos de compensación y encuentra su intersección. introduzca la descripción de la imagen aquí

Un caso particular sería cuando las pendientes de dos segmentos de línea subsiguientes son iguales. Esto tiene que ser cuidado ( eps en el código de abajo).

 from __future__ import division import numpy as np import matplotlib.pyplot as plt dpi = 100 def intersect(p1, p2, q1, q2, eps=1.e-10): """ given two lines, first through points pn, second through qn, find the intersection """ x1 = p1[0]; y1 = p1[1]; x2 = p2[0]; y2 = p2[1] x3 = q1[0]; y3 = q1[1]; x4 = q2[0]; y4 = q2[1] nomX = ((x1*y2-y1*x2)*(x3-x4)- (x1-x2)*(x3*y4-y3*x4)) denom = float( (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) ) nomY = (x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4) if np.abs(denom) < eps: #print "intersection undefined", p1 return np.array( p1 ) else: return np.array( [ nomX/denom , nomY/denom ]) def offset(x,y, o, eps=1.e-10): """ Offset coordinates given by array x,y by o """ X = np.c_[x,y].T m = np.array([[0,-1],[1,0]]) S = X[:,1:]-X[:,:-1] R = np.dot(m, S) norm = np.sqrt(R[0,:]**2+R[1,:]**2) / o On = R/norm Outa = On+X[:,1:] Outb = On+X[:,:-1] G = np.zeros_like(X) for i in xrange(0, len(X[0,:])-2): p = intersect(Outa[:,i], Outb[:,i], Outa[:,i+1], Outb[:,i+1], eps=eps) G[:,i+1] = p G[:,0] = Outb[:,0] G[:,-1] = Outa[:,-1] return G[0,:], G[1,:] def offset_curve(ax, x,y, o, eps=1.e-10): """ Offset array x,y in data coordinates by o in points """ trans = ax.transData.transform inv = ax.transData.inverted().transform X = np.c_[x,y] Xt = trans(X) xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72., eps=eps ) Xto = np.c_[xto, yto] Xo = inv(Xto) return Xo[:,0], Xo[:,1] # some single points y = np.array([1,1,2,0,3,2,1.,4,3]) *1.e9 x = np.arange(len(y)) x[3]=x[4] #or try a sinus #x = np.linspace(0,9) #y=np.sin(x)*x/3. fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi) cols = ["r", "b"] lw = 11. lines = [] for i in range(len(cols)): l, = plt.plot(x,y, lw=lw, color=cols[i], solid_joinstyle="miter") lines.append(l) def plot_rainbow(event=None): xr = range(2); yr = range(2); xr[0],yr[0] = offset_curve(ax, x,y, lw/2.) xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.) for i in range(2): lines[i].set_data(xr[i], yr[i]) plot_rainbow() fig.canvas.mpl_connect("resize_event", plot_rainbow) fig.canvas.mpl_connect("button_release_event", plot_rainbow) plt.show() 

introduzca la descripción de la imagen aquí

Tenga en cuenta que este método debería funcionar bien siempre que el desplazamiento entre las líneas sea menor que la distancia entre los puntos subsiguientes de la línea. De lo contrario, el método 1 podría ser más adecuado.

Lo mejor que se me ocurre es tomar sus datos, generar una serie de pequeñas compensaciones y usar fill_between para hacer bandas del color que desee.

Escribí una función para hacer esto. No sé qué forma está tratando de dibujar, por lo que esto puede o no funcionar para usted. Lo probé en una parábola y obtuve resultados decentes. También puedes jugar con la lista de colores.

 def rainbow_plot(x, y, spacing=0.1): fig, ax = plt.subplots() colors = ['red', 'yellow', 'green', 'cyan','blue'] top = max(y) lines = [] for i in range(len(colors)+1): newline_data = y - top*spacing*i lines.append(newline_data) for i, c in enumerate(colors): ax.fill_between(x, lines[i], lines[i+1], facecolor=c) return fig, ax x = np.linspace(0,1,51) y = 1-(x-0.5)**2 rainbow_plot(x,y) 

trama parabólica