python - legends - Matplotlib 2 subtramas, 1 barra de colores
pyplot title (7)
Como principiante que tropecé con este hilo, me gustaría añadir una adaptación pitón-para- tontos de abevieiramota muy clara respuesta (porque estoy en el nivel que tuve que buscar ''ravel'' para averiguar qué su código estaba haciendo):
import numpy as np
import matplotlib.pyplot as plt
fig, ((ax1,ax2,ax3),(ax4,ax5,ax6)) = plt.subplots(2,3)
axlist = [ax1,ax2,ax3,ax4,ax5,ax6]
first = ax1.imshow(np.random.random((10,10)), vmin=0, vmax=1)
third = ax3.imshow(np.random.random((12,12)), vmin=0, vmax=1)
fig.colorbar(first, ax=axlist)
plt.show()
Mucho menos pitónico, mucho más fácil para los noobs como yo para ver lo que está sucediendo aquí.
Pasé demasiado tiempo investigando cómo obtener dos subparcelas para compartir el mismo eje y con una única barra de color compartida entre las dos en Matplotlib.
Lo que sucedía era que cuando llamaba a la función colorbar()
en subplot1
o subplot2
, subplot2
automáticamente el gráfico de manera que la barra de color más el gráfico encajara dentro del cuadro delimitador de "subtrama", causando las dos parcelas lado a lado para ser dos tamaños muy diferentes.
Para evitar esto, traté de crear una tercera subparcela que luego corté para que no apareciera ninguna gráfica con solo una barra de colores. El único problema es que ahora las alturas y el ancho de las dos parcelas son desiguales, y no sé cómo hacer que se vea bien.
Aquí está mi código:
from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import patches
from matplotlib.ticker import NullFormatter
# SIS Functions
TE = 1 # Einstein radius
g1 = lambda x,y: (TE/2) * (y**2-x**2)/((x**2+y**2)**(3/2))
g2 = lambda x,y: -1*TE*x*y / ((x**2+y**2)**(3/2))
kappa = lambda x,y: TE / (2*np.sqrt(x**2+y**2))
coords = np.linspace(-2,2,400)
X,Y = np.meshgrid(coords,coords)
g1out = g1(X,Y)
g2out = g2(X,Y)
kappaout = kappa(X,Y)
for i in range(len(coords)):
for j in range(len(coords)):
if np.sqrt(coords[i]**2+coords[j]**2) <= TE:
g1out[i][j]=0
g2out[i][j]=0
fig = plt.figure()
fig.subplots_adjust(wspace=0,hspace=0)
# subplot number 1
ax1 = fig.add_subplot(1,2,1,aspect=''equal'',xlim=[-2,2],ylim=[-2,2])
plt.title(r"$/gamma_{1}$",fontsize="18")
plt.xlabel(r"x ($/theta_{E}$)",fontsize="15")
plt.ylabel(r"y ($/theta_{E}$)",rotation=''horizontal'',fontsize="15")
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.xticks([-2.0,-1.5,-1.0,-0.5,0,0.5,1.0,1.5])
plt.imshow(g1out,extent=(-2,2,-2,2))
plt.axhline(y=0,linewidth=2,color=''k'',linestyle="--")
plt.axvline(x=0,linewidth=2,color=''k'',linestyle="--")
e1 = patches.Ellipse((0,0),2,2,color=''white'')
ax1.add_patch(e1)
# subplot number 2
ax2 = fig.add_subplot(1,2,2,sharey=ax1,xlim=[-2,2],ylim=[-2,2])
plt.title(r"$/gamma_{2}$",fontsize="18")
plt.xlabel(r"x ($/theta_{E}$)",fontsize="15")
ax2.yaxis.set_major_formatter( NullFormatter() )
plt.axhline(y=0,linewidth=2,color=''k'',linestyle="--")
plt.axvline(x=0,linewidth=2,color=''k'',linestyle="--")
plt.imshow(g2out,extent=(-2,2,-2,2))
e2 = patches.Ellipse((0,0),2,2,color=''white'')
ax2.add_patch(e2)
# subplot for colorbar
ax3 = fig.add_subplot(1,1,1)
ax3.axis(''off'')
cbar = plt.colorbar(ax=ax2)
plt.show()
Como se señaló en otras respuestas, la idea es generalmente definir un eje para que resida la barra de color. Hay varias maneras de hacerlo; uno que no ha sido mencionado todavía sería especificar directamente los ejes de la barra de colores en la creación de subtramas con plt.subplots()
. La ventaja es que la posición de los ejes no necesita ser configurada manualmente y en todos los casos con aspecto automático, la barra de colores tendrá exactamente la misma altura que las subtramas. Incluso en muchos casos en que se utilizan imágenes, el resultado será satisfactorio, como se muestra a continuación.
Al usar plt.subplots()
, el uso del argumento gridspec_kw
permite hacer que los ejes de la barra de colores sean mucho más pequeños que los otros ejes.
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
Ejemplo:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(5.5,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im = ax.imshow(np.random.rand(11,8), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,8), vmin=0, vmax=1)
ax.set_ylabel("y label")
fig.colorbar(im, cax=cax)
plt.show()
Esto funciona bien, si el aspecto de las gráficas se escala automáticamente o las imágenes se reducen debido a su aspecto en la dirección del ancho (como en el ejemplo anterior). Sin embargo, si las imágenes son más anchas que altas, el resultado sería el siguiente, que podría no ser deseado.
Una solución para fijar la altura de la barra de colores a la altura de la subtrama sería usar mpl_toolkits.axes_grid1.inset_locator.InsetPosition
para establecer los ejes de la barra de colores en relación con los ejes de la subtrama de la imagen.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
fig, (ax, ax2, cax) = plt.subplots(ncols=3,figsize=(7,3),
gridspec_kw={"width_ratios":[1,1, 0.05]})
fig.subplots_adjust(wspace=0.3)
im = ax.imshow(np.random.rand(11,16), vmin=0, vmax=1)
im2 = ax2.imshow(np.random.rand(11,16), vmin=0, vmax=1)
ax.set_ylabel("y label")
ip = InsetPosition(ax2, [1.05,0,0.05,1])
cax.set_axes_locator(ip)
fig.colorbar(im, cax=cax, ax=[ax,ax2])
plt.show()
Esta solución no requiere un ajuste manual de las ubicaciones de los ejes o el tamaño de la barra de colores, funciona con diseños de varias filas y de una sola fila, y funciona con tight_layout()
. Está adaptado de un ejemplo de galería , utilizando ImageGrid
de matplotlib AxesGrid Toolbox .
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
# Set up figure and image grid
fig = plt.figure(figsize=(9.75, 3))
grid = ImageGrid(fig, 111, # as in plt.subplot(111)
nrows_ncols=(1,3),
axes_pad=0.15,
share_all=True,
cbar_location="right",
cbar_mode="single",
cbar_size="7%",
cbar_pad=0.15,
)
# Add data to image grid
for ax in grid:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
# Colorbar
ax.cax.colorbar(im)
ax.cax.toggle_label(True)
#plt.tight_layout() # Works, but may still require rect paramater to keep colorbar labels visible
plt.show()
La solución de usar una lista de ejes de abevieiramota funciona muy bien hasta que use solo una fila de imágenes, como se señala en los comentarios. Usar una relación de aspecto razonable para figsize
ayuda, pero todavía está lejos de ser perfecto. Por ejemplo:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(9.75, 3))
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()
La función de barra de color proporciona el parámetro de reducción que es un factor de escala para el tamaño de los ejes de la barra de colores. Requiere cierto ensayo y error manual. Por ejemplo:
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.75)
Puede simplificar el código de Joe Kington utilizando el parámetro ax
de figure.colorbar()
con una lista de ejes. De la documentación :
hacha
Ninguno | objeto (s) padre (s) a partir del cual se robará el espacio para un nuevo eje de barra de colores. Si se da una lista de ejes, todos se redimensionarán para dejar espacio para los ejes de la barra de colores.
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.colorbar(im, ax=axes.ravel().tolist())
plt.show()
Simplemente coloque la barra de colores en su propio eje y use subplots_adjust
para dejar espacio para ello.
Como un ejemplo rápido:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)
plt.show()
Usar make_axes
es aún más fácil y da un mejor resultado. También brinda la posibilidad de personalizar el posicionamiento de la barra de colores. También tenga en cuenta la opción de subplots
para compartir los ejes xey.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True)
for ax in axes.flat:
im = ax.imshow(np.random.random((10,10)), vmin=0, vmax=1)
cax,kw = mpl.colorbar.make_axes([ax for ax in axes.flat])
plt.colorbar(im, cax=cax, **kw)
plt.show()