web-dev-qa-db-fra.com

Comment positionner et aligner une légende de figure matplotlib?

J'ai un chiffre avec deux sous-intrigues en 2 lignes et 1 colonne. Je peux ajouter une légende de belle figure avec

fig.legend((l1, l2), ['2011', '2012'], loc="lower center", 
           ncol=2, fancybox=True, shadow=True, prop={'size':'small'})

Cependant, cette légende est positionnée au centre du figure et non en dessous du centre du axes comme je voudrais l'avoir. Maintenant, je peux obtenir mes coordonnées d'axes avec

axbox = ax[1].get_position()

et en théorie je devrais pouvoir positionner la légende en spécifiant le mot-clé loc avec un Tuple:

fig.legend(..., loc=(axbox.x0+0.5*axbox.width, axbox.y0-0.08), ...)

Cela fonctionne, sauf que la légende est alignée à gauche de sorte que loc spécifie le bord/coin gauche de la boîte de légende et non le centre. J'ai recherché des mots clés tels que align , alignement horizontal , etc., mais n'a pas pu en trouver. J'ai également essayé d'obtenir la "position de légende", mais la légende n'a pas de méthode * get_position () *. J'ai lu à propos de * bbox_to_anchor * mais je ne peux pas le comprendre lorsqu'il est appliqué à une légende de figure. Cela semble être fait pour les légendes des haches.

Ou: dois-je utiliser une légende d'axes décalés à la place? Mais alors, pourquoi y a-t-il des légendes des figures en premier lieu? Et d'une manière ou d'une autre, il doit être possible d'aligner au centre une légende de figure, car loc = "centre inférieur" le fait aussi.

Merci pour toute aide,

Martin

20
maschu

Dans ce cas, vous pouvez utiliser des axes pour les méthodes figure legend. Dans tous les cas, bbox_to_anchor C'est la clé. Comme vous l'avez déjà remarqué bbox_to_anchor spécifie un tuple de coordonnées (ou une boîte) sur lequel placer la légende. Lorsque vous utilisez bbox_to_anchor pensez au location kwarg comme contrôlant l'alignement horizontal et vertical.

La différence est simplement de savoir si le tuple de coordonnées est interprété comme des axes ou des coordonnées de figures.

Comme exemple d'utilisation d'une légende de figure:

import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)

x = np.linspace(0, np.pi, 100)

line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0.5
# in figure coordinates.
# "center" is basically saying center horizontal alignment and 
# center vertical alignment in this case
fig.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0.5], 
           loc='center', ncol=2)

plt.show()

enter image description here

Comme exemple d'utilisation de la méthode des axes, essayez quelque chose comme ceci:

import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)

x = np.linspace(0, np.pi, 100)

line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

# The key to the position is bbox_to_anchor: Place it at x=0.5, y=0
# in axes coordinates.
# "upper center" is basically saying center horizontal alignment and 
# top vertical alignment in this case
ax1.legend([line1, line2], ['yep', 'nope'], bbox_to_anchor=[0.5, 0], 
           loc='upper center', ncol=2, borderaxespad=0.25)

plt.show()

enter image description here

27
Joe Kington

C'est une très bonne question et la réponse acceptée indique la clé (c'est-à-dire loc désigne l'alignement et bbox_to_anchor Indique la position). J'ai également essayé quelques codes et je voudrais souligner l'importance de la propriété bbox_transform cela peut parfois être explicitement spécifié pour obtenir les effets souhaités. Ci-dessous, je vais vous montrer mes résultats sur fig.legend. ax.legend Devrait être très similaire à loc et bbox_to_anchor Fonctionne de la même manière.

Lorsque vous utilisez le paramètre par défaut, nous aurons les éléments suivants.

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,4), sharex=True)

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

enter image description here

C'est fondamentalement satisfaisant. Mais il pourrait être facilement trouvé que la légende se superpose avec les graduations de l'axe des x de ax2. C'est le problème qui deviendra encore plus grave lorsque figsize et/ou dpi de la figure change, voir ce qui suit.

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

enter image description here

Vous voyez donc qu'il y a de grands écarts entre ax2 Et la légende. Ce n'est pas ce que nous voulons. Comme le questionneur, nous aimerions contrôler manuellement l'emplacement de la légende. Tout d'abord, j'utiliserai le style à 2 chiffres de bbox_to_anchor Comme l'a fait la réponse.

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point of the legend specified by loc at the position specified by bbox_to_anchor.
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
           bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05])

enter image description here

Presque là! Mais c'est totalement faux car le centre de la légende n'est pas au centre de ce que nous voulons vraiment dire! La clé pour résoudre ce problème est que nous devons informer explicitement le bbox_transform En tant que fig.transFigure. Par défaut None, la transformation transAxes des Axes sera utilisée . Ceci est compréhensible car la plupart du temps nous utiliserons ax.legend().

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point of the legend specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
           bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05], bbox_transform=fig.transFigure)

enter image description here

Comme alternative, nous pouvons également utiliser un style à 4 chiffres bbox_to_anchor Pour loc. Il s'agit essentiellement de spécifier une vraie boîte pour la légende et loc indique vraiment l'alignement! La valeur par défaut bbox_to_anchor Devrait simplement être [0,0,1,1], Ce qui signifie la totalité de la boîte à chiffres! Les quatre nombres représentent respectivement x0,y0,width,height. C'est très similaire à en spécifiant un cax pour une barre de couleurs partagée ! Par conséquent, vous pouvez facilement modifier le y0 Juste un peu plus bas que axbox.y0 Et ajuster loc en conséquence.

fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})

x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')

axbox = ax2.get_position()

# to place center point specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2,
           bbox_to_anchor=[0, axbox.y0-0.05,1,1], bbox_transform=fig.transFigure)

enter image description here

0
Fei Yao