Add external margins with constrained layout? - python

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 -- applying plt.subplots_adjust() after creating the figure but prior to fig.savefig() messes up the constrained layout
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!):

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

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.

If what you want to do is add a margin around the finished figure, and you are not concerned with how it looks on screen, another option is to set pad_inches with bbox_inches='tight'.
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True, figsize=(11, 8.5))
fig.suptitle("My BIG Label", fontweight="bold", fontsize="x-large")
sfigs = fig.subfigures(2, 1)
# top subfigure
ax = sfigs[0].subplots(1, 3)
# bottom subfigure
n_bottom_row_plots = 10
axs = sfigs[1].subplots(1, n_bottom_row_plots)
fig.savefig("temp.png", facecolor="coral", bbox_inches='tight', pad_inches=1)
There are few things in the above comment and answers to note - first constrained layout indeed deals with subtitles, however, it doesn't if you manually set y because it assumes if you placed it manually that is where you would like it.
Second, there is a rect argument for the layout, but the suptitle doesn't get constrained by that, because rect is not really meant to be a constraint on decorations, but on where the axes get placed. I think this could be considered a bug, or at least unexpected behaviour and constrained layout should work just inside its rect. However, given the ease of adding padding in saved output it's probably not urgent, but if someone wants this to work that way, they are encouraged to open a bug report.
Finally, I modernized the above to use subfigures which are meant to be easier to work with than nested subplotspecs. Note each subfigure can also have a suptitle, etc

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

Related

Matplotlib: Fit plot with labels into subplot area

I want to make a plot with a grid of thumbnails on the left and a line plot on the right. Here is a minimal example
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
for i in range(n_grid):
for j in range(n_grid):
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(i,j))
plt.imshow(np.random.random((16,16)))
ax.set_axis_off()
### Line plot
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(0,n_grid), rowspan=n_grid-1, colspan=n_grid)
plt.plot(np.cumsum(np.random.random(100)), label='Random Sum')
plt.xlim([0, 100])
plt.ylim(0,50)
plt.xlabel('Number', fontsize=12)
plt.ylabel('Sum', fontsize=12)
plt.figtext(0.5, 0.01, f'Unique identifier', ha='center', va='baseline')
#plt.tight_layout()
plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_1.png', dpi=96)
The problem is that the yticklabels and ylabel stick over the center into the area of the thumbnails. The lineplot on the right is too wide.
One common solution found on the internet is using automatic resizing with tight_layout(), so I change the last three lines to
plt.tight_layout()
#plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_2.png', dpi=96)
This does not rescale the lineplot, but instead makes the wspace and hspace attributes so big I get way too much whitespace between the thumbnails.
I am looking for a solution to either
Set wspace and hspace of only the right subplot, not all of them together, or
resize the lineplot to fit into the designated area, without the labels sticking out
It would seem that this is an easy problem, but despite searching for about 2 hours and digging around in the object properties with iPython I found nothing suitable. All solutions seem to change the size and padding of the subplots, not fitting a plot into the area defined with subplot2grid. The only other solution I can think of is a hack that calculates a modified aspect from the value ranges to make the lineplot always a given percentage thinner.
You can play around with subfigures. For example, if you do:
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
# add 2 subfigures
subfigs = fig.subfigures(1, 2, wspace=0)
# add thumbnail grid into left subfig
gsLeft = subfigs[0].add_gridspec(n_grid, n_grid)
axLeft = []
for i in range(n_grid):
for j in range(n_grid):
axLeft.append(subfigs[0].add_subplot(gsLeft[i, j]))
axLeft[-1].imshow(np.random.random((16,16)))
axLeft[-1].set_axis_off()
### Line plot
gsRight = subfigs[1].add_gridspec(3, 1)
axRight = subfigs[1].add_subplot(gsRight[:2, 0])
axRight.plot(np.cumsum(np.random.random(100)), label='Random Sum')
axRight.set_xlim([0, 100])
axRight.set_ylim(0,50)
axRight.set_xlabel('Number', fontsize=12)
axRight.set_ylabel('Sum', fontsize=12)
# adjust subfigures here (play around with these to get the desired effect)
subfigs[0].subplots_adjust(wspace=0.03, hspace=0.03, bottom=0.05, top=0.95, left=0.05, right=0.95)
subfigs[1].subplots_adjust(left=0.01)
# add title (here I've had to add it to the left figure, so it's not centred,
# in my test adding it to the figure itself meant it was not visible, although
# the example in the Matplotlib docs suggests it should work!)
# fig.suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
subfigs[0].suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
fig.savefig("plot_1.png", dpi=150)
This gives:
but you can play around with the values to adjust it as you like.

