Matplotlib rest_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()

Відповіді:


179

Ви можете налаштувати геометрію субплоту в самому tight_layoutдзвінку так:

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

Як зазначено в документації ( https://matplotlib.org/users/tight_layout_guide.html ):

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


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()

5
Зверніть увагу, що значення за замовчуванням для вершини становить 0,9, коли ви телефонуєтеsubplots_adjust
Брайан Бернс

72
Я не можу повірити, що у 2017 році нашого пана це все-таки помилка matplotlib.
словазвідси

29
Зауважте, що subplots_adjustпотрібно дзвонити після будь-яких дзвінків tight_layout, інакше жодного ефекту дзвінка не буде subplots_adjust.
Ахал Дейв

7
@wordsforthewise зробіть те, що 2018 рік
vlsd

6
@vlsd Здрастуйте, 2019
Xiaoyu

55

Одне, що ви можете легко змінити у своєму коді, - це fontsizeви використовуєте для заголовків. Однак я припускаю, що ти не просто хочеш цього робити!

Деякі варіанти використання fig.subplots_adjust(top=0.85):

Зазвичай tight_layout()робить досить гарну роботу з розміщенням все у хороших місцях, щоб вони не перетиналися. Причина tight_layout()не допомагає в цьому випадку в тому, що tight_layout()вона не враховує fig.suptitle (). Про це на GitHub існує відкрите питання: https://github.com/matplotlib/matplotlib/isissue/829 [закрито у 2014 році через необхідність повноцінного менеджера з геометрії - переведено на https://github.com/matplotlib/matplotlib / випуски / 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'щоб наслідувати a 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-розрядні) та matplotlib1.2.0]


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()

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 не працює з цим :(


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)

3

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

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

Це змушує чіткий розрахунок макета suptitleвраховувати, і він виглядає так, як ви очікували.


Це єдиний варіант, який працював на мене. Я використовую зошити Юпітера для виготовлення фігур і збереження їх. Я працюю в python 3.6 на машині Linux.
GRquanti

2

Єдине, що для мене працювало - це змінити виклик на suptitle:

fig.suptitle("title", y=.995)

1

У мене був аналогічний випуск, який з'явився під час використання tight_layoutдля дуже великої сітки сюжетів (понад 200 субплотів) та візуалізації в зошиті з юпітером. Я прийняв швидке рішення, яке завжди розміщує ваші 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)

Для субплотів із змінним розміром ви все ще можете скористатися цим методом, щоб отримати верхній верхній підсплав, а потім вручну визначити додаткову суму, яку потрібно додати до суппітлету.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.