2025, Nov 04 18:02

Круговая диаграмма по центру точки данных в Matplotlib с inset_axes

Как центрировать круговую диаграмму на точке данных в Matplotlib: используем Axes.inset_axes с transform=transData и прямоугольник в координатах данных

Привязать круговую диаграмму к конкретной точке данных в графике Matplotlib кажется простым, пока не вмешается преобразование координат осей. Если добавить новые оси через нормализованные координаты фигуры, круговая диаграмма будет центрироваться относительно этих осей, а не исходной точки (x, y) в пространстве данных. Решение — размещать вставку в координатах данных, чтобы центр пирога совпадал с нужной точкой.

Постановка задачи

Цель: построить линейный график, отметить конкретную точку, создать маленькие оси и отрисовать круговую диаграмму так, чтобы она была строго по центру этой точки. Ниже — минимальный пример, который показывает, почему наивный подход не центрирует диаграмму в точке:

import matplotlib.pyplot as plt
import numpy as np

canvas, base_ax = plt.subplots(nrows=1, ncols=1, figsize=(6, 3))
base_ax.set_xlim(0, 16)
base_ax.set_ylim(8, 12)

# Построим синусоиду
xs = np.arange(0, 5 * np.pi, 0.1)
ys = np.sin(xs) + 10
base_ax.plot(xs, ys, color="blue")

# Отметим целевую точку данных x=12, y=10
base_ax.plot(12, 10, marker="o", color="red")

# Нормируем целевые координаты к диапазону [0, 1]
u_norm = (12 - 0) / (16 - 0)  # 0.75
v_norm = (10 - 8) / (12 - 8)   # 0.5

# Разместим небольшие оси через прямоугольник в нормализованных координатах фигуры
p_left = 0.75
p_bottom = 0.5
box_w = 0.1
box_h = 0.1
mini_ax = canvas.add_axes(rect=(p_left, p_bottom, box_w, box_h))

# Нарисуем круговую диаграмму
mini_ax.pie((0.2, 0.3, 0.5))

Почему пирог не оказывается по центру точки

Прямоугольник, передаваемый при создании новых осей, задаётся в нормализованной системе координат 0–1. Круговая диаграмма затем центрируется относительно этих маленьких осей, то есть в середине прямоугольника. Хотя значения left и bottom были вычислены из пределов данных и нормированы, сами оси не привязаны к координатам данных. В итоге диаграмма центрируется внутри мини-вставки, но вовсе не обязана совпадать с исходной точкой (12, 10) в пространстве данных.

Надёжный подход: располагайте вставку в координатах данных

Чтобы центрировать диаграмму на точке данных, новые оси нужно размещать в координатах данных. Вызов Axes.inset_axes с параметром transform=ax.transData позволяет интерпретировать прямоугольник в единицах данных. Вычтите половину желаемой ширины и высоты вокруг точки, чтобы вставка оказалась по центру, и затем нарисуйте диаграмму.

import matplotlib.pyplot as plt
import numpy as np

scene, host_ax = plt.subplots(nrows=1, ncols=1, figsize=(6, 3))
host_ax.set_xlim(0, 16)
host_ax.set_ylim(8, 12)

# Построим синусоиду
xv = np.arange(0, 5 * np.pi, 0.1)
yv = np.sin(xv) + 10
host_ax.plot(xv, yv, color="blue")

# Точка данных, в которой должен быть центр диаграммы
center_pt = (12, 10)
host_ax.plot(*center_pt, marker="o", color="red")

# Ширина и высота вставки в единицах данных
span_x = 2
span_y = 0.5

# Центрируем прямоугольник вставки на точке данных
rect_data = [center_pt[0] - span_x / 2, center_pt[1] - span_y / 2, span_x, span_y]

# Создаём вставку в координатах данных и рисуем диаграмму
child_ax = host_ax.inset_axes(rect_data, transform=host_ax.transData)
child_ax.pie((0.2, 0.3, 0.5))

Что происходит «под капотом»

Передавая в inset_axes параметр transform=ax.transData, вы заставляете интерпретировать прямоугольник напрямую в системе координат данных осей. Вычитая половину ширины и высоты, вы совмещаете центр прямоугольника с целевой точкой, и круговая диаграмма оказывается визуально точно там, где нужно.

Почему это важно

Когда нужно отметить на графике конкретную позицию круговой диаграммой, координаты данных избавляют от несоответствия между нормализованными прямоугольниками и реальными позициями в данных. Это также избавляет от ручной сборки секторов: встроенный метод pie проще, чем вручную описывать сегменты. В галерее есть пример «точечных» круговых диаграмм, иллюстрирующий похожую идею.

Выводы

Если нужно центрировать круговую диаграмму на определённой точке, создайте вставку через Axes.inset_axes, передайте прямоугольник в единицах данных и сместите его на половину ширины и высоты вокруг точки. Так визуальная задумка будет соответствовать данным, а лишний код исчезнет.

Статья основана на вопросе на StackOverflow от Bera и ответе Matt Pitkin.