matplotlib change colorbar height within own axes

I'm currently trying to create a stackplot of graphs, of which my first two have colorbars. To do this nicely, I'm using GridSpec to define two columns, with the second being much thinner and specifically for colorbars (or other out-of-plot things like legends).
grids = gs.GridSpec(5, 2, width_ratios=[1, 0.01])
ax1 = fig.add_subplot(grids[0, 0])
cax1 = fig.add_subplot(grids[0, 1])
The problem is that for these top two plots, the ticklabels of my colorbar overlap slightly, due to the fact that I've got zero horizontal space between my plots.
I know that there are ways to control the height of the colorbar, but they seem to rely on the colorbar making it's own axes by borrowing space from the parent axes. I was wondering if there was any way to control how much space (or specifically, height) the colorbar takes up when you use the cax kwarg
fig.colorbar(im1, cax=cax1, extend='max')
or if it defaults (immutably) to take up the entire height of the axes given to it.
Thanks!
EDIT: Here's an image of the issue I'm struggling with.
If I could make the second slightly shorter, or shift the upper one slightly up then it wouldn't be an issue. Unfortunately since I've used GridSpec (which has been amazing otherwise) I'm constrained to the limits of the axes.
I don't think there is any way to ask colorbar to not fill the whole cax. However, it is fairly trivial to shrink the size of the cax before (or after actually) plotting the colorbar.
I wrote this small function:
def shrink_cbar(ax, shrink=0.9):
b = ax.get_position()
new_h = b.height*shrink
pad = (b.height-new_h)/2.
new_y0 = b.y0 + pad
new_y1 = b.y1 - pad
b.y0 = new_y0
b.y1 = new_y1
ax.set_position(b)
which can be used like so:
fig = plt.figure()
grids = gs.GridSpec(2, 2, width_ratios=[1, 0.01])
ax1 = fig.add_subplot(grids[0, 0])
cax1 = fig.add_subplot(grids[0, 1])
ax2 = fig.add_subplot(grids[1, 0])
cax2 = fig.add_subplot(grids[1, 1])
shrink_cbar(cax2, 0.75)

matplotlib: `tight_layout()` didn't work in PyQt widget

I've built a PyQt application to show three subplot using gridspec:
class Mesh(FigureCanvas):
def __init__(self, Max_i, Max_j, file='',
mesh_string='', nkx=0, nky=0, factor=1.1, figsize=(12, 8),
facecolor='none', dpi=100):
self.figure = Figure(figsize=figsize, dpi=dpi, facecolor=facecolor)
FigureCanvas.__init__(self, self.figure)
...
gs = gridspec.GridSpec(3, 4)
ax_main = self.figure.add_subplot(gs[:2, :])
ax_dx = self.figure.add_subplot(gs[2, :2])
ax_dy = self.figure.add_subplot(gs[2, 2:])
gs.update(wspace=100)
ax_dx.plot(Arr_x[: Max_i], Arr_dx, 'k-', lw=1.2)
ax_dy.plot(Arr_y[: Max_j], Arr_dy, 'k-', lw=1.2)
ax_dx.set_xlim(Arr_x[0], Arr_x[Max_i - 1])
ax_dx.set_ylim(0, Arr_dx.max() * factor)
ax_dy.set_xlim(Arr_y[0], Arr_y[Max_j - 1])
ax_dy.set_ylim(0, Arr_dy.max() * factor)
ax_dx.set_xlabel('x')
ax_dx.set_ylabel('dx')
ax_dy.set_xlabel('y')
ax_dy.set_ylabel('dy')
...
#set tight_layout here!
gs.tight_layout(self.figure)
However, the labels of different axes still overlapping each other, nothing changed at all, it seems tight_layout() didn't work in Qt widget, so is there another way to arrange subplots in Qt widget?
you can use subplots_adjust to change the spacing between plots and the edges of the figure:
self.figure.subplots_adjust(left=0.1,right=0.9,
bottom=0.1,top=0.9,
hspace=0.2,wspace=0.2)
Where left, right, bottom and top adjust the width of the margins at the edge of the figure, and hspace and wspace change the vertical and horizontal spaces between plots, respectively.
So, it sounds like you need to increase your hspace and wspace. It might take some experimenting to find a good configuration for your figure.

