Issue
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:
- don't work with
constrained_layout
mode -- applyingplt.subplots_adjust()
after creating the figure but prior tofig.savefig()
messes up the constrained layout - don't actually quantitatively adjust the positioning of the figure -- adding
bbox_inches="tight"
orpad=-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!):
Solution
You can set the rectangle that the layout engine operates within. See the rect
parameter for each engine at https://matplotlib.org/stable/api/layout_engine_api.html.
It's unfortunately not a very friendly part of the API, especially because TightLayoutEngine
and ConstrainedLayoutEngine
have different semantics for rect
: TightLayoutEngine
uses rect = (left, bottom, right, top)
and ConstrainedLayoutEngine
uses rect = (left, bottom, width, height)
.
def set_margins(fig, margins):
"""Set figure margins as [left, right, top, bottom] in inches
from the edges of the figure."""
left,right,top,bottom = margins
width, height = fig.get_size_inches()
#convert to figure coordinates:
left, right = left/width, 1-right/width
bottom, top = bottom/height, 1-top/height
#get the layout engine and convert to its desired format
engine = fig.get_layout_engine()
if isinstance(engine, matplotlib.layout_engine.TightLayoutEngine):
rect = (left, bottom, right, top)
elif isinstance(engine, matplotlib.layout_engine.ConstrainedLayoutEngine):
rect = (left, bottom, right-left, top-bottom)
else:
raise RuntimeError('Cannot adjust margins of unsupported layout engine')
#set and recompute the layout
engine.set(rect=rect)
engine.execute(fig)
With your example:
fig = plt.figure(constrained_layout=True, figsize=(11, 8.5))
page_grid = gridspec.GridSpec(nrows=2, ncols=1, figure=fig)
#your margins were [0.2, 0.8, 0.2, 0.8] in figure coordinates
#which are 0.2*11 and 0.2*8.5 in inches from the edge
set_margins(fig,[0.2*11, 0.2*11, 0.2*8.5, 0.2*8.5])
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)
fig.savefig("temp.png", facecolor="coral")
Note: fig.suptitle
text is apparently not handled by the layout engine, so it doesn't move.
Answered By - Samuel Powell
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.