web-dev-qa-db-fra.com

Matplotlib tight_layout () ne prend pas en compte la figure

Si j'ajoute un sous-titre à ma figure matplotlib, il est superposé aux titres de la sous-parcelle. Est-ce que quelqu'un sait comment s'en occuper facilement? J'ai essayé la fonction 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()
144
katrasnikj

Vous pouvez ajuster la géométrie de la sous-parcelle dans l'appel même tight_layout Comme suit:

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

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

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

P.S. La communauté m'a recommandé de poster mon commentaire comme réponse.

120
soupault

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

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

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

Habituellement, tight_layout() fait un très bon travail en plaçant tout dans des emplacements appropriés afin d'éviter tout chevauchement. La raison pour laquelle tight_layout() n'aide pas dans ce cas est que tight_layout() ne prend pas en compte fig.suptitle (). Il y a un problème non résolu à ce sujet sur GitHub: https://github.com/matplotlib/matplotlib/issues/829 [fermé en 2014 en raison de la nécessité de disposer d'un gestionnaire de géométrie complet - déplacé vers https://github.com/matplotlib/matplotlib/issues/1109 ].

Si vous lisez le fil de discussion, il existe une solution à votre problème impliquant GridSpec. La clé est de laisser un peu d’espace en haut de la figure lorsque vous appelez 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

Peut-être que GridSpec est un peu excessif pour vous, ou votre vrai problème impliquera beaucoup plus de sous-graphes sur une toile beaucoup plus grande, ou d’autres complications. Un simple hack consiste simplement à utiliser annotate() et à verrouiller les coordonnées sur le 'figure fraction' Pour imiter un suptitle. Vous devrez peut-être faire des ajustements plus fins une fois que vous jetterez un œil à la sortie, cependant. Notez que cette seconde solution n'utilise pas tight_layout().

Solution plus simple (bien qu'il faille éventuellement l'ajuster)

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 de Python 2.7.3 (64 bits) et de matplotlib 1.2.0]

49
aseagram

Une solution alternative simple à utiliser consiste à ajuster les coordonnées du texte du sous-titre dans la figure en utilisant l’argument y dans l’appel de sous-titre (voir le 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()
18
Puggie

J'ai eu du mal à utiliser les méthodes de rognage de matplotlib, je viens donc de créer une fonction pour le faire via un appel de bash à ImageMagickcommande mogrify , qui fonctionne bien et obtient tous les espaces blancs supplémentaires du bord de la figure. Pour cela, vous devez utiliser UNIX/Linux, utiliser le shell bash et installer ImageMagick.

Il vous suffit de lancer un appel à cet appel après votre appel savefig().

def autocrop_img(filename):
    '''Call ImageMagick mogrify from bash to autocrop image'''
    import subprocess
    import os

    cwd, img_name = os.path.split(filename)

    bashcmd = 'mogrify -trim %s' % img_name
    process = subprocess.Popen(bashcmd.split(), stdout=subprocess.PIPE, cwd=cwd)
4
ryanjdillon

La disposition étroite ne fonctionne pas avec le sous-titre, mais constrained_layout Est-ce que. Voir cette question Améliorer la taille/l’espacement des sous-parcelles avec de nombreuses sous-parcelles dans matplotlib

J'ai trouvé que l'ajout des sous-parcelles à la fois avait l'air mieux, c'est-à-dire.

fig, axs = plt.subplots(rows, cols, contrained_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
2
bhayley

Comme mentionné par d'autres, par défaut, la mise en page étroite ne prend pas en compte les sous-titres. Cependant, j'ai trouvé qu'il est possible d'utiliser le bbox_extra_artists argument à passer dans le sous-titre en tant que boîte englobante à prendre en compte:

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

Cela oblige le calcul de mise en page serré à prendre en compte le suptitle et il se présente comme prévu.

1
Herman Schaaf

J'ai eu un problème similaire qui est apparu lors de l'utilisation de tight_layout pour une très grande grille de parcelles (plus de 200 sous-parcelles) et le rendu dans un cahier Jupyter. J'ai fait une solution rapide qui place toujours votre suptitle à une certaine distance au-dessus de votre intrigue secondaire supérieure:

import matplotlib.pyplot as plt

n_rows = 50
n_col = 4
fig, axs = plt.subplots(n_rows, n_cols)

#make plots ...

# define y position of suptitle to be ~20% of a row above the top row
y_title_pos = axs[0][0].get_position().get_points()[1][1]+(1/n_rows)*0.2
fig.suptitle('My Sup Title', y=y_title_pos)

Pour les sous-parcelles de taille variable, vous pouvez toujours utiliser cette méthode pour obtenir le sommet de la sous-parcelle la plus haute, puis définir manuellement un montant supplémentaire à ajouter au sous-titre.

0
Brian Pollack