Establezca el tamaño de la barra de colores Matplotlib para que coincida con el gráfico

161

No puedo obtener la barra de color en gráficos como este para que tenga la misma altura que el gráfico, salvo el uso de Photoshop después del hecho. ¿Cómo consigo que coincidan las alturas?Ejemplo de desajuste del tamaño de la barra de colores

Elliot
fuente
¿Has probado las sugerencias de stackoverflow.com/questions/16702479/…
lmjohns3
@ imjohns3 Nada en esa publicación parece hacer nada a la barra de colores. Se mantiene del mismo tamaño sin importar lo que configuré. Sin embargo, si configuro la fracción y el encogimiento, el tamaño del gráfico cambiará mientras la barra de color permanezca igual, hasta que volvamos a lo que ya tengo, entonces dejarán de hacer nada.
Elliot
Consulte los documentos ( matplotlib.org/api/colorbar_api.html ) y use fractiono shrinkargs.
BoltzmannBrain

Respuestas:

194

Puede hacerlo fácilmente con un matplotlib AxisDivider .

El ejemplo de la página vinculada también funciona sin usar subtramas:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np

plt.figure()
ax = plt.gca()
im = ax.imshow(np.arange(100).reshape((10,10)))

# create an axes on the right side of ax. The width of cax will be 5%
# of ax and the padding between cax and ax will be fixed at 0.05 inch.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)

plt.colorbar(im, cax=cax)

ingrese la descripción de la imagen aquí

bogatron
fuente
1
No estoy trabajando dentro de una subtrama, por lo que esto no es aplicable.
Elliot
Después de mucho retocar, lo puse a trabajar. Tiene muchos problemas para interactuar con subplot_adjust, debe recibir las llamadas en los lugares correctos entre sí.
Elliot
11
Esto está cambiando ligeramente el tamaño de los gráficos. Tengo 4 en una cuadrícula de 2x2, pero solo quiero que las dos de la derecha tengan barras (la escala se aplica por fila). Sin embargo, esto los hace no del mismo tamaño. Intenté simplemente no tener la llamada de la barra de colores (con la llamada del divisor), pero, por supuesto, esto deja un cuadro blanco vacío y números a un lado. ¿Cómo consigo que tengan un tamaño consistente sin poner barras en todos ellos?
Elliot
@bogatron Desafortunadamente, esto no funciona con los ejes proyectados
Bogdan
Si desea agregar un título usando plt.title, se mostrará intercambiado a la derecha. ¿Hay una manera de superar esto?
user2820579
225

Esta combinación (y valores cercanos a estos) parece funcionar "mágicamente" para mí para mantener la barra de colores ajustada a la trama, sin importar el tamaño de la pantalla.

plt.colorbar(im,fraction=0.046, pad=0.04)

Tampoco requiere compartir el eje que puede sacar la trama de forma cuadrada.

Skytaker
fuente
44
Esto puede funcionar en algunos casos, pero en general no. Intente, por ejemplo, trazar algo como en la pregunta original, que tiene un ancho dos veces la altura.
Matthias
La opción de fracción aún parece funcionar en el caso que usted menciona, si se ajusta para que coincida con la altura de la gráfica. (mpl v1.4.3)
skytaker
66
Esta es la única forma universal de hacerlo. Las soluciones con axex_grid1 no funcionarán para ejes proyectados como GeoAxes.
Bogdan
Dios mío, esto es increíble. Muchas gracias por la solución
2D_
3
En respuesta al comentario de @Matthias. Puede corregir el caso en que la imagen es demasiado ancha utilizando este truco: im_ratio = data.shape[0]/data.shape[1] plt.colorbar(im,fraction=0.046*im_ratio, pad=0.04)dónde dataestá su imagen.
eindzl
30

@bogatron ya dio la respuesta sugerida por los documentos matplotlib , que produce la altura correcta, pero presenta un problema diferente. Ahora, el ancho de la barra de colores (así como el espacio entre la barra de colores y el gráfico) cambia con el ancho del gráfico. En otras palabras, la relación de aspecto de la barra de colores ya no está fija.

Para obtener la altura correcta y una relación de aspecto dada, debes profundizar un poco más en el misterioso axes_grid1módulo.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
import numpy as np

aspect = 20
pad_fraction = 0.5

ax = plt.gca()
im = ax.imshow(np.arange(200).reshape((20, 10)))
divider = make_axes_locatable(ax)
width = axes_size.AxesY(ax, aspect=1./aspect)
pad = axes_size.Fraction(pad_fraction, width)
cax = divider.append_axes("right", size=width, pad=pad)
plt.colorbar(im, cax=cax)

Tenga en cuenta que esto especifica el ancho de la barra de colores y la altura del gráfico (en contraste con el ancho de la figura, como era antes).

El espacio entre la barra de colores y la gráfica ahora se puede especificar como una fracción del ancho de la barra de colores, que en mi humilde opinión es un número mucho más significativo que una fracción del ancho de la figura.

trama de imagen con barra de colores

ACTUALIZAR:

He creado un cuaderno de IPython sobre el tema , en el que lleno el código anterior en función de un lugar fácilmente reutilizable:

