Pregunta

Tengo una secuencia de gráficos de línea para dos variables (x, y) para varios valores diferentes de una variable z. Normalmente agregaría las parcelas de línea 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 de eso en la galerista y todos mis intentos que tratan con la barra de color fallaron. Aparentemente debo crear una colección de tramas antes de tratar de agregar una barra de colores.

¿Hay una forma fácil de hacer esto? Gracias.

Editar (aclaración):

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 tramas no es un "mapeable", lo que sea que esto signifique.

He creado 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 X e Y y zs es una lista de las diferentes condiciones para colorear cada línea. Sin embargo, se siente un poco como un revuelo ... Pensé que habría una forma más ordenada de hacer esto. Me gusta la flexibilidad del plt.plot() función.

¿Fue útil?

Solución

(Sé que esta es una pregunta antigua pero ...) Las barras de color requieren una matplotlib.cm.ScalarMappable, plt.plot Produce líneas que no son mapeables escalar, por lo tanto, para hacer una barra de colores, tendremos que hacer un mapeable escalar.

Está bien. Entonces el constructor de un ScalarMappable toma una cmap y un norm instancia. (Las normas escalan datos al rango 0-1, CMAP 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)

Debido a que sus datos ya están en el rango 0-1, puede simplificar el sm Creación a:

sm = plt.cm.ScalarMappable(cmap=my_cmap)

Espero que eso ayude a alguien.

EDITAR: Para matplotlib v1.2 o mayor 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 mayor 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 v3.1 o mayor se simplifica a:

import matplotlib.pyplot as plt
sm = plt.cm.ScalarMappable(cmap=my_cmap, norm=plt.Normalize(vmin=0, vmax=1))
plt.colorbar(sm)

Otros consejos

Aquí hay una forma de hacerlo mientras 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 hace múltiples gráficos, ya que puede llamar plt.colorbar () sin regenerar la información para ello.

enter image description here

Aquí hay un ejemplo ligeramente simplificado inspirado en la respuesta superior dada por Boris y Enganchado (¡Gracias por la gran idea!):

1. Barra de color discreta

La barra de color discreta está más involucrada, porque el colormapap generado por mpl.cm.get_cmap() no es una imagen mapeable necesaria como un colorbar() argumento. Un mappable de Dummie debe generarse 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 color discreta:enter image description here


2. Barra de color continuo

La barra de color continua es menos involucrada, como 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:enter image description here

Nota al margen En este ejemplo, personalmente no sé por qué cmap.set_array([]) es necesario (de lo contrario, obtendríamos mensajes de error). Si alguien entiende los principios debajo del capó, comente :)

Como otras respuestas aquí, intente usar gráficos ficticios, lo cual no es un estilo realmente bueno, aquí hay un código genérico para un

Barra de color discreta

Una barra de color discreta se produce de la misma manera que se crea una barra de color continua, solo con una normalización diferente. En este caso un BoundaryNorm debería ser usado.

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([])  # this line may be ommitted for matplotlib >= 3.1

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()

enter image description here

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top