Defina o tamanho da barra de cores do Matplotlib para corresponder ao gráfico

161

Não consigo fazer com que a barra de cores em gráficos de exibição como esta tenha a mesma altura do gráfico, sem usar o Photoshop depois do fato. Como faço para combinar as alturas?Exemplo de incompatibilidade de tamanho da barra de cores

Elliot
fonte
Você já tentou as sugestões de stackoverflow.com/questions/16702479/...
lmjohns3
@ imjohns3 Nada nesse post parece fazer nada na barra de cores. Ele permanece do mesmo tamanho, não importa o que eu defina. Se eu definir fração e diminuir, o tamanho do gráfico mudará enquanto a barra de cores permanecerá a mesma, até voltarmos ao que já tenho, e eles pararão de fazer qualquer coisa.
Elliot
Confira os documentos - matplotlib.org/api/colorbar_api.html - e use fractionou shrinkargs.
BoltzmannBrain

Respostas:

194

Você pode fazer isso facilmente com um AxisDivider matplotlib .

O exemplo da página vinculada também funciona sem o uso de 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)

insira a descrição da imagem aqui

Bogatron
fonte
1
Como não estou trabalhando dentro de uma subtrama, isso não é aplicável.
22413 Elliot
Depois de muito mexer, consegui que funcionasse. Tem muitos problemas para interagir com o subplot_adjust, você precisa receber as chamadas nos lugares certos em relação uma à outra.
22413 Elliot
11
Isso está alterando levemente o tamanho dos gráficos. Eu tenho 4 em uma grade 2x2, mas só quero que os dois à direita tenham barras (a escala se aplica por linha). No entanto, isso não os torna do mesmo tamanho. Tentei simplesmente não ter a chamada da barra de cores (com a chamada do divisor), mas é claro que isso deixa uma caixa branca vazia e números ao lado. Como faço para que eles tenham um tamanho consistente sem colocar barras em todos eles?
Elliot
@bogatron Infelizmente, isso não funciona com eixos projetados
Bogdan
Se você deseja adicionar um título usando plt.title, ele será exibido trocado para a direita. Existe uma maneira de superar isso?
user2820579
224

Essa combinação (e valores próximos a esses) parece funcionar "magicamente" para manter a barra de cores dimensionada para o gráfico, independentemente do tamanho da exibição.

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

Também não requer o compartilhamento do eixo que pode obter o gráfico fora do quadrado.

skytaker
fonte
4
Isso pode funcionar em alguns casos, mas em geral não. Tente, por exemplo, traçar algo como na pergunta original, que tem uma largura duas vezes a altura.
Matthias
A opção de fração ainda parece funcionar no caso mencionado, se dimensionada para corresponder à altura da plotagem. (mpl v1.4.3)
skytaker 4/15
6
Esta é a única maneira universal de fazê-lo. As soluções com axex_grid1 não funcionarão para eixos projetados, como GeoAxes.
Bogdan
OMG isso é incrível. Muito obrigado pela solução
2D_
3
Em resposta ao comentário @Matthias. Você pode corrigir o caso em que a imagem é muito larga usando este truque: im_ratio = data.shape[0]/data.shape[1] plt.colorbar(im,fraction=0.046*im_ratio, pad=0.04)onde dataestá sua imagem.
eindzl
30

O @bogatron já deu a resposta sugerida pelos documentos do matplotlib , que produz a altura certa, mas apresenta um problema diferente. Agora, a largura da barra de cores (assim como o espaço entre a barra de cores e a plotagem) muda com a largura da plotagem. Em outras palavras, a proporção da barra de cores não é mais fixa.

Para obter a altura correta e uma determinada proporção, você precisa se aprofundar um pouco no 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)

Observe que isso especifica a largura da barra de cores e a altura do gráfico (em contraste com a largura da figura, como era antes).

O espaçamento entre a barra de cores e o gráfico agora pode ser especificado como uma fração da largura da barra de cores, que é um número muito mais significativo do que uma fração da largura da figura.

plotagem de imagem com barra de cores

ATUALIZAR:

Criei um bloco de notas IPython sobre o tópico , onde agrupei o código acima em uma função facilmente reutilizável:

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)

Pode ser usado assim:

im = plt.imshow(np.arange(200).reshape((20, 10)))
add_colorbar(im)
Matthias
fonte
1
Esta é uma pequena função realmente útil! Uma palavra de aviso é que ele não funciona quando você deseja adicionar várias barras de cores, porque elas aparecem umas sobre as outras.
David Hall
22

Agradeço todas as respostas acima. No entanto, como algumas respostas e comentários apontou, o axes_grid1módulo pode não GeoAxes de endereços, enquanto ajustando fraction, pad, shrink, e outros parâmetros semelhantes podem não necessariamente dar a ordem muito precisa, o que realmente me incomoda. Acredito que dar o que é colorbarseu axespode ser uma solução melhor para resolver todos os problemas mencionados.

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

insira a descrição da imagem aqui

Mais tarde, acho que matplotlib.pyplot.colorbara documentação oficial também oferece a axopção, que são eixos existentes que fornecerão espaço para a barra de cores. Portanto, é útil para várias subparcelas, veja a seguir.

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

insira a descrição da imagem aqui

Novamente, você também pode obter efeitos semelhantes especificando cax, uma maneira mais precisa da minha 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

insira a descrição da imagem aqui

Fei Yao
fonte
@ HS-nebula Obrigado por seus comentários e estou feliz que você goste. :)
Fei Yao
Resposta realmente incrível. Obrigado!!
episodeyang
11

Quando você cria a colorbartentativa usando os parâmetros de fração e / ou contração.

Dos documentos:

fração 0,15; fração dos eixos originais a serem usados ​​na barra de cores

encolher 1,0; fração pela qual diminuir a barra de cores

Steve Barnes
fonte
1
Se eu definir shrink 1.0 e fração para qualquer coisa, ele reduzirá o gráfico, sem afetar o tamanho da barra de cores, até que a alteração da fração faça com que seja exatamente o que eu já tenho; nesse momento, alterá-los para de fazer qualquer coisa.
Elliot
Onde exatamente você os especifica, eles devem ser parâmetros para a colorbar()função ou método.
22613 Steve Barnes
Obrigado. só precisa especificar o parâmetro shrink e funciona como mágica!
CodingNow
11

Todas as soluções acima são boas, mas eu gosto da @ Steve e da @ bejota as melhores, pois não envolvem chamadas sofisticadas e são universais.

Por universal, quero dizer que funciona com qualquer tipo de eixo, inclusive GeoAxes. Por exemplo, você projetou eixos para mapeamento:

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

uma chamada para

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

falhará com: KeyException: map_projection

Portanto, a única maneira universal de lidar com o tamanho da barra de cores com todos os tipos de eixos é:

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

Trabalhe com fração de 0,035 a 0,046 para obter o melhor tamanho. No entanto, os valores para a fração e o paddig precisarão ser ajustados para obter o melhor ajuste para sua plotagem e variarão dependendo se a orientação da barra de cores estiver na posição vertical ou horizontal.

Bogdan
fonte
1
Também podemos adicionar shrinkparâmetros quando, em fractionconjunto pad, não produzem os resultados desejados o suficiente.
Fei Yao