import matplotlib.pyplot as plt
from mpl_toolkits import axes_grid1

def add_colorbar(im, aspect=20, pad_fraction=0.5, **kwargs):
    """Add a vertical color bar to an image plot."""
    divider = axes_grid1.make_axes_locatable(im.axes)
    width = axes_grid1.axes_size.AxesY(im.axes, aspect=1./aspect)
    pad = axes_grid1.axes_size.Fraction(pad_fraction, width)
    current_ax = plt.gca()
    cax = divider.append_axes("right", size=width, pad=pad)
    plt.sca(current_ax)
    return im.axes.figure.colorbar(im, cax=cax, **kwargs)

Se puede usar así:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)
Matías
fuente
1
¡Esta es una pequeña función realmente útil! Una palabra de advertencia es que no funciona cuando desea agregar varias barras de colores, ya que aparecen una encima de la otra.
David Hall
22

Agradezco todas las respuestas anteriores. Sin embargo, como señalaron algunas respuestas y comentarios, el axes_grid1módulo no puede abordar GeoAxes, mientras que al ajustar fraction,pad , shrink, y otros parámetros similares puede no necesariamente dar la orden muy precisa, lo que realmente me molesta. Creo que dar el colorbarsuyo propio axespodría ser una mejor solución para abordar todos los problemas que se han mencionado.

Código

import matplotlib.pyplot as plt
import numpy as np

fig=plt.figure()
ax = plt.axes()
im = ax.imshow(np.arange(100).reshape((10,10)))

# Create an axes for colorbar. The position of the axes is calculated based on the position of ax.
# You can change 0.01 to adjust the distance between the main image and the colorbar.
# You can change 0.02 to adjust the width of the colorbar.
# This practice is universal for both subplots and GeoAxes.

cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax) # Similar to fig.colorbar(im, cax = cax)

Resultado

ingrese la descripción de la imagen aquí

Más tarde, encuentro matplotlib.pyplot.colorbar la documentación oficial también daax opción, que son ejes existentes que proporcionarán espacio para la barra de colores. Por lo tanto, es útil para múltiples subtramas, vea a continuación.

Código

fig, ax = plt.subplots(2,1,figsize=(12,8)) # Caution, figsize will also influence positions.
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
fig.colorbar(im1, ax=ax)

Resultado

ingrese la descripción de la imagen aquí

De nuevo, también puede lograr efectos similares especificando cax, una forma más precisa desde mi perspectiva.

Código

fig, ax = plt.subplots(2,1,figsize=(12,8))
im1 = ax[0].imshow(np.arange(100).reshape((10,10)), vmin = -100, vmax =100)
im2 = ax[1].imshow(np.arange(-100,0).reshape((10,10)), vmin = -100, vmax =100)
cax = fig.add_axes([ax[1].get_position().x1-0.25,ax[1].get_position().y0,0.02,ax[0].get_position().y1-ax[1].get_position().y0])
fig.colorbar(im1, cax=cax)

Resultado

ingrese la descripción de la imagen aquí

Fei Yao
fuente
@ HS-nebula Gracias por sus comentarios y estoy encantado de que lo disfruten. :)
Fei Yao
Realmente sorprendente respuesta. ¡¡Gracias!!
episodeyang
11

Cuando creas el colorbar intento, use los parámetros de fracción y / o reducción.

De los documentos:

fracción 0,15; fracción de ejes originales para usar para la barra de colores

reducir 1.0; fracción por la cual reducir la barra de colores

Steve Barnes
fuente
1
Si configuro el encogimiento 1.0 y la fracción en cualquier cosa, encoge el gráfico, sin afectar el tamaño de la barra de colores, hasta que cambiar la fracción hace que sea exactamente lo que ya tengo, en cuyo punto cambiarlos deja de hacer nada.
Elliot
¿Dónde los especifica exactamente? Tienen que ser parámetros para la colorbar()función o método.
Steve Barnes
Gracias. ¡solo necesita especificar el parámetro de contracción y funciona como magia!
CodingNow
11

Todas las soluciones anteriores son buenas, pero me gustan las mejores de @ Steve y @ bejota, ya que no implican llamadas elegantes y son universales.

Por universal quiero decir que funciona con cualquier tipo de ejes incluidos GeoAxes. Por ejemplo, si ha proyectado ejes para el mapeo:

projection = cartopy.crs.UTM(zone='17N')
ax = plt.axes(projection=projection)
im = ax.imshow(np.arange(200).reshape((20, 10)))

una llamada a

cax = divider.append_axes("right", size=width, pad=pad)

fallará con: KeyException: map_projection

Por lo tanto, la única forma universal de tratar el tamaño de la barra de colores con todos los tipos de ejes es:

ax.colorbar(im, fraction=0.046, pad=0.04)

Trabaja con una fracción de 0.035 a 0.046 para obtener tu mejor tamaño. Sin embargo, los valores para la fracción y el paddig deberán ajustarse para obtener el mejor ajuste para su gráfico y diferirán dependiendo de si la orientación de la barra de colores está en posición vertical u horizontal.

Bogdan
fuente
1
También podemos agregar shrinkparámetros cuando, fractionjunto con padno producir los resultados deseados lo suficiente.
Fei Yao