web-dev-qa-db-fra.com

Xlabel / ylabel commun pour les sous-parcelles matplotlib

J'ai l'intrigue suivante:

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

et maintenant je voudrais donner à ce graphique des étiquettes communes en abscisses et en ordonnées. Par "commun", je veux dire qu’il devrait y avoir une grande étiquette en abscisse sous toute la grille de sous-parcelles et une grande étiquette en ordonnée à droite. Je ne trouve rien à ce sujet dans la documentation de plt.subplots, et mes recherches suggèrent que je dois créer une grande plt.subplot(111) pour commencer - mais comment puis-je y insérer mes intrigues secondaires 5 * 2 en utilisant plt.subplots?

105
jolindbe

Cela ressemble à ce que vous voulez réellement. Il applique la même approche de cette réponse à votre cas spécifique:

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

Multiple plots with common axes label

164
divenex

Sans sharex=True, sharey=True vous obtenez:

enter image description here

Avec cela, vous devriez le rendre plus agréable:

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

enter image description here

Toutefois, si vous souhaitez ajouter des étiquettes supplémentaires, vous devez les ajouter uniquement aux graphiques Edge:

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

enter image description here

Ajouter une étiquette pour chaque tracé la gâcherait (il existe peut-être un moyen de détecter automatiquement les étiquettes répétées, mais je ne suis pas au courant de l'existence d'une étiquette).

33
Piotr Migdal

Puisque je le considère pertinent et assez élégant (pas besoin de spécifier des coordonnées pour placer du texte), je copie (avec une légère adaptation) ne réponse à une autre question connexe .

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

Cela se traduit par (avec la version 2.2.0 de matplotlib):

5 rows and 2 columns subplots with common x and y axis labels

21
bli

Depuis la commande:

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

vous avez utilisé retourne un tuple composé de la figure et d'une liste des occurrences d'axes, il suffit déjà de faire quelque chose comme (attention, j'ai changé fig,ax en 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 vous souhaitez modifier des détails sur une sous-parcelle spécifique, vous pouvez y accéder via axes[i]i effectue une itération sur vos sous-parcelles.

Il pourrait également être très utile d’inclure un

fig.tight_layout()

à la fin du fichier, avant la plt.show(), afin d’éviter le chevauchement des libellés.

14
Marius

J'ai rencontré un problème similaire en traçant une grille de graphiques. Les graphiques consistaient en deux parties (haut et bas). Le label y était censé être centré sur les deux parties.

Je ne voulais pas utiliser une solution qui dépend de la position de la figure extérieure (comme fig.text ()), j'ai donc manipulé la position y de la fonction set_ylabel (). Il est généralement égal à 0,5, le milieu de la parcelle auquel il est ajouté. 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()

image

Inconvénients:

  • La distance horizontale par rapport au tracé est basée sur la partie supérieure, les graduations inférieures peuvent s'étendre dans l'étiquette.

  • La formule ne prend pas en compte l'espace entre les parties.

  • Lève une exception lorsque la hauteur de la partie supérieure est 0.

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

2
CPe

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

Si vous connaissez les bottom et top kwargs ayant initialisé une initialisation GridSpec, ou si vous connaissez les positions des arêtes de vos axes dans coordonnées Figure =, vous pouvez également spécifier la position de ylabel dans les coordonnées Figure avec une certaine magie de "transformation" sophistiquée. Par exemple:

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

... et vous devriez voir que l'étiquette ajuste toujours de manière appropriée gauche-droite pour éviter de se chevaucher avec les étiquettes de contrôle, comme d'habitude - mais maintenant, elle s'ajustera pour être toujours exactement entre les deux les intrigues secondaires souhaitées.

De plus, si vous n'utilisez même pas set_position, le ylabel apparaîtra par défaut exactement au milieu du chiffre. J'imagine que c'est parce que lorsque l'étiquette est finalement dessinée, matplotlib utilise 0,5 pour la coordonnée y- sans vérifier si la transformation des coordonnées sous-jacentes a été modifiée.

2
Luke Davis

Cela semblera mieux si vous réservez de l'espace pour les étiquettes communes en créant des étiquettes invisibles pour la sous-parcelle dans le coin inférieur gauche. Il est également bon de passer à la taille de fonte 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 hereenter image description here

1
EL_DON