2025, Dec 29 12:01

Единый масштаб y без пустот: вертикальные профили в Matplotlib

Пошаговое решение для Matplotlib: сравнение вертикальных профилей с общим масштабом y без пустых областей. Масштабируйте высоту осей через set_position.

Когда сравниваешь вертикальные профили из нескольких симуляций, обычно хочется сохранить единую шкалу по оси y, чтобы визуальные различия не возникали из-за пересчёта масштаба. Сложность появляется, когда области по высоте различаются: один прогон достигает z=1.4, другой — 1.1, третий — 1.0. При общей оси y более короткие профили оставляют незаполненное пространство над своими верхними значениями, и панели выглядят как будто с разной «пустой» подкладкой. Цель — удержать одинаковый масштаб на всех графиках, но дать каждой оси остановиться на своём реальном максимуме, чтобы рисунки последовательно уменьшались по высоте слева направо.

Как воспроизвести проблему

Ниже приведённый фрагмент задаёт одинаковый масштаб по y и показывает пустую область над более низкими доменами.

import matplotlib.pyplot as plt
import numpy as np
canvas, cols = plt.subplots(1, 3, sharey=True)
z_a = np.linspace(0, 1.4, 20)
z_b = np.linspace(0, 1.1, 20)
z_c = np.linspace(0, 1.0, 20)
cols[0].plot(np.exp(-z_a), z_a, c='k')
cols[1].plot(np.exp(-z_b), z_b, c='k')
cols[2].plot(np.exp(-z_c), z_c, c='k')
plt.show()

Что происходит на самом деле

Общая ось y даёт единые пределы: масштаб сохраняется, но каждый подплот обязан оставлять вертикальное место до глобального максимума. Поэтому во второй и третьей панелях над верхними точками данных видна пустота. Переход на gridspec или subplots_mosaic сам по себе не спасает: оси всё равно остаются одинаковыми по относительным размерам, пока вы явно не вмешаетесь в раскладку.

Решение: масштабировать высоту подграфиков по диапазону данных

Практичный подход — посчитать отношение максимального z каждого профиля к глобальному максимуму и умножить на него высоту соответствующей оси, выровняв нижние края. Так сохраняется масштаб по y, а каждая панель обрезается в нужной точке. Чтобы избежать едва заметного несовпадения делений, при изменении размеров явно задайте пределы по y.

import matplotlib.pyplot as plt
import numpy as np
z_a = np.linspace(0, 1.4, 20)
z_b = np.linspace(0, 1.1, 20)
z_c = np.linspace(0, 1.0, 20)
fig, axarr = plt.subplots(1, 3)
axarr[0].plot(np.exp(-z_a), z_a, c='k')
axarr[1].plot(np.exp(-z_b), z_b, c='k')
axarr[2].plot(np.exp(-z_c), z_c, c='k')
pos_cache = [ax.get_position() for ax in axarr]
max_z_each = [np.max(z_a), np.max(z_b), np.max(z_c)]
max_z_all = max(max_z_each)
height_scale = [m / max_z_all for m in max_z_each]
for axis, bbox, s in zip(axarr, pos_cache, height_scale):
    new_h = (bbox.y1 - bbox.y0) * s
    bottom = bbox.y0
    axis.set_position([bbox.x0, bottom, bbox.width, new_h])
    axis.set_ylim(0, max_z_all * s)
plt.show()

Если нужно чуть больше горизонтального отступа между колонками, уменьшите ширину в вызове set_position, например, до 0.8 * bbox.width. Учтите, что последующий вызов plt.subplots_adjust или plt.tight_layout снова сделает все оси одинаковой высоты. В качестве альтернативы можно поэкспериментировать с inset_axes — его можно приспособить под похожий эффект.

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

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

Выводы

Когда несколько панелей используют один и тот же физический масштаб, но диапазоны по высоте различаются, урежьте высоту каждой оси по её собственному максимуму, выровняв нижние границы. Меняйте размеры через set_position и фиксируйте пределы по y, чтобы не получить даже малейшего рассинхрона делений. Если нужно — регулируйте интервалы, напрямую меняя ширину осей, и избегайте глобальных функций раскладки, которые сбросят тщательно выставленные высоты. В итоге получите компактный, точный и сопоставимый набор графиков без визуального мусора.