Lorsque vous placez la légende en dehors des axes en utilisant bbox_to_anchor comme dans cette réponse, l'espace entre les axes et le la légende change lorsque la figure est redimensionnée. Pour les tracés exportés statiques, c'est parfait ; vous pouvez simplement modifier les chiffres jusqu'à ce que vous obteniez les bons résultats. Mais pour les tracés interactifs que vous souhaiterez peut-être redimensionner, c'est un problème. Comme on peut le voir dans cet exemple :

import numpy as np
from matplotlib import pyplot as plt

x = np.arange(5)
y = np.random.randn(5)

fig, ax = plt.subplots(tight_layout=True)
ax.plot(x, y, label='data1')
ax.plot(x, y-1, label='data2')
legend = ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05), ncol=2)
plt.show()

Résultat:

legend placement ruined by resize

Comment puis-je m'assurer que la légende garde la même distance des axes même lorsque la figure est redimensionnée ?

1
freidrichen 17 mars 2019 à 14:32

2 réponses

Meilleure réponse

La distance d'une légende au bord de la boîte englobante est définie par l'argument borderaxespad. Le borderaxespad est en unités de multiples de la taille de la police - ce qui le rend automatiquement indépendant de la taille des axes. Donc dans ce cas,

import matplotlib.pyplot as plt
import numpy as np
x = np.arange(5)
y = np.random.randn(5)

fig, ax = plt.subplots(constrained_layout=True)
ax.plot(x, y, label='data1')
ax.plot(x, y-1, label='data2')
legend = ax.legend(loc="upper center", bbox_to_anchor=(0.5,0), borderaxespad=2)

plt.show()

enter image description here enter image description here


Une question similaire sur l'affichage d'un titre sous les axes à une distance constante est posée dans Placer le titre en bas de la figure d'un axe ?

1
ImportanceOfBeingErnest 19 mars 2019 à 12:02

Vous pouvez utiliser les événements de redimensionnement du canevas pour mettre à jour les valeurs dans bbox_to_anchor à chaque mise à jour. Pour calculer les nouvelles valeurs, vous pouvez utiliser l'inverse de la transformation des axes (Bbox.inverse_transformed(ax.transAxes)), qui se traduit par les coordonnées d'écran en pixels vers les coordonnées des axes qui sont normalement utilisées dans bbox_to_anchor.

Voici un exemple avec support pour mettre la légende sur les quatre côtés des axes :

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.transforms import Bbox


class FixedOutsideLegend:
    """A legend placed at a fixed offset (in pixels) from the axes."""

    def __init__(self, ax, location, pixel_offset, **kwargs):
        self._pixel_offset = pixel_offset

        self.location = location
        if location == 'right':
            self._loc = 'center left'
        elif location == 'left':
            self._loc = 'center right'
        elif location == 'upper':
            self._loc = 'lower center'
        elif location == 'lower':
            self._loc = 'upper center'
        else:
            raise ValueError('Unknown location: {}'.format(location))

        self.legend = ax.legend(
            loc=self._loc, bbox_to_anchor=self._get_bbox_to_anchor(), **kwargs)
        ax.figure.canvas.mpl_connect('resize_event', self.on_resize)

    def on_resize(self, event):
        self.legend.set_bbox_to_anchor(self._get_bbox_to_anchor())

    def _get_bbox_to_anchor(self):
        """
        Find the lengths in axes units that correspond to the specified
        pixel_offset.
        """
        screen_bbox = Bbox.from_bounds(
            0, 0, self._pixel_offset, self._pixel_offset)
        try:
            ax_bbox = screen_bbox.inverse_transformed(ax.transAxes)
        except np.linagl.LinAlgError:
            ax_width = 0
            ax_height = 0
        else:
            ax_width = ax_bbox.width
            ax_height = ax_bbox.height

        if self.location == 'right':
            return (1 + ax_width, 0.5)
        elif self.location == 'left':
            return (-ax_width, 0.5)
        elif self.location == 'upper':
            return (0.5, 1 + ax_height)
        elif self.location == 'lower':
            return (0.5, -ax_height)


x = np.arange(5)
y = np.random.randn(5)

fig, ax = plt.subplots(tight_layout=True)
ax.plot(x, y, label='data1')
ax.plot(x, y-1, label='data2')
legend = FixedOutsideLegend(ax, 'lower', 20, ncol=2)
plt.show()

Résultat:

legend spacing still good after resize

1
freidrichen 17 mars 2019 à 11:32