233 votes

xlabel/ylabel commun pour les sous-plans de matplotlib

J'ai la parcelle suivante :

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

et j'aimerais maintenant donner à ce graphique des étiquettes communes pour l'axe des x et l'axe des y. Par "commun", j'entends qu'il devrait y avoir une grande étiquette d'axe des x sous toute la grille des sous-plans, et une grande étiquette d'axe des y à droite. Je ne trouve rien à ce sujet dans la documentation de plt.subplots et mes recherches sur Google suggèrent que je dois faire un gros plt.subplot(111) pour commencer - mais comment puis-je ensuite y placer mes 5*2 sous-intrigues en utilisant plt.subplots ?

8voto

EL_DON Points 608

L'apparence sera meilleure si vous réservez de l'espace pour les étiquettes communes en créant des étiquettes invisibles pour la sous-intrigue dans le coin inférieur gauche. Il est également bon de passer la taille de la police à partir de rcParams. De cette façon, les étiquettes communes changeront de taille avec votre configuration rc, et les axes seront également ajustés pour laisser de la place aux étiquettes communes.

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'])

enter image description here enter image description here

6voto

Luke Davis Points 840

Mise à jour :

Cette fonctionnalité fait désormais partie de la paquet proplot matplotlib que j'ai récemment publié sur pypi. Par défaut, lorsque vous faites des figures, les étiquettes sont "partagées" entre les sous-plans.


Réponse originale :

J'ai découvert une méthode plus robuste :

Si vous connaissez le bottom y top kwargs qui sont entrés dans un GridSpec l'initialisation, ou vous connaissez autrement les positions des bords de vos axes en Figure vous pouvez également spécifier la position de l'étiquette y en coordonnées Figure des coordonnées avec une certaine magie de "transformation".

Par exemple :

import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
bottom, top = 0.1, 0.9
fig, axs = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = 0.5 * (bottom + top)
transform = mtransforms.blended_transform_factory(mtransforms.IdentityTransform(), fig.transFigure)  # specify x, y transform
axs[0].yaxis.label.set_transform(transform)  # changed from default blend (IdentityTransform(), axs[0].transAxes)
axs[0].yaxis.label.set_position((0, avepos))
axs[0].set_ylabel('Hello, world!')

...et vous devriez voir que l'étiquette s'ajuste toujours correctement de gauche à droite pour éviter de chevaucher d'autres étiquettes, comme d'habitude, mais qu'elle se positionne aussi exactement entre les sous-plans souhaités.

Notamment, si vous omettez le set_position l'étiquette y apparaît exactement au milieu de la figure. Je suppose que c'est parce que lorsque l'étiquette est finalement dessinée, matplotlib utilise 0,5 pour le y sans vérifier si la transformation des coordonnées sous-jacentes a changé.

3voto

CPe Points 31

J'ai rencontré un problème similaire en traçant une grille de graphiques. Les graphiques étaient composés de deux parties (haut et bas). L'étiquette y était censée être centrée sur les deux parties.

Je ne voulais pas utiliser une solution qui dépend de la connaissance de la position dans la figure extérieure (comme fig.text()), donc j'ai manipulé la position y de la fonction set_ylabel(). Elle est généralement de 0,5, le milieu du graphique auquel elle est ajoutée. Comme le remplissage entre les parties (hspace) dans mon code était nul, je pouvais calculer le milieu des deux parties par rapport à la partie supérieure.

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

photo

Inconvénients :

  • La distance horizontale par rapport au tracé est basée sur la partie supérieure, les ticks du bas pouvant s'étendre dans l'étiquette.

  • La formule ne tient pas compte de l'espace entre les pièces.

  • Lance une exception lorsque la hauteur de la partie supérieure est égale à 0.

Il existe probablement une solution générale qui prend en compte le remplissage entre les chiffres.

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