6

When generating a figure to save to a pdf file, I'd like to adjust the positioning of the figure relative to the edges of the page, for example to add an inch margin along all sides. As far as I can tell, the solutions to do this (for example, in this question) either:

  1. don't work with constrained_layout mode -- applying plt.subplots_adjust() after creating the figure but prior to fig.savefig() messes up the constrained layout
  2. don't actually quantitatively adjust the positioning of the figure -- adding bbox_inches="tight" or pad=-1 don't seem to do anything meaningful

Is there a straightforward way to adjust external margins of a constrained layout figure?

For example:

fig = plt.figure(constrained_layout=True, figsize=(11, 8.5))

page_grid = gridspec.GridSpec(nrows=2, ncols=1, figure=fig)

# this doesn't appear to do anything with constrained_layout=True
page_grid.update(left=0.2, right=0.8, bottom=0.2, top=0.8)

top_row_grid = gridspec.GridSpecFromSubplotSpec(1, 3, subplot_spec=page_grid[0])
for i in range(3):
    ax = fig.add_subplot(top_row_grid[:, i], aspect="equal")

n_bottom_row_plots = 10
qc_grid = gridspec.GridSpecFromSubplotSpec(1, n_bottom_row_plots, subplot_spec=page_grid[1])
for i, metric in enumerate(range(n_bottom_row_plots)):
    ax = fig.add_subplot(qc_grid[:, i])
    plt.plot(np.arange(5), np.arange(5))

fig.suptitle("my big label", fontweight="bold", fontsize="x-large", y=0.9)

# this ruins the constrained layout
# plt.subplots_adjust(left=0.2,right=0.8, bottom=0.2, top=0.8)

fig.savefig("temp.png", facecolor="coral")

Yields the following (I'd like to see more coral around the edges!):

enter image description here

Noah
  • 19,514
  • 8
  • 61
  • 69
  • As of matpotlib 3.3.1, images saved with `constrained_layout=False` display the right margin correctly. What version are you using? – r-beginners Aug 20 '20 at 14:20
  • @r-beginners I'm running 3.2.2, but I have `constrained_layout=True`... what do you mean the right margin is displayed correctly? – Noah Aug 20 '20 at 18:31
  • The same width as the left margin is in the right margin – r-beginners Aug 21 '20 at 01:26

2 Answers2

2

Have you tried using the constrained layout padding option?

fig.set_constrained_layout_pads(w_pad=4./72., h_pad=4./72.,
            hspace=0./72., wspace=0./72.)

While this might help with spacing, the constrained layout will restrict the amount of object that you can add to the defined space.

''' Here is the modified code '''

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


fig = plt.figure(constrained_layout=True, figsize=(11, 8.5))
fig.set_constrained_layout_pads(w_pad=2./12., h_pad=4./12.,
            hspace=0., wspace=0.)
page_grid = gridspec.GridSpec(nrows=2, ncols=1, figure=fig)

fig.suptitle("My BIG Label", fontweight="bold", fontsize="x-large", y=0.98)


# this doesn't appear to do anything with constrained_layout=True
page_grid.update(left=0.2, right=0.8, bottom=0.2, top=0.8)

top_row_grid = gridspec.GridSpecFromSubplotSpec(1, 3, subplot_spec=page_grid[0])
for i in range(3):
    ax = fig.add_subplot(top_row_grid[:, i], aspect="equal")

n_bottom_row_plots = 10
qc_grid = gridspec.GridSpecFromSubplotSpec(1, n_bottom_row_plots, subplot_spec=page_grid[1])
for i, metric in enumerate(range(n_bottom_row_plots)):
    ax = fig.add_subplot(qc_grid[:, i])
    plt.plot(np.arange(5), np.arange(5))


# this ruins the constrained layout
# plt.subplots_adjust(left=0.2,right=0.8, bottom=0.2, top=0.8)

fig.savefig("temp.png", facecolor="coral")
Ares Zephyr
  • 462
  • 1
  • 12
  • Thanks, this is a step in the right direction! I'm guessing by the lack of better answers that this should be a feature request ticket on mpl :) – Noah Sep 07 '20 at 01:50
0

If you want to achieve more flexibility, I personally dont recommend using constrained layout and specifying [left, right, bottom, top] together. Either specify the margin by yourself, or let matplotlib contrained layout do the rearangement for you. For more space between axes for placing texts and labels, just use hspace and wspace to adjust.

If I want more margin from both sides, and have enough space for axes labels, tick labels, and some texts. I do the following way.

fig = plt.figure(figsize=(11, 8.5), facecolor='coral')
# you code already has this
left, right, bottom, top = [0.1, 0.95, 0.1, 0.5]
# You can specify wspace and hspace to hold axes labels and some other texts.
wspace = 0.25
hspace = 0.1

nrows=1
ncols=3
gs1 = fig.add_gridspec(nrows=1, ncols=3, left=left, right=right, bottom=0.6, 
top=0.9, wspace=wspace, hspace=hspace)
axes1 = [fig.add_subplot(gs1[row, col]) for row in range(nrows) for col in 
range(ncols)]

nrows=1
ncols=10
# this grid have larger wspace than gs1
gs2 = fig.add_gridspec(nrows=1, ncols=ncols, left=left, right=right, 
bottom=0.1, top=top, wspace=0.6, hspace=hspace)
axes2 = [fig.add_subplot(gs2[row, col]) for row in range(nrows) for col in 
range(ncols)]

Now, it have larger side margin as well as enough space betweeen axes

ted930511
  • 1,390
  • 13
  • 31
  • Well, that works fine for this toy example... until you start adding things to the plot. Since you've turned off `constrained_layout`, I'm back in the mpl hell of tweaking every possible combination of label size and spacing. – Noah Sep 01 '20 at 18:04
  • The idea works for quite flexible layout. In the answer, I dont do anything to change label size. Even you use constrained layout, changing label size need addtional code unless you use your own default. You only need to change `wspace` and `hspace'. Constrained layout means less flexibility af least from my experience. – ted930511 Sep 01 '20 at 18:20