python - real - Matplotlib-agrega barra de color a una secuencia de gráficos de líneas
matplotlib python 3 (4)
(Sé que esta es una vieja pregunta, pero ...) Las barras de colores requieren un matplotlib.cm.ScalarMappable
, plt.plot
produce líneas que no son escalares mapeable, por lo tanto, para hacer una barra de color, vamos a necesitar hacer una escalar mappable
De acuerdo. Entonces, el constructor de un ScalarMappable
toma un cmap
y una instancia de norm
. (las normas escalan los datos al rango 0-1, cmaps con los que ya ha trabajado y toman un número entre 0-1 y devuelve un color). Entonces en tu caso:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(min=0, max=1))
plt.colorbar(sm)
Como sus datos ya están en el rango 0-1, puede simplificar la creación de sm
para:
sm = plt.cm.ScalarMappable(cmap=my_cmap)
Espero que ayude a alguien.
EDITAR : para matplotlib v1.2 o superior, el código se convierte en:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)
EDITAR : para matplotlib v1.3 o superior, el código se convierte en:
import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
# fake up the array of the scalar mappable. Urgh...
sm._A = []
plt.colorbar(sm)
Tengo una secuencia de gráficos de líneas para dos variables (x, y) para varios valores diferentes de una variable z. Normalmente agregaría los gráficos de líneas con leyendas como esta:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
# suppose mydata is a list of tuples containing (xs, ys, z)
# where xs and ys are lists of x''s and y''s and z is a number.
legns = []
for(xs,ys,z) in mydata:
pl = ax.plot(xs,ys,color = (z,0,0))
legns.append("z = %f"%(z))
ax.legends(legns)
plt.show()
Pero tengo demasiados gráficos y las leyendas cubrirán el gráfico. Prefiero tener una barra de color que indique el valor de z correspondiente al color. No puedo encontrar nada como eso en la galería y todos mis intentos si tratan con la barra de color han fallado. Aparentemente debo crear una colección de parcelas antes de intentar agregar una barra de colores.
¿Hay una forma fácil de hacer esto? Gracias.
EDITAR (aclaración):
Yo quería hacer algo como esto:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111)
mycmap = cm.hot
# suppose mydata is a list of tuples containing (xs, ys, z)
# where xs and ys are lists of x''s and y''s and z is a number between 0 and 1
plots = []
for(xs,ys,z) in mydata:
pl = ax.plot(xs,ys,color = mycmap(z))
plots.append(pl)
fig.colorbar(plots)
plt.show()
Pero esto no funcionará de acuerdo con la referencia de Matplotlib porque una lista de parcelas no es "asignable", sea lo que sea lo que esto signifique.
LineCollection
una función de trazado alternativa usando LineCollection
:
def myplot(ax,xs,ys,zs, cmap):
plot = lc([zip(x,y) for (x,y) in zip(xs,ys)], cmap = cmap)
plot.set_array(array(zs))
x0,x1 = amin(xs),amax(xs)
y0,y1 = amin(ys),amax(ys)
ax.add_collection(plot)
ax.set_xlim(x0,x1)
ax.set_ylim(y0,y1)
return plot
xs
y ys
son listas de listas de coordenadas xey, y zs
es una lista de las diferentes condiciones para colorear cada línea. Sin embargo, se siente un poco como un escándalo ... pensé que habría una manera más clara de hacer esto. Me gusta la flexibilidad de la función plt.plot()
.
Aquí hay un ejemplo ligeramente simplificado inspirado en la respuesta principal dada por y (¡Gracias por la gran idea!):
1. Barra de colores discreta
La barra de color discreta está más involucrada, porque el mapa de mpl.cm.get_cmap()
generado por mpl.cm.get_cmap()
no es una imagen asignable necesaria como un argumento de colorbar()
. Se debe generar un dummie mappable como se muestra a continuación:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
cmap = mpl.cm.get_cmap(''jet'', n_lines)
fig, ax = plt.subplots(dpi=100)
# Make dummie mappable
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
# Clear axis
ax.cla()
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap(i))
fig.colorbar(dummie_cax, ticks=c)
plt.show();
Esto producirá una trama con una barra de colores discreta:
2. Barra de color continua
La barra de color continua es menos complicada, ya que mpl.cm.ScalarMappable()
nos permite obtener una "imagen" para colorbar()
.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)
norm = mpl.colors.Normalize(vmin=c.min(), vmax=c.max())
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.jet)
cmap.set_array([])
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap.to_rgba(i + 1))
fig.colorbar(cmap, ticks=c)
plt.show();
Esto producirá una trama con una barra de color continua:
[Nota al cmap.set_array([])
] En este ejemplo, personalmente no sé por qué es necesario cmap.set_array([])
(de lo contrario, obtendríamos mensajes de error). Si alguien entiende los principios bajo el capó, por favor comente :)
Como otras respuestas aquí intentan usar gráficos ficticios, lo cual no es realmente un buen estilo, aquí hay un código genérico para un
Discreta barra de color
Se produce una barra de color discreta de la misma manera que se crea una barra de color continua, solo con una normalización diferente. En este caso, se debe usar una BoundaryNorm
.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1., n_lines + 1)
cmap = plt.get_cmap("jet", len(c))
norm = matplotlib.colors.BoundaryNorm(np.arange(len(c)+1)+0.5,len(c))
sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])
fig, ax = plt.subplots(dpi=100)
for i, yi in enumerate(y.T):
ax.plot(x, yi, c=cmap(i))
fig.colorbar(sm, ticks=c)
plt.show()
Esta es una forma de hacerlo mientras se usa plt.plot (). Básicamente, haces una trama desechable y obtienes la barra de colores desde allí.
import matplotlib as mpl
import matplotlib.pyplot as plt
min, max = (-40, 30)
step = 10
# Setting up a colormap that''s a simple transtion
mymap = mpl.colors.LinearSegmentedColormap.from_list(''mycolors'',[''blue'',''red''])
# Using contourf to provide my colorbar info, then clearing the figure
Z = [[0,0],[0,0]]
levels = range(min,max+step,step)
CS3 = plt.contourf(Z, levels, cmap=mymap)
plt.clf()
# Plotting what I actually want
X=[[1,2],[1,2],[1,2],[1,2]]
Y=[[1,2],[1,3],[1,4],[1,5]]
Z=[-40,-20,0,30]
for x,y,z in zip(X,Y,Z):
# setting rgb color based on z normalized to my range
r = (float(z)-min)/(max-min)
g = 0
b = 1-r
plt.plot(x,y,color=(r,g,b))
plt.colorbar(CS3) # using the colorbar info I got from contourf
plt.show()
Es un poco derrochador, pero conveniente. Tampoco es muy derrochador si haces varias parcelas, ya que puedes llamar a plt.colorbar () sin regenerar la información para ello.