293 votes

Matplotlib tight_layout() ne prend pas en compte le suptitle de la figure

Si j'ajoute un sous-titre à ma figure matplotlib, il est recouvert par les titres des sous-plots. Quelqu'un sait-il comment s'occuper facilement de cela ? J'ai essayé le tight_layout() mais cela ne fait qu'empirer les choses.

Exemple :

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()

283voto

soupault Points 1470

Vous pouvez ajuster la géométrie du sous-plot dans le très tight_layout appeler comme suit :

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

Comme il est indiqué dans la documentation ( https://matplotlib.org/users/tight_layout_guide.html ) :

tight_layout() ne prend en compte que les ticklabels, les étiquettes d'axe et les titres. Ainsi, d'autres artistes peuvent être coupés et peuvent également se chevaucher.

1 votes

Cela a fonctionné comme un charme, mais comment le changement se produit-il lorsque l'on spécifie la boîte de délimitation ? J'ai lu la documentation mais je n'ai pas compris grand chose. Ce serait formidable si vous pouviez m'expliquer ! Merci.

5 votes

Pourriez-vous expliquer la signification de chaque chiffre en rectangle ? Je ne les ai pas vus dans la documentation.

13 votes

@steven see tight_layout docs : [left, bottom, right, top] in normalized (0, 1) figure coordinates

130voto

unutbu Points 222216

Vous pouvez ajuster manuellement l'espacement en utilisant 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()

1 votes

Merci. Je suppose que c'est la meilleure façon de procéder.

5 votes

Notez que la valeur par défaut pour top est de 0,9 lorsque vous appelez subplots_adjust

104 votes

Je ne peux pas croire qu'en l'an 2017 de notre seigneur ce soit encore un bug de matplotlib.

66voto

aseagram Points 123

Une chose que vous pourriez changer dans votre code très facilement est la fonction fontsize que vous utilisez pour les titres. Cependant, je vais supposer que vous ne voulez pas seulement faire cela !

Quelques alternatives à l'utilisation de fig.subplots_adjust(top=0.85) :

Habituellement tight_layout() fait un bon travail en plaçant tout au bon endroit pour qu'il n'y ait pas de chevauchement. La raison tight_layout() n'aide pas dans ce cas, c'est parce que tight_layout() ne prend pas en compte fig.suptitle(). Il existe un problème ouvert à ce sujet sur GitHub : https://github.com/matplotlib/matplotlib/issues/829 [fermé en 2014 en raison du besoin d'un gestionnaire de la géométrie complète - transféré vers https://github.com/matplotlib/matplotlib/issues/1109 ].

Si vous lisez le fil de discussion, il y a une solution à votre problème impliquant GridSpec . La clé est de laisser un peu d'espace en haut de la figure lors de l'appel. tight_layout en utilisant le rect kwarg. Pour votre problème, le code devient :

Utilisation de 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()

Le résultat :

using gridspec

Quizás GridSpec est un peu exagéré pour vous, ou votre problème réel implique beaucoup plus d'intrigues secondaires sur une toile beaucoup plus grande, ou d'autres complications. Une astuce simple consiste à utiliser annotate() et verrouiller les coordonnées à la 'figure fraction' pour imiter un suptitle . Vous devrez peut-être procéder à des ajustements plus fins une fois que vous aurez examiné le résultat. Notez que cette deuxième solution ne no utiliser tight_layout() .

Une solution plus simple (bien qu'il puisse être nécessaire de l'affiner)

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()

Le résultat :

simple

[Utilisation Python 2.7.3 (64 bits) et matplotlib 1.2.0]

24voto

bhayley Points 151

La mise en page serrée ne fonctionne pas avec suptitle, mais constrained_layout fait. Voir cette question Améliorer la taille et l'espacement des sous-plots avec de nombreux sous-plots dans matplotlib

J'ai trouvé qu'ajouter les intrigues secondaires en une seule fois était mieux, par exemple.

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

# then iterating over the axes to fill in the plots

Mais il peut aussi être ajouté au moment où la figure est créée :

fig = plt.figure(constrained_layout=True)

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

Note : Pour rendre mes intrigues secondaires plus proches, j'ai aussi utilisé

fig.subplots_adjust(wspace=0.05)

et constrained_layout ne fonctionne pas avec cela :(

21voto

Puggie Points 1942

Une solution alternative et simple à utiliser est d'ajuster les coordonnées du texte du suptitle dans la figure en utilisant l'argument y dans l'appel de suptitle (voir l'onglet docs ) :

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()

12 votes

C'est une excellente solution dans un notebook mais la commande plt.savefig() n'obéit pas. Cette solution coupe toujours le titre dans une commande savefig.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X