How do you create two subplots that share x axes where only one axes has a 1:1 aspect ratio?

Viewed 191

I am struggling to figure out how to have stacked subplots in which one subplot shows the data with a 1:1 aspect ratio, the other one doesn't and they both share the x axes. The figure I want looks like this:

enter image description here

My starting point for this figure has been with this code:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()

ax_top = fig.add_subplot(2, 1, 1)
ax_top.set_aspect(1.0)

x = np.linspace(0, 4*np.pi)
ax_top.plot(x, 5*np.sin(x))

ax_bot = fig.add_subplot(2, 1, 2, sharex=ax_top)
ax_bot.bar([2, 4, 6, 8, 10], [12, 13, 15, 10, 8])

plt.show()

But that produces this:

enter image description here

I would expect the lower plot to have the same width as the upper plot and the matching x ticks line up between the two plots. I've attempted many things to try to coerce this basic figure into the desired figure, but I seem to be missing some fundamental reason it doesn't work.

How might I make my desired figure using matplotlib?

3 Answers

Just make the figure taller so the dimension that gets whitespace is the vertical one:

Just to be clear what is happening here - subplot(2, 1) allocates half the figure for one subplot and half for the other. If the aspect ratio of a plot makes it narrower then there has to be white space.

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(4, 7.5))

ax_top = fig.add_subplot(2, 1, 1)
ax_top.set_aspect(1.0)

x = np.linspace(0, 4*np.pi)
ax_top.plot(x, 5*np.sin(x))

ax_bot = fig.add_subplot(2, 1, 2, sharex=ax_top)
ax_bot.bar([2, 4, 6, 8, 10], [12, 13, 15, 10, 8])

plt.show()

enter image description here

There are of course other ways to do this, including using the axes grid toolkit: https://matplotlib.org/devdocs/gallery/axes_grid1/scatter_hist_locatable_axes.html

Another possible solution is to adjust the height ratios using the gridspec_kw for subplots().

import numpy as np
import matplotlib.pyplot as plt

fig, (ax_top, ax_bot) = plt.subplots(nrows=2,
                                     sharex=True,
                                     gridspec_kw={'height_ratios': [3, 1]})

ax_top.set_aspect(1.0)

x = np.linspace(0, 4*np.pi)
ax_top.plot(x, 2*np.sin(x))

ax_bot.bar([2, 4, 6, 8, 10], [12, 13, 15, 10, 8])

plt.show()

produces:

enter image description here

I would suggest using Gridspec as it is usually more flexible than plt. Here is what I am thinking about :

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

fig = plt.figure()
gs = gridspec.GridSpec(nrows=2, ncols=1, figure=fig)
ax = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[1, 0],sharex=ax)


x = np.linspace(0, 4*np.pi)
ax.plot(x, 5*np.sin(x))
ax2.bar([2, 4, 6, 8, 10], [12, 13, 15, 10, 8])

Natacha

Related