tight_layout() no tiene en cuenta el título de la figura

7 minutos de lectura

avatar de usuario de katrasnikj
katrasnikj

Si agrego un subtítulo a mi figura matplotlib, se superpone con los títulos de la trama secundaria. ¿Alguien sabe cómo solucionarlo fácilmente? Probé el tight_layout() funcionar, pero solo empeora las cosas.

Ejemplo:

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.tight_layout()
plt.show()

avatar de usuario de Soupault
Sopa de sopa

Puede ajustar la geometría de la subparcela en el mismo tight_layout llamar de la siguiente manera:

fig.tight_layout(rect=[0, 0.03, 1, 0.95])

Como se indica en la documentación (https://matplotlib.org/users/tight_layout_guide.html):

tight_layout() solo considera ticklabels, etiquetas de eje y títulos. Por lo tanto, otros artistas pueden quedar recortados y también pueden superponerse.

  • Funcionó de maravilla, pero ¿cómo ocurre el cambio al especificar el cuadro delimitador? Leí el documento pero no me quedó muy claro. ¡Sería genial si pudieras explicarme! Gracias.

    – thepunitsingh

    24 de noviembre de 2017 a las 13:22

  • ¿podría explicar el significado de cada número en rect? No los vi en el documento.

    – steven

    13 mayo 2019 a las 21:41

  • @steven ver tight_layout documentos: [left, bottom, right, top] in normalized (0, 1) figure coordinates

    – sopa de sopa

    14 de mayo de 2019 a las 6:58

  • Puede aumentar aún más el espacio entre el subtítulo y los ejes bajando el valor más a la derecha: tight_layout(rect=[0, 0.03, 1, 0.9]) en vez de 0.95 como en la respuesta.

    – ComFreek

    24 de septiembre de 2019 a las 9:17


  • No funcionó dentro de jupyter. Probé diferentes valores para los números, no tuvo ningún efecto

    – Aleksejs Fomins

    23 de enero de 2020 a las 14:22

Puede ajustar manualmente el espaciado usando plt.subplots_adjust(top=0.85):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.subplots_adjust(top=0.85)
plt.show()

  • No puedo creer que en el año 2017 de nuestro señor, esto siga siendo un error de matplotlib.

    – palabras para el sabio

    13 de enero de 2017 a las 23:28

  • Tenga en cuenta que subplots_adjust debe ser llamado después cualquier llamada a tight_layouto no habrá efecto de llamar subplots_adjust.

    – Achal Dave

    20 de enero de 2017 a las 18:58

  • @wordsforthewise haz ese 2018 ahora

    – vlsd

    16 de marzo de 2018 a las 17:23

  • @vlsd Hola desde 2019

    – Xiaoyu Lu

    23 de septiembre de 2019 a las 4:02

  • 2020 aquí y esto sigue siendo un problema

    – Tomás Tiotto

    21 de junio de 2020 a las 8:38

avatar de usuario de aseagram
aseagrama

Una cosa que podrías cambiar en tu código muy fácilmente es el fontsize está utilizando para los títulos. Sin embargo, voy a asumir que no solo quieres hacer eso.

Algunas alternativas al uso fig.subplots_adjust(top=0.85):

Normalmente tight_layout() hace un trabajo bastante bueno al colocar todo en buenas ubicaciones para que no se superpongan. La razón tight_layout() no ayuda en este caso es porque tight_layout() no tiene en cuenta fig.suptitle(). Hay un problema abierto sobre esto en GitHub: https://github.com/matplotlib/matplotlib/issues/829 [closed in 2014 due to requiring a full geometry manager – shifted to https://github.com/matplotlib/matplotlib/issues/1109 ].

Si lees el hilo, hay una solución a tu problema que implica GridSpec. La clave es dejar algo de espacio en la parte superior de la figura al llamar tight_layoututilizando el rect kwarg Para su problema, el código se convierte en:

Usando GridSpec

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

f = np.random.random(100)
g = np.random.random(100)

fig = plt.figure(1)
gs1 = gridspec.GridSpec(1, 2)
ax_list = [fig.add_subplot(ss) for ss in gs1]

ax_list[0].plot(f)
ax_list[0].set_title('Very Long Title 1', fontsize=20)

ax_list[1].plot(g)
ax_list[1].set_title('Very Long Title 2', fontsize=20)

fig.suptitle('Long Suptitle', fontsize=24)    

gs1.tight_layout(fig, rect=[0, 0.03, 1, 0.95])  

plt.show()

El resultado:

usando gridspec

Quizás GridSpec es un poco exagerado para usted, o su problema real involucrará muchas más tramas secundarias en un lienzo mucho más grande u otras complicaciones. Un truco simple es simplemente usar annotate() y bloquear las coordenadas a la 'figure fraction' imitar un suptitle. Sin embargo, es posible que deba hacer algunos ajustes más finos una vez que eche un vistazo a la salida. Tenga en cuenta que esta segunda solución no no usar tight_layout().

Solución más simple (aunque es posible que deba ajustarse)

fig = plt.figure(2)

ax1 = plt.subplot(121)
ax1.plot(f)
ax1.set_title('Very Long Title 1', fontsize=20)

ax2 = plt.subplot(122)
ax2.plot(g)
ax2.set_title('Very Long Title 2', fontsize=20)

# fig.suptitle('Long Suptitle', fontsize=24)
# Instead, do a hack by annotating the first axes with the desired 
# string and set the positioning to 'figure fraction'.
fig.get_axes()[0].annotate('Long Suptitle', (0.5, 0.95), 
                            xycoords="figure fraction", ha="center", 
                            fontsize=24
                            )
plt.show()

El resultado:

simple

[Using Python 2.7.3 (64-bit) and matplotlib 1.2.0]

  • Gracias, si uso la primera solución que sugirió (usando GridSpec) ¿Cómo puedo crear subparcelas que comparten ejes? yo suelo usar plt.subplots(N,1, sharex=<>, sharey=<>) al crear subparcelas, pero el código que publicaste usa add_subplot en cambio

    – Amelio Vázquez-Reina

    19/03/2014 a las 15:55


  • Fundición fig.tight_layout(rect=[0, 0.03, 1, 0.95]) también funciona

    – sopa de sopa

    16 de septiembre de 2015 a las 13:06

  • La segunda solución es la única que funcionó para mí. ¡Gracias!

    – Hagbard

    9 de marzo de 2020 a las 13:26

avatar de usuario de bhayley
bhayley

El diseño ajustado no funciona con subtítulos, pero constrained_layout lo hace. Consulte esta pregunta Mejore el tamaño/espaciado de subparcelas con muchas subparcelas en matplotlib

Encontré que agregar las subtramas a la vez se veía mejor, es decir

fig, axs = plt.subplots(rows, cols, constrained_layout=True)

# then iterating over the axes to fill in the plots

Pero también se puede agregar en el punto en que se crea la figura:

fig = plt.figure(constrained_layout=True)

ax1 = fig.add_subplot(cols, rows, 1)
# etc

Nota: para hacer que mis subparcelas estén más juntas, también estaba usando

fig.subplots_adjust(wspace=0.05)

y constrained_layout no funciona con esto 🙁

Una solución alternativa y fácil de usar es ajustar las coordenadas del texto del subtítulo en la figura usando el argumento y en la llamada del subtítulo (ver el documentos):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', y=1.05, fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.show()

  • Esta es una gran manera en un cuaderno pero el comando plt.savefig() no obedece. Esta solución aún corta el título en un comando savefig.

    – superhéroe

    23 de junio de 2016 a las 10:17

Avatar de usuario de Herman Schaaf
herman schaaf

Como mencionaron otros, por defecto, el diseño ajustado no tiene en cuenta los subtítulos. Sin embargo, he encontrado que es posible usar el bbox_extra_artists argumento para pasar el subtítulo como un cuadro delimitador que debe tenerse en cuenta:

st = fig.suptitle("My Super Title")
plt.savefig("figure.png", bbox_extra_artists=[st], bbox_inches="tight")

Esto obliga al cálculo de diseño ajustado a tomar la suptitle en cuenta, y se ve como era de esperar.

  • Esta es una gran manera en un cuaderno pero el comando plt.savefig() no obedece. Esta solución aún corta el título en un comando savefig.

    – superhéroe

    23 de junio de 2016 a las 10:17

avatar de usuario de aboger
aboger

Este sitio web tiene una solución simple para esto con un ejemplo que funcionó para mí. La línea de código que deja espacio para el título es la siguiente:

plt.tight_layout(rect=[0, 0, 1, 0.95]) 

Aquí hay una imagen de prueba de que funcionó para mí:
Enlace de imágen

¿Ha sido útil esta solución?