2025, Oct 18 06:16

Плотная компоновка QQ-графиков рядом с гистограммой в Matplotlib

Как убрать горизонтальные отступы между QQ-графиками рядом с гистограммой в Matplotlib: используем GridSpec, width_ratios и wspace=0 для плотной компоновки.

Когда вы размещаете в одном ряду гистограмму и несколько QQ-графиков, внутри панелей QQ нередко остаются лишние горизонтальные пустоты, даже если убрать подписи и деления по оси Y. Подстройка глобальных отступов иногда выручает, но не всегда — особенно если подграфики различаются по форме и содержанию. Ниже — аккуратный способ расположить QQ-графики вплотную, чтобы лишние поля не мешали.

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

Схема состоит из гистограммы и нескольких QQ-графиков следом. На QQ-панелях убраны подписи по оси Y, ось Y сделана общей для согласования шкал. Тем не менее, у QQ-графиков остаётся горизонтальный внутренний отступ, который хочется убрать.

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
arr = np.random.normal(0, 1, 1000)
canvas, axe_row = plt.subplots(1, 4, figsize=(10, 4))
# Первый блок: гистограмма
axe_row[0].hist([1, 2, 3], bins=30, edgecolor='black')
axe_row[0].set_ylabel('Count')
axe_row[0].set_xlabel('Val')
# Остальные панели: QQ-графики
for a in axe_row[1:]:
    stats.probplot(arr, dist="norm", plot=a)
# Убираем информацию по оси Y на QQ-панелях и делаем общую ось Y
for a in axe_row[1:]:
    a.set_ylabel(None)
    a.tick_params(labelleft=False)
    a.sharey(axe_row[0])
canvas.tight_layout()
plt.show()

Откуда берётся лишнее горизонтальное пространство

Глобальные параметры расстояний управляют зазорами между осями, но не регулируют внутренние поля, которые каждая ось оставляет под деления, рамки и подписи. Даже скрыв метки и деления по оси Y, отдельная QQ-ось всё равно может резервировать место слева, поэтому простое «ужатие» макета или уменьшение промежутков между осями не убирает остаточное поле. Когда первый подграфик — гистограмма, а остальные — QQ-графики, внутренние отступы у них различаются, и в результате появляется неровная или стойкая пустота.

Решение: GridSpec с width_ratios и нулевой шириной разделителей

Используйте макет на базе GridSpec и задайте ширины явно. Вставив столбцы нулевой ширины как разделители и установив wspace в ноль, можно визуально убрать горизонтальный зазор между QQ-графиками, при этом оставить гистограмму нужного размера.

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
arr = np.random.normal(0, 1, 1000)
fig_obj = plt.figure(figsize=(10, 4))
layout = fig_obj.add_gridspec(
    1, 7,
    width_ratios=[1.5, 0.5, 1, 0.00, 1, 0.00, 1],
    wspace=0
)
# Гистограмма
ax_histogram = fig_obj.add_subplot(layout[0])
ax_histogram.hist([1, 2, 3], bins=30, edgecolor='black')
ax_histogram.set_ylabel('Count')
ax_histogram.set_xlabel('Val')
# QQ-графики: размещаем в столбцах 2, 4, 6, между ними вставляем разделители нулевой ширины
qq_axes = [fig_obj.add_subplot(layout[i]) for i in range(2, 7, 2)]
stats.probplot(arr, dist="norm", plot=qq_axes[0])
qq_axes[0].set_ylabel('Y_label')
for ax in qq_axes[1:]:
    stats.probplot(arr, dist="norm", plot=ax)
    ax.set_ylabel(None)
    ax.set_yticklabels([])
    ax.set_yticks([])
    ax.spines['left'].set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

Этот подход явно задаёт сетку из семи столбцов: гистограмма получает большую ширину, QQ-графики располагаются через один столбец, а промежуточные столбцы имеют нулевую ширину. При wspace=0 QQ-графики оказываются вплотную друг к другу, и прежняя горизонтальная пустота исчезает.

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

Диагностика становится нагляднее, когда подграфики не окружены ненужными полями. В компактных дашбордах, публикациях или быстрых отчётах EDA важен каждый пиксель по горизонтали. Предсказуемый макет также упрощает выравнивание и сравнение распределений на нескольких QQ-графиках.

Итоги

Если между QQ-графиками остаются упорные зазоры, выход — не только в глобальных настройках интервалов, а в управлении сеткой. GridSpec с width_ratios и wspace=0 позволяет распределить ширину ровно там, где нужно, а столбцы нулевой ширины выступают невидимыми разделителями, убирая внутренние поля. Спрячьте элементы оси Y на QQ-панелях, держите единую шкалу по Y — и весь ряд будет читаться чисто, без потерь пространства.

Статья основана на вопросе с StackOverflow от sds и ответе пользователя Sindik.