Как получить постоянное расстояние между легендой и осями даже при изменении размера фигуры? - PullRequest
1 голос
/ 17 марта 2019

При размещении легенды за пределами осей с использованием bbox_to_anchor, как в в этом ответе, пространство между осями и легендой изменяется при изменении размера фигуры.Для статических экспортированных графиков это хорошо;Вы можете просто подправить цифры, пока не поймете это правильно.Но для интерактивных графиков, которые вы можете изменить, это проблема.Как видно из этого примера:

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

Результат:

legend placement ruined by resize

Как я могу убедиться, что легенда сохраняетНа таком же расстоянии от осей, даже когда фигура изменяется?

Ответы [ 2 ]

1 голос
/ 17 марта 2019

Расстояние легенды от края ограничительной рамки задается аргументом borderaxespad. borderaxespad в единицах, кратных размеру шрифта, что делает его автоматически независимым от размера осей. Так что в этом случае

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


Аналогичный вопрос об отображении заголовка под осями на постоянном расстоянии задается в Поместить заголовок внизу рисунка осей?

1 голос
/ 17 марта 2019

Вы можете использовать события изменения размера холста для обновления значений в bbox_to_anchor при каждом обновлении.Для вычисления новых значений вы можете использовать обратное преобразование осей (Bbox.inverse_transformed(ax.transAxes)), которое преобразуется из координат экрана в пикселях в координаты осей, которые обычно используются в bbox_to_anchor.

. Вот примерс поддержкой размещения легенды на всех четырех сторонах осей:

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

Результат:

legend spacing still good after resize

...