2025, Dec 14 12:02
Смещение экспоненты оси Y в matplotlib для стопок подграфиков
Экспонента смещения по оси Y в matplotlib перекрывает данные? Разбираем причину и даем решение: принудительная отрисовка и замена смещения аннотацией.
Складывать несколько подграфиков в matplotlib кажется простым — до тех пор, пока текст смещения по оси Y (экспонента в научной записи) не оказывается прямо посередине графика сверху. При общих осях X и плотной компоновке стандартное размещение этого смещения может перекрываться с данными. Простой вызов set_position для текста смещения может выглядеть как бессмысленный — будто ничего не происходит, — что сбивает с толку, когда нужно прижать подпись под верхней рамкой оси.
Минимальный пример, воспроизводящий проблему
from matplotlib import pyplot as plt
from numpy import linspace, sqrt, pi
fig, axes = plt.subplots(3, 1, sharex=True)
plt.subplots_adjust(hspace=0)
def zr_len(w_zero, lam):
return pi * w_zero**2 / lam
def waist_zero(lam, L_len, R_len):
return sqrt(lam / pi * sqrt(L_len * (R_len - L_len)))
def beam_w(z_pos, lam):
return waist_zero(lam, 0.55, 1) * sqrt(1 + (z_pos / zr_len(waist_zero(lam, 0.55, 1), lam))**2)
x_vals = linspace(0, 1, 5)
y_vals = beam_w(x_vals, 5.54e-6)
for idx in range(3):
axes[idx].set_xlabel("z / m")
axes[idx].set_ylabel("$d(w)$ / mm")
axes[idx].ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
axes[idx].grid("both", linestyle=":")
# Попытка сдвинуть экспоненту смещения
axes[idx].get_yaxis().get_offset_text().set_position((0, 0.7))
axes[1].errorbar(x_vals, y_vals, xerr=0.025, yerr=0.2*1e-3, color="black", linestyle="")
plt.show()
Что на самом деле происходит
Текст смещения пуст, пока не отрисован холст, из‑за чего кажется, что перемещение не работает. После отрисовки меняются и содержимое, и координаты текста. В интерактивном режиме это выглядит так:
>>> axes[2].get_yaxis().get_offset_text()
Text(0, 0.7, '')
>>> fig.canvas.draw()
>>> axes[1].get_yaxis().get_offset_text()
Text(0, 303.3666666666667, '1e−3')
Иными словами, до рендеринга перемещать нечего — строки с экспонентой ещё нет; а после рендеринга matplotlib сам переставляет подпись. Поэтому прямой вызов set_position кажется неэффективным.
Сведения из обсуждения показывают различия между версиями: проблему наблюдали на matplotlib 3.9.4 в Arch Linux, тогда как другой пользователь на 3.8.4 сообщил, что изменение координат через set_position действительно сдвигало текст. Приведённый ниже обходной путь от этого не зависит: он просто заменяет смещение фиксированной аннотацией.
Практическое решение: сначала отрисовать, затем заменить смещение аннотацией
Принудительная отрисовка гарантирует, что экспонента будет вычислена. После этого можно считать текст, разместить его в нужном месте в координатах, привязанных к осям, и скрыть исходный текст смещения, чтобы он не накладывался на данные.
from matplotlib import pyplot as plt
from numpy import linspace, sqrt, pi
fig, axes = plt.subplots(3, 1, sharex=True)
plt.subplots_adjust(hspace=0)
def zr_len(w_zero, lam):
return pi * w_zero**2 / lam
def waist_zero(lam, L_len, R_len):
return sqrt(lam / pi * sqrt(L_len * (R_len - L_len)))
def beam_w(z_pos, lam):
return waist_zero(lam, 0.55, 1) * sqrt(1 + (z_pos / zr_len(waist_zero(lam, 0.55, 1), lam))**2)
x_vals = linspace(0, 1, 5)
y_vals = beam_w(x_vals, 5.54e-6)
for idx in range(3):
axes[idx].set_xlabel("z / m")
axes[idx].set_ylabel("$d(w)$ / mm")
axes[idx].ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
axes[idx].grid("both", linestyle=":")
axes[1].errorbar(x_vals, y_vals, xerr=0.025, yerr=0.2*1e-3, color="black", linestyle="")
# Принудительно вычислить и разместить текст смещения
fig.canvas.draw()
# Заменить текст смещения пользовательской аннотацией, позиционированной внутри области осей
for idx in range(3):
y_off = axes[idx].get_yaxis().get_offset_text()
axes[idx].annotate(y_off.get_text(), xy=(0.01, 0.85), xycoords='axes fraction')
y_off.set_visible(False)
plt.show()
Почему это важно
В стопках графиков расположение подписей напрямую влияет на читаемость. Экспоненту научной записи легко не заметить, когда она наслаивается на кривые или соседние оси, а неверно прочитанный масштаб за секунду обесценивает анализ. Явный контроль за местом экспоненты — небольшое изменение с непропорционально большим выигрышем в ясности.
Выводы
Если перенос подписи смещения по оси Y как будто игнорируется, проверьте её после принудительного рендеринга. Когда холст отрисован, экспонента уже существует, и её можно заменить аннотацией в координатах долей осей, после чего исходную подпись скрыть. Так вы избежите наложений в макетах с общими осями и сохраните индикатор порядка величины там, где ему и место.