Matplotlibight_layout () не принимает во внимание фигуры

208

Если я добавлю субтитры к моей фигуре matplotlib, она будет наложена на заголовки субплота. Кто-нибудь знает, как легко позаботиться об этом? Я попробовал tight_layout()функцию, но она только ухудшает ситуацию.

Пример:

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.tight_layout()
plt.show()
katrasnikj
источник

Ответы:

183

Вы можете настроить геометрию подзаговора в самом tight_layoutвызове следующим образом:

fig.tight_layout(rect=[0, 0.03, 1, 0.95])

Как указано в документации ( https://matplotlib.org/users/tight_layout_guide.html ):

tight_layout()учитывает только метки, метки осей и заголовки. Таким образом, другие художники могут быть обрезаны, а также могут перекрываться.

Soupault
источник
1
Это работает как шарм, но как происходит изменение при указании ограничительной рамки? Я прочитал документ, но не так много было ясно для меня. Было бы здорово, если бы вы могли объяснить мне! Спасибо.
thepunitsingh
2
не могли бы вы объяснить значение каждого числа в прямоугольнике? Я не видел их в доке.
Стивен
6
@steven смотри tight_layoutдокументы:[left, bottom, right, top] in normalized (0, 1) figure coordinates
soupault
1
Вы можете увеличить расстояние между подзаголовком и осями еще больше, опустив самое правильное значение: tight_layout(rect=[0, 0.03, 1, 0.9])вместо того, 0.95что указано в ответе.
ComFreek,
1
Не работал внутри Юпитера. Пробовал разные значения для чисел,
безрезультатно
114

Вы можете вручную настроить интервал, используя plt.subplots_adjust(top=0.85):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.subplots_adjust(top=0.85)
plt.show()
unutbu
источник
5
Обратите внимание, что значение по умолчанию для top равно 0.9, когда вы звонитеsubplots_adjust
Брайан Бернс
72
Я не могу поверить в 2017 году нашего лорда, это все еще ошибка Matplotlib.
словами
29
Обратите внимание, что он subplots_adjustдолжен быть вызван после любых вызовов tight_layout, иначе он не будет вызываться subplots_adjust.
Ачал Дейв
7
@wordsforhewise сделать это 2018 сейчас
vlsd
6
@vlsd Привет с 2019 года
Сяоюй Лу
55

Одна вещь, которую вы можете очень легко изменить в своем коде, это то, что fontsizeвы используете для названий. Тем не менее, я предполагаю, что вы не просто хотите это сделать!

Некоторые альтернативы использованию fig.subplots_adjust(top=0.85):

Обычно tight_layout()неплохо справляется с позиционированием всего в хороших местах, чтобы они не перекрывались. Причина tight_layout()в этом случае не помогает, потому tight_layout()что не принимает во внимание fig.suptitle (). На GitHub есть открытый вопрос об этом: https://github.com/matplotlib/matplotlib/issues/829 [закрыт в 2014 году из-за необходимости полного менеджера геометрии - перенесен на https://github.com/matplotlib/matplotlib / Issues / 1109 ].

Если вы читаете ветку, есть решение вашей проблемы с участием GridSpec. Ключ должен оставить некоторое пространство в верхней части фигуры при вызове tight_layout, используя rectkwarg. Для вашей проблемы код становится:

Использование GridSpec

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

f = np.random.random(100)
g = np.random.random(100)

fig = plt.figure(1)
gs1 = gridspec.GridSpec(1, 2)
ax_list = [fig.add_subplot(ss) for ss in gs1]

ax_list[0].plot(f)
ax_list[0].set_title('Very Long Title 1', fontsize=20)

ax_list[1].plot(g)
ax_list[1].set_title('Very Long Title 2', fontsize=20)

fig.suptitle('Long Suptitle', fontsize=24)    

gs1.tight_layout(fig, rect=[0, 0.03, 1, 0.95])  

plt.show()

Результат:

используя gridspec

Может быть GridSpec, это немного излишне для вас, или ваша настоящая проблема будет включать в себя гораздо больше вспомогательных сюжетов на гораздо большем холсте или другие сложности. Простой взлом состоит в том, чтобы просто использовать annotate()и зафиксировать координаты 'figure fraction'для имитации suptitle. Возможно, вам придется внести некоторые более точные корректировки, как только вы посмотрите на результат. Обратите внимание, что это второе решение не использует tight_layout().

Более простое решение (хотя может потребоваться его точная настройка)

fig = plt.figure(2)

ax1 = plt.subplot(121)
ax1.plot(f)
ax1.set_title('Very Long Title 1', fontsize=20)

ax2 = plt.subplot(122)
ax2.plot(g)
ax2.set_title('Very Long Title 2', fontsize=20)

# fig.suptitle('Long Suptitle', fontsize=24)
# Instead, do a hack by annotating the first axes with the desired 
# string and set the positioning to 'figure fraction'.
fig.get_axes()[0].annotate('Long Suptitle', (0.5, 0.95), 
                            xycoords='figure fraction', ha='center', 
                            fontsize=24
                            )
plt.show()

Результат:

просто

[Использование Python2.7.3 (64-разрядная matplotlibверсия ) и 1.2.0]

aseagram
источник
1
Спасибо, если я использую первое решение, которое вы предложили (используя GridSpec), как я могу создать вспомогательные участки, которые имеют общие оси? Я обычно использую plt.subplots(N,1, sharex=<>, sharey=<>)при создании подзаговоров, но add_subplotвместо этого используется код, который вы разместили
Амелио Васкес-Рейна
10
Кастинг fig.tight_layout(rect=[0, 0.03, 1, 0.95])тоже работает.
soupault
1
Второе решение - единственное, которое сработало для меня. Спасибо!
Хагбард
19

Альтернативное и простое в использовании решение - настроить координаты текста субтитра на рисунке, используя аргумент y в вызове suptitle (см. Документацию ):

import numpy as np
import matplotlib.pyplot as plt

f = np.random.random(100)
g = np.random.random(100)
fig = plt.figure()
fig.suptitle('Long Suptitle', y=1.05, fontsize=24)
plt.subplot(121)
plt.plot(f)
plt.title('Very Long Title 1', fontsize=20)
plt.subplot(122)
plt.plot(g)
plt.title('Very Long Title 2', fontsize=20)
plt.show()
Puggie
источник
8
Это отличный способ для ноутбука, но команда plt.savefig () не подчиняется. Это решение все еще обрезает заголовок в команде savefig.
супергерой
11

Плотный макет не работает с suptitle, но constrained_layoutработает. Посмотрите этот вопрос. Увеличьте размер / интервал подзаговоров с помощью множества подзаговоров в matplotlib.

Я обнаружил, что добавление участков сразу выглядело лучше, т.е.

fig, axs = plt.subplots(rows, cols, constrained_layout=True)

# then iterating over the axes to fill in the plots

Но его также можно добавить в момент создания фигуры:

fig = plt.figure(constrained_layout=True)

ax1 = fig.add_subplot(cols, rows, 1)
# etc

Примечание: чтобы сблизить мои подсюжеты, я также использовал

fig.subplots_adjust(wspace=0.05)

и constrained_layout не работает с этим :(

bhayley
источник
4

Я боролся с Matplotlib подстроечных метод, так что я теперь просто сделал функцию , чтобы сделать это с помощью bashвызова ImageMagick«S mogrify команды , которая хорошо работает и получает все дополнительные пробельные выключения краев фигуры. Это требует, чтобы вы использовали UNIX / Linux, использовали bashоболочку и ImageMagickустановили.

Просто позвоните на это после вашего savefig()звонка.

def autocrop_img(filename):
    '''Call ImageMagick mogrify from bash to autocrop image'''
    import subprocess
    import os

    cwd, img_name = os.path.split(filename)

    bashcmd = 'mogrify -trim %s' % img_name
    process = subprocess.Popen(bashcmd.split(), stdout=subprocess.PIPE, cwd=cwd)
ryanjdillon
источник
3

Как уже упоминалось, по умолчанию плотный макет не учитывает субтитры. Однако я обнаружил, что можно использовать bbox_extra_artistsаргумент для передачи субтитра в качестве ограничивающего прямоугольника, который следует учитывать:

st = fig.suptitle("My Super Title")
plt.savefig("figure.png", bbox_extra_artists=[st], bbox_inches='tight')

Это заставляет принять suptitleво внимание расчет точной компоновки , и это выглядит так, как вы ожидаете.

Герман Шааф
источник
Это единственный вариант, который работал для меня. Я использую блокноты Jupyter, чтобы делать рисунки и сохранять их. Я работаю в Python 3.6 на машине Linux.
GRquanti
2

Единственное, что сработало для меня, - это изменение вызова suptitle:

fig.suptitle("title", y=.995)
markemus
источник
1

У меня была похожая проблема, которая возникла при использовании tight_layoutочень большой сетки графиков (более 200 дополнительных графиков) и рендеринга в блокноте Jupyter. Я сделал быстрое решение, которое всегда размещает вас suptitleна определенном расстоянии выше вашего верхнего участка:

import matplotlib.pyplot as plt

n_rows = 50
n_col = 4
fig, axs = plt.subplots(n_rows, n_cols)

#make plots ...

# define y position of suptitle to be ~20% of a row above the top row
y_title_pos = axs[0][0].get_position().get_points()[1][1]+(1/n_rows)*0.2
fig.suptitle('My Sup Title', y=y_title_pos)

Для субплотов переменного размера вы все еще можете использовать этот метод, чтобы получить верхнюю часть самого верхнего субплота, а затем вручную определить дополнительную сумму для добавления в субтитр.

Брайан Поллак
источник