Moving matplotlib legend outside of the axis makes it cutoff by the figure box

I'm familiar with the following questions:
Matplotlib savefig with a legend outside the plot
How to put the legend out of the plot
It seems that the answers in these questions have the luxury of being able to fiddle with the exact shrinking of the axis so that the legend fits.
Shrinking the axes, however, is not an ideal solution because it makes the data smaller making it actually more difficult to interpret; particularly when its complex and there are lots of things going on ... hence needing a large legend
The example of a complex legend in the documentation demonstrates the need for this because the legend in their plot actually completely obscures multiple data points.
http://matplotlib.sourceforge.net/users/legend_guide.html#legend-of-complex-plots
What I would like to be able to do is dynamically expand the size of the figure box to accommodate the expanding figure legend.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(x, np.sin(x), label='Sine')
ax.plot(x, np.cos(x), label='Cosine')
ax.plot(x, np.arctan(x), label='Inverse tan')
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0))
ax.grid('on')
Notice how the final label 'Inverse tan' is actually outside the figure box (and looks badly cutoff - not publication quality!)
Finally, I've been told that this is normal behaviour in R and LaTeX, so I'm a little confused why this is so difficult in python... Is there a historical reason? Is Matlab equally poor on this matter?
I have the (only slightly) longer version of this code on pastebin http://pastebin.com/grVjc007
Sorry EMS, but I actually just got another response from the matplotlib mailling list (Thanks goes out to Benjamin Root).
The code I am looking for is adjusting the savefig call to:
fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight')
#Note that the bbox_extra_artists must be an iterable
This is apparently similar to calling tight_layout, but instead you allow savefig to consider extra artists in the calculation. This did in fact resize the figure box as desired.
import matplotlib.pyplot as plt
import numpy as np
plt.gcf().clear()
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
fig = plt.figure(1)
ax = fig.add_subplot(111)
ax.plot(x, np.sin(x), label='Sine')
ax.plot(x, np.cos(x), label='Cosine')
ax.plot(x, np.arctan(x), label='Inverse tan')
handles, labels = ax.get_legend_handles_labels()
lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5,-0.1))
text = ax.text(-0.2,1.05, "Aribitrary text", transform=ax.transAxes)
ax.set_title("Trigonometry")
ax.grid('on')
fig.savefig('samplefigure', bbox_extra_artists=(lgd,text), bbox_inches='tight')
This produces:
[edit] The intent of this question was to completely avoid the use of arbitrary coordinate placements of arbitrary text as was the traditional solution to these problems. Despite this, numerous edits recently have insisted on putting these in, often in ways that led to the code raising an error. I have now fixed the issues and tidied the arbitrary text to show how these are also considered within the bbox_extra_artists algorithm.
Added: I found something that should do the trick right away, but the rest of the code below also offers an alternative.
Use the subplots_adjust() function to move the bottom of the subplot up:
fig.subplots_adjust(bottom=0.2) # <-- Change the 0.02 to work for your plot.
Then play with the offset in the legend bbox_to_anchor part of the legend command, to get the legend box where you want it. Some combination of setting the figsize and using the subplots_adjust(bottom=...) should produce a quality plot for you.
Alternative:
I simply changed the line:
fig = plt.figure(1)
to:
fig = plt.figure(num=1, figsize=(13, 13), dpi=80, facecolor='w', edgecolor='k')
and changed
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0))
to
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,-0.02))
and it shows up fine on my screen (a 24-inch CRT monitor).
Here figsize=(M,N) sets the figure window to be M inches by N inches. Just play with this until it looks right for you. Convert it to a more scalable image format and use GIMP to edit if necessary, or just crop with the LaTeX viewport option when including graphics.
Here is another, very manual solution. You can define the size of the axis and paddings are considered accordingly (including legend and tickmarks). Hope it is of use to somebody.
Example (axes size are the same!):
Code:
#==================================================
# Plot table
colmap = [(0,0,1) #blue
,(1,0,0) #red
,(0,1,0) #green
,(1,1,0) #yellow
,(1,0,1) #magenta
,(1,0.5,0.5) #pink
,(0.5,0.5,0.5) #gray
,(0.5,0,0) #brown
,(1,0.5,0) #orange
]
import matplotlib.pyplot as plt
import numpy as np
import collections
df = collections.OrderedDict()
df['labels'] = ['GWP100a\n[kgCO2eq]\n\nasedf\nasdf\nadfs','human\n[pts]','ressource\n[pts]']
df['all-petroleum long name'] = [3,5,2]
df['all-electric'] = [5.5, 1, 3]
df['HEV'] = [3.5, 2, 1]
df['PHEV'] = [3.5, 2, 1]
numLabels = len(df.values()[0])
numItems = len(df)-1
posX = np.arange(numLabels)+1
width = 1.0/(numItems+1)
fig = plt.figure(figsize=(2,2))
ax = fig.add_subplot(111)
for iiItem in range(1,numItems+1):
ax.bar(posX+(iiItem-1)*width, df.values()[iiItem], width, color=colmap[iiItem-1], label=df.keys()[iiItem])
ax.set(xticks=posX+width*(0.5*numItems), xticklabels=df['labels'])
#--------------------------------------------------
# Change padding and margins, insert legend
fig.tight_layout() #tight margins
leg = ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1), borderaxespad=0)
plt.draw() #to know size of legend
padLeft = ax.get_position().x0 * fig.get_size_inches()[0]
padBottom = ax.get_position().y0 * fig.get_size_inches()[1]
padTop = ( 1 - ax.get_position().y0 - ax.get_position().height ) * fig.get_size_inches()[1]
padRight = ( 1 - ax.get_position().x0 - ax.get_position().width ) * fig.get_size_inches()[0]
dpi = fig.get_dpi()
padLegend = ax.get_legend().get_frame().get_width() / dpi
widthAx = 3 #inches
heightAx = 3 #inches
widthTot = widthAx+padLeft+padRight+padLegend
heightTot = heightAx+padTop+padBottom
# resize ipython window (optional)
posScreenX = 1366/2-10 #pixel
posScreenY = 0 #pixel
canvasPadding = 6 #pixel
canvasBottom = 40 #pixel
ipythonWindowSize = '{0}x{1}+{2}+{3}'.format(int(round(widthTot*dpi))+2*canvasPadding
,int(round(heightTot*dpi))+2*canvasPadding+canvasBottom
,posScreenX,posScreenY)
fig.canvas._tkcanvas.master.geometry(ipythonWindowSize)
plt.draw() #to resize ipython window. Has to be done BEFORE figure resizing!
# set figure size and ax position
fig.set_size_inches(widthTot,heightTot)
ax.set_position([padLeft/widthTot, padBottom/heightTot, widthAx/widthTot, heightAx/heightTot])
plt.draw()
plt.show()
#--------------------------------------------------
#==================================================
I tried a very simple way, just make the figure a bit wider:
fig, ax = plt.subplots(1, 1, figsize=(a, b))
adjust a and b to a proper value such that the legend is included in the figure

