python - pyplot - Xlabel/ylabel común para las subparcelas de matplotlib
plot title font size matplotlib (6)
Descubrí un método más robusto:
Si conoce los kwargs bottom
y top
que entraron en una inicialización de GridSpec
, o si conoce las posiciones de los bordes de sus ejes en las coordenadas de la Figure
, también puede especificar la posición ylabel en las coordenadas de la Figure
con alguna fantasía de "transformación". Por ejemplo:
import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
)) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel(''Hello, world!'')
... y debería ver que la etiqueta todavía se ajusta apropiadamente de izquierda a derecha para evitar solaparse con etiquetas de tictac, como lo hace normalmente, pero ahora se ajustará para estar siempre exactamente entre las subtramas deseadas.
Además, si ni siquiera usa set_position
, el ylabel aparecerá por defecto exactamente a la mitad de la figura . Supongo que esto se debe a que cuando finalmente se dibuja la etiqueta, matplotlib
usa 0.5 para la coordenada y
sin verificar si la transformación de coordenadas subyacente ha cambiado.
Tengo la siguiente trama:
fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)
y ahora me gustaría darle a esta trama etiquetas de eje x comunes y etiquetas de eje y. Con "común", quiero decir que debería haber una etiqueta de eje x grande debajo de toda la grilla de subtramas, y una etiqueta grande de eje y a la derecha. No puedo encontrar nada al respecto en la documentación de plt.subplots
, y mis plt.subplots
Google sugieren que tengo que hacer un gran plt.subplot(111)
para empezar, pero ¿cómo puedo poner mis subtramas 5 * 2 en ese usando plt.subplots
?
Desde el comando:
fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)
Usaste devuelve una tupla que consiste en la figura y una lista de las instancias de los ejes, ya es suficiente para hacer algo como (tenga en cuenta que he cambiado la fig,ax
a la fig,axes
):
fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)
for ax in axes:
ax.set_xlabel(''Common x-label'')
ax.set_ylabel(''Common y-label'')
Si desea cambiar algunos detalles de una subtrama específica, puede acceder a ella a través de los axes[i]
donde itere sobre sus subtramas.
También podría ser muy útil incluir un
fig.tight_layout()
al final del archivo, antes de plt.show()
, para evitar la superposición de etiquetas.
Esto se ve como lo que realmente quieres. Aplica el mismo enfoque de esta respuesta a su caso específico:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))
fig.text(0.5, 0.04, ''common X'', ha=''center'')
fig.text(0.04, 0.5, ''common Y'', va=''center'', rotation=''vertical'')
Me encontré con un problema similar al trazar una grilla de gráficos. Los gráficos consistían en dos partes (superior e inferior). Se suponía que la etiqueta y estaba centrada en ambas partes.
No quería usar una solución que depende de conocer la posición en la figura externa (como fig.text ()), así que manipulé la posición y de la función set_ylabel (). Por lo general, es 0.5, en el medio de la trama se agrega. Como el relleno entre las partes (hspace) en mi código era cero, pude calcular el centro de las dos partes con respecto a la parte superior.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)
# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])
# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)
# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel(''shared label'', y = mid)
plt.show()
Desventajas:
La distancia horizontal a la trama se basa en la parte superior, las marcas inferiores se pueden extender a la etiqueta.
La fórmula no toma espacio entre las partes en cuenta.
Lanza una excepción cuando la altura de la parte superior es 0.
Probablemente haya una solución general que tenga en cuenta el relleno entre figuras.
Se verá mejor si reserva espacio para las etiquetas comunes al hacer etiquetas invisibles para la subtrama en la esquina inferior izquierda. También es bueno pasar el tamaño de fuente de rcParams. De esta forma, las etiquetas comunes cambiarán de tamaño con su configuración de rc, y los ejes también se ajustarán para dejar espacio para las etiquetas comunes.
fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel(''.'', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel(''.'', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, ''common X'', va=''center'', ha=''center'', fontsize=rcParams[''axes.labelsize''])
fig.text(0.04, 0.5, ''common Y'', va=''center'', ha=''center'', rotation=''vertical'', fontsize=rcParams[''axes.labelsize''])
Sin sharex=True, sharey=True
, obtienes:
Con eso deberías hacerlo mejor:
fig, axes2d = plt.subplots(nrows=3, ncols=3,
sharex=True, sharey=True,
figsize=(6,6))
for i, row in enumerate(axes2d):
for j, cell in enumerate(row):
cell.imshow(np.random.rand(32,32))
plt.tight_layout()
Pero si desea agregar etiquetas adicionales, debe agregarlas solo a los gráficos de bordes:
fig, axes2d = plt.subplots(nrows=3, ncols=3,
sharex=True, sharey=True,
figsize=(6,6))
for i, row in enumerate(axes2d):
for j, cell in enumerate(row):
cell.imshow(np.random.rand(32,32))
if i == len(axes2d) - 1:
cell.set_xlabel("noise column: {0:d}".format(j + 1))
if j == 0:
cell.set_ylabel("noise row: {0:d}".format(i + 1))
plt.tight_layout()
Agregar etiquetas para cada parcela lo arruinaría (tal vez haya una forma de detectar automáticamente etiquetas repetidas, pero no conozco ninguna).