Establecer el rango de la barra de colores en matplotlib

156

Tengo el siguiente código:

import matplotlib.pyplot as plt

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

plt.clf()
plt.pcolor(X, Y, v, cmap=cm)
plt.loglog()
plt.xlabel('X Axis')
plt.ylabel('Y Axis')

plt.colorbar()
plt.show()

Entonces esto produce un gráfico de los valores 'v' en los ejes X vs Y, usando el mapa de colores especificado. Los ejes X e Y son perfectos, pero el mapa de colores se extiende entre el mínimo y el máximo de v. Me gustaría forzar que el mapa de colores oscile entre 0 y 1.

Pensé en usar:

plt.axis(...)

Para establecer los rangos de los ejes, pero esto solo toma argumentos para el mínimo y máximo de X e Y, no el mapa de colores.

Editar:

Para mayor claridad, digamos que tengo un gráfico cuyos valores varían (0 ... 0.3) y otro gráfico cuyos valores (0.2 ... 0.8).

En ambos gráficos, querré que el rango de la barra de colores sea (0 ... 1). En ambos gráficos, quiero que este rango de color sea idéntico usando el rango completo de cdict arriba (por lo que 0.25 en ambos gráficos será del mismo color). En el primer gráfico, todos los colores entre 0.3 y 1.0 no aparecerán en el gráfico, pero sí en la tecla de barra de colores al costado. En el otro, todos los colores entre 0 y 0.2, y entre 0.8 y 1 no aparecerán en el gráfico, pero sí en la barra de colores al costado.

Pablo
fuente

Respuestas:

177

Usa vminy vmaxfuerza el rango de los colores. Aquí hay un ejemplo:

ingrese la descripción de la imagen aquí

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )

def do_plot(n, f, title):
    #plt.clf()
    plt.subplot(1, 3, n)
    plt.pcolor(X, Y, f(data), cmap=cm, vmin=-4, vmax=4)
    plt.title(title)
    plt.colorbar()

plt.figure()
do_plot(1, lambda x:x, "all")
do_plot(2, lambda x:np.clip(x, -4, 0), "<0")
do_plot(3, lambda x:np.clip(x, 0, 4), ">0")
plt.show()
tom10
fuente
3
¿Por qué esta respuesta es mejor que la que usa plt.clim publicada por @Amro?
Alex Lamson
90

Use la función CLIM (equivalente a la función CAXIS en MATLAB):

plt.pcolor(X, Y, v, cmap=cm)
plt.clim(-4,4)  # identical to caxis([-4,4]) in MATLAB
plt.show()
Amro
fuente
2
Creo que clim () escala los ejes de color, pero los colores mismos cambian los valores. El punto en una cierta fracción a lo largo de la escala será del mismo color, sea cual sea la escala, pero el valor que representa cambiará.
Paul
44
Si. Este es el comportamiento deseado del autor de la pregunta, por lo que resuelve el problema: que la escala de colores sea idéntica entre los gráficos.
Excalabur
16

No estoy seguro de si esta es la solución más elegante (esto es lo que usé), pero podría escalar sus datos al rango entre 0 y 1 y luego modificar la barra de colores:

import matplotlib as mpl
...
ax, _ = mpl.colorbar.make_axes(plt.gca(), shrink=0.5)
cbar = mpl.colorbar.ColorbarBase(ax, cmap=cm,
                       norm=mpl.colors.Normalize(vmin=-0.5, vmax=1.5))
cbar.set_clim(-2.0, 2.0)

Con los dos límites diferentes puede controlar el rango y la leyenda de la barra de colores. En este ejemplo, solo se muestra el rango entre -0.5 a 1.5 en la barra, mientras que el mapa de colores cubre de -2 a 2 (por lo que este podría ser su rango de datos, que registra antes de la escala).

Entonces, en lugar de escalar el mapa de colores, escala sus datos y ajusta la barra de colores a eso.

nikow
fuente
1
Creo que eso está haciendo algo sutilmente diferente ... lo siento, probablemente no fui lo suficientemente precisa en mi pregunta. Su solución escalará los colores para que lo que solía representar el valor 1.0 ahora represente el valor máximo en mis datos. La barra de colores mostrará 0..1 como lo necesito (con Vmin = 0, vmax = 1), pero todo por encima de este valor máximo será el mismo color ...
Paul
1
... He actualizado mi pregunta para mostrar lo que busco más claramente. Lo siento si fui demasiado vago.
Paul
10

Usando entorno figura y .set_clim ()

Esta alternativa podría ser más fácil y segura si tiene varias parcelas:

import matplotlib as m
import matplotlib.pyplot as plt
import numpy as np

cdict = {
  'red'  :  ( (0.0, 0.25, .25), (0.02, .59, .59), (1., 1., 1.)),
  'green':  ( (0.0, 0.0, 0.0), (0.02, .45, .45), (1., .97, .97)),
  'blue' :  ( (0.0, 1.0, 1.0), (0.02, .75, .75), (1., 0.45, 0.45))
}

cm = m.colors.LinearSegmentedColormap('my_colormap', cdict, 1024)

x = np.arange(0, 10, .1)
y = np.arange(0, 10, .1)
X, Y = np.meshgrid(x,y)

data = 2*( np.sin(X) + np.sin(3*Y) )
data1 = np.clip(data,0,6)
data2 = np.clip(data,-6,0)
vmin = np.min(np.array([data,data1,data2]))
vmax = np.max(np.array([data,data1,data2]))

fig = plt.figure()
ax = fig.add_subplot(131)
mesh = ax.pcolormesh(data, cmap = cm)
mesh.set_clim(vmin,vmax)
ax1 = fig.add_subplot(132)
mesh1 = ax1.pcolormesh(data1, cmap = cm)
mesh1.set_clim(vmin,vmax)
ax2 = fig.add_subplot(133)
mesh2 = ax2.pcolormesh(data2, cmap = cm)
mesh2.set_clim(vmin,vmax)
# Visualizing colorbar part -start
fig.colorbar(mesh,ax=ax)
fig.colorbar(mesh1,ax=ax1)
fig.colorbar(mesh2,ax=ax2)
fig.tight_layout()
# Visualizing colorbar part -end

plt.show()

ingrese la descripción de la imagen aquí

Una sola barra de colores

La mejor alternativa es usar una sola barra de color para toda la trama. Hay diferentes formas de hacerlo, este tutorial es muy útil para comprender la mejor opción. Prefiero esta solución que simplemente puede copiar y pegar en lugar de la parte anterior de la barra de colores de visualización del código.

fig.subplots_adjust(bottom=0.1, top=0.9, left=0.1, right=0.8,
                    wspace=0.4, hspace=0.1)
cb_ax = fig.add_axes([0.83, 0.1, 0.02, 0.8])
cbar = fig.colorbar(mesh, cax=cb_ax)

ingrese la descripción de la imagen aquí

PD

Sugeriría usar en pcolormeshlugar de pcolorporque es más rápido (más información aquí ).

GM
fuente