changing size of a plot in a subplot figure

i create a figure with 4 subplots (2 x 2), where 3 of them are of the type imshow and the other is errorbar. Each imshow plots have in addition a colorbar at the right side of them. I would like to resize my 3rd plot, that the area of the graph would be exactly under the one above it (with out colorbar)
as example (this is what i now have):
How could i resize the 3rd plot?
Regards
To adjust the dimensions of an axes instance, you need to use the set_position() method. This applies to subplotAxes as well. To get the current position/dimensions of the axis, use the get_position() method, which returns a Bbox instance. For me, it's conceptually easier to just interact with the position, ie [left,bottom,right,top] limits. To access this information from a Bbox, the bounds property.
Here I apply these methods to something similar to your example above:
import matplotlib.pyplot as plt
import numpy as np
x,y = np.random.rand(2,10)
img = np.random.rand(10,10)
fig = plt.figure()
ax1 = fig.add_subplot(221)
im = ax1.imshow(img,extent=[0,1,0,1])
plt.colorbar(im)
ax2 = fig.add_subplot(222)
im = ax2.imshow(img,extent=[0,1,0,1])
plt.colorbar(im)
ax3 = fig.add_subplot(223)
ax3.plot(x,y)
ax3.axis([0,1,0,1])
ax4 = fig.add_subplot(224)
im = ax4.imshow(img,extent=[0,1,0,1])
plt.colorbar(im)
pos4 = ax4.get_position().bounds
pos1 = ax1.get_position().bounds
# set the x limits (left and right) to first axes limits
# set the y limits (bottom and top) to the last axes limits
newpos = [pos1[0],pos4[1],pos1[2],pos4[3]]
ax3.set_position(newpos)
plt.show()
You may feel that the two plots do not exactly look the same (in my rendering, the left or xmin position is not quite right), so feel free to adjust the position until you get the desired effect.

Categories

Resources