Getting the same subplot size using matplotlib imshow and scatter
Question:
I am trying to plot an image (using matplotlib.imshow
) and a scatter plot within the same figure. When trying this, the image appears smaller than the scatter plot. Small example code is shown below:
import matplotlib.pyplot as plt
import numpy as np
image = np.random.randint(100,200,(200,200))
x = np.arange(0,10,0.1)
y = np.sin(x)
fig, (ax1, ax2) = plt.subplots(1,2)
ax1.imshow(image)
ax2.scatter(x,y)
plt.show()
Which gives the following figure:
How can I get the two sublpots to have the same height? (and width I suppose)
I have tried using gridspec
as shown in this answer:
fig=plt.figure()
gs=GridSpec(1,2)
ax1=fig.add_subplot(gs[0,0])
ax2=fig.add_subplot(gs[0,1])
ax1.imshow(image)
ax2.scatter(x,y)
But this gives the same result. I have also tried to adjust the subplot sizes manually by using:
fig = plt.figure()
ax1 = plt.axes([0.05,0.05,0.45,0.9])
ax2 = plt.axes([0.55,0.19,0.45,0.62])
ax1.imshow(image)
ax2.scatter(x,y)
By trial and error I can get the two subplots to the correct size, though any change in the overall figure size will mean that the subplots will no longer be the same size.
Is there a way to make imshow
and a scatter
plot appear the same size in a figure without manually changing the axes sizes?
I am using Python 2.7 and matplotlib 2.0.0
Answers:
Here’s some code I use:
fig, axis_array = plt.subplots(1, 2, figsize=(chosen_value, 1.05 * chosen_value / 2),
subplot_kw={'aspect': 1})
I’m explicitly selecting that there will be 2 sub plots in my figure, and that the figure will be chosen_value tall and each subplot will be about half that size wide, and that the subplots will have an aspect ratio of 1 (i.e., they will both be square). The figure size is a specific ratio which forces the spacing.
It’s not perfectly clear what your desired outcome is.

You may use automatic aspect on the image
ax.imshow(z, aspect="auto")

Or you may set the aspect of the line plot depending on its axis limits such that it gets the same size as the image (in case the image has equal x and y sizes)
asp = np.diff(ax2.get_xlim())[0] / np.diff(ax2.get_ylim())[0] ax2.set_aspect(asp)
import numpy as np import matplotlib.pyplot as plt x = np.linspace(0,10,20) y = np.sin(x) z = np.random.rand(100,100) fig, (ax, ax2) = plt.subplots(ncols=2) ax.imshow(z) ax2.plot(x,y, marker=".") asp = np.diff(ax2.get_xlim())[0] / np.diff(ax2.get_ylim())[0] ax2.set_aspect(asp) plt.show()
If the image does not have equal limits (is not square), one still needs to divide by the aspect of the image:
asp = np.diff(ax2.get_xlim())[0] / np.diff(ax2.get_ylim())[0] asp /= np.abs(np.diff(ax1.get_xlim())[0] / np.diff(ax1.get_ylim())[0]) ax2.set_aspect(asp)

More sophisticated solutions:

This answer for using the subplot parameters to achieve a certain aspect.

If you want to use
mpl_toolkits
and make your hands dirty, this answer would be a good read.

I had the same problem and asked a very similar question in SO. The solution proposed by @ImportanceOfBeingErnest worked like a charm for me, but for completeness, I’d like to mention a pretty simple workaround I was suggested to apply (credit to @Yilun Zhang) before my question was marked as an exact duplicate of this one:
The problem is that the plot region height is too large and this is leaving empty place in the image.
If you change your code to:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
then you get the desired outcome:
For those sharing the yaxis across both plots, setting constrained_layout
to True
may help.