example - palette python matplotlib
Cómo trazar una línea de color de degradado en matplotlib? (5)
Para decirlo en una forma general, estoy buscando una manera de unir varios puntos con una línea de color de degradado usando matplotlib , y no lo encuentro en ningún lado. Para ser más específico, estoy trazando un paseo aleatorio 2D con una línea de color. Pero, como los puntos tienen una secuencia relevante, me gustaría mirar la trama y ver dónde se movieron los datos. Una línea de color degradado haría el truco. O una línea con transparencia gradualmente cambiante.
Solo trato de mejorar la visualización de mis datos. Echa un vistazo a esta hermosa imagen producida por el paquete ggplot2 de R. Estoy buscando lo mismo en matplotlib. Gracias.
Demasiado tiempo para un comentario, así que solo quería confirmar que LineCollection
es mucho más rápido que los subsegmentos for-loop over line.
el método LineCollection es mucho más rápido en mis manos.
# Setup
x = np.linspace(0,4*np.pi,1000)
y = np.sin(x)
MAP = ''cubehelix''
NPOINTS = len(x)
Probaremos el trazado iterativo contra el método LineCollection anterior.
%%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
Subir muestras de su línea para obtener un mejor degradado de color, como lo proporciona la respuesta actualmente seleccionada, es una buena idea si desea un degradado uniforme y solo tiene algunos puntos.
Estaba usando el código @alexbw, para trazar una parábola. Funciona muy bien. Soy capaz de cambiar el conjunto de colores para la función. Para el cálculo, me tomó alrededor de 1 minuto y 30 segundos. Estaba usando Intel i5, gráficos 2 gb, 8 gb ram.
El código es el siguiente:
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
Y el resultado es: https://i.stack.imgur.com/gL9DG.png
Hace poco respondí una pregunta con una solicitud similar ( creando más de 20 colores de leyenda únicos usando matplotlib ). Allí mostré que puedes mapear el ciclo de colores que necesitas para trazar tus líneas en un mapa de colores. Puede usar el mismo procedimiento para obtener un color específico para cada par de puntos.
Debe elegir cuidadosamente el mapa de colores, ya que las transiciones de color a lo largo de su línea pueden parecer drásticas si el mapa de colores es colorido.
Alternativamente, puede cambiar el alfa de cada segmento de línea, que va de 0 a 1.
El ejemplo de código que se incluye a continuación incluye una rutina ( highResPoints
) para expandir la cantidad de puntos que tiene su recorrido aleatorio, porque si tiene muy pocos puntos, las transiciones pueden parecer drásticas. Este fragmento de código se inspiró en otra respuesta reciente que proporcioné: https://.com/a/8253729/717357
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()
Esta figura muestra los cuatro casos:
He agregado mi solución usando pcolormesh Cada segmento de línea se dibuja usando un rectángulo que se está interpolando entre los colores en cada extremo. Entonces realmente se está interpolando el color, pero tenemos que pasar un grosor de la línea.
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)
Tenga en cuenta que si tiene muchos puntos, llamar a plt.plot
para cada segmento de línea puede ser bastante lento. Es más eficiente usar un objeto LineCollection.
Usando la receta de línea de colorline
puedes hacer lo siguiente:
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()