71

I would like to have three plots in a single figure. The figure should have a subplot layout of two by two, where the first plot should occupy the first two subplot cells (i.e. the whole first row of plot cells) and the other plots should be positioned underneath the first one in cells 3 and 4.

I know that MATLAB allows this by using the subplot command like so:

subplot(2,2,[1,2]) % the plot will span subplots 1 and 2

Is it also possible in pyplot to have a single axes occupy more than one subplot? The docstring of pyplot.subplot doesn't talk about it.

Anyone got an easy solution?

tdy
  • 36,675
  • 19
  • 86
  • 83
Sven
  • 713
  • 1
  • 5
  • 5
  • Possible duplicate of http://stackoverflow.com/questions/1358977/how-to-make-several-plots-on-a-single-page-using-matplotlib – cyborg Dec 15 '11 at 16:42

7 Answers7

74

You can simply do:

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 7, 0.01)
    
plt.subplot(2, 1, 1)
plt.plot(x, np.sin(x))
    
plt.subplot(2, 2, 3)
plt.plot(x, np.cos(x))
    
plt.subplot(2, 2, 4)
plt.plot(x, np.sin(x)*np.cos(x))

i.e., the first plot is really a plot in the upper half (the figure is only divided into 2x1 = 2 cells), and the following two smaller plots are done in a 2x2=4 cell grid. The third argument to subplot() is the position of the plot inside the grid (in the direction of reading in English, with cell 1 being in the top-left corner): for example in the second subplot (subplot(2, 2, 3)), the axes will go to the third section of the 2x2 matrix i.e, to the bottom-left corner.

enter image description here

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
  • 11
    While this answers the specific example given in the question, it's unclear how this applies to layouts which are not powers of 2. For my code I would like to split the layout into thirds. A plot to fill right 2 thirds and another plot filling a left third. – AnnanFay Mar 24 '16 at 17:45
  • 4
    This answer actually applies to regular layouts (no "power of 2" constraint): `subplot(3, 1, 1)`, for instance, cuts in thirds. Now, what you want is handled by the [axes_grid](http://matplotlib.org/gallery.html#axes_grid) toolkit. – Eric O. Lebigot Mar 25 '16 at 20:52
  • 2
    @EOL, I'm thinking of 2 thirds, not 1 thirds. For example, I have two subplot , with only 1 column, the 1st spans 2/3, the 2nd spans 1/3. How could this be done? THanks. – StayFoolish Dec 10 '17 at 12:04
  • I am not fully sure, but it looks like the appropriate method is described in a specific Matplotlib toolkit (`axes_grid1`): https://matplotlib.org/2.0.2/mpl_toolkits/axes_grid/users/overview.html#axes-grid1. – Eric O. Lebigot Dec 11 '17 at 22:03
  • edit: I just realized I was always looking at matplotlib subplots documentation vs subplot. Either way, thanks again for the great post. – sucksatnetworking Jul 07 '21 at 07:46
  • 3
    This answer is pretty old, and largely discouraged. Please see the `gridspec` and `subplot_mosaic` responses below. – Jody Klymak Mar 31 '22 at 09:19
58

The Using Gridspec to make multi-column/row subplot layouts shows a way to do this with GridSpec. A simplified version of the example with 3 subplots would look like

import matplotlib.pyplot as plt

fig = plt.figure()

gs = fig.add_gridspec(2,2)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
ax3 = fig.add_subplot(gs[1, :])

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • so so so SO. awesome! thanks a million. this is necessary if you have for instance 6 rows... and you want one plot on row 1. and another plot to span rows 2,3,4. you can't use the usual subplot tricks (i.e. `subplot(611)` and `subplot(612)`... because subplot doesn't have any idea of `span`). – Trevor Boyd Smith Jun 07 '19 at 18:38
  • This is the most intuitive to me and is super useful if you're doing different things with different axes – GeoMonkey Oct 15 '22 at 17:12
  • For completeness could you show how to populate the empty plots with actual plots? Thanks :) – Tom Nov 08 '22 at 11:11
39

To have multiple subplots with an axis occupy, you can simply do:

from matplotlib import pyplot as plt
import numpy as np

b=np.linspace(-np.pi, np.pi, 100)

a1=np.sin(b)

a2=np.cos(b)

a3=a1*a2

plt.subplot(221)
plt.plot(b, a1)
plt.title('sin(x)')

plt.subplot(222)
plt.plot(b, a2)
plt.title('cos(x)')

plt.subplot(212)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')

plt.show()

enter image description here

Another way is

plt.subplot(222)
plt.plot(b, a1)
plt.title('sin(x)')

plt.subplot(224)
plt.plot(b, a2)
plt.title('cos(x)')

plt.subplot(121)
plt.plot(b, a3)
plt.title('sin(x)*cos(x)')

plt.show()

enter image description here

Hooshmand zandi
  • 549
  • 5
  • 4
17

For finer-grained control you might want to use the subplot2grid module of matplotlib.pyplot.

http://matplotlib.org/users/gridspec.html

A.Wan
  • 1,818
  • 3
  • 21
  • 34
  • 2
    very cool, didn't know about this, exactly what I need, especially the colspan and rowspan features. – mxmlnkn Nov 26 '17 at 23:42
15

A more modern answer would be: Simplest is probably to use subplots_mosaic: https://matplotlib.org/stable/tutorials/provisional/mosaic.html

import matplotlib.pyplot as plt
import numpy as np

# Some example data to display
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)

fig, axd = plt.subplot_mosaic([['left', 'right'],['bottom', 'bottom']],
                              constrained_layout=True)
axd['left'].plot(x, y, 'C0')
axd['right'].plot(x, y, 'C1')
axd['bottom'].plot(x, y, 'C2')
plt.show()

Example w/ subplot_mosaic

Jody Klymak
  • 4,979
  • 2
  • 15
  • 31
6

There are three main options in matplotlib to make separate plots within a figure:

  1. subplot: access the axes array and add subplots
  2. gridspec: control the geometric properties of the underlying figure (demo)
  3. subplots: wraps the first two in a convenient api (demo)

The posts so far have addressed the first two options, but they have not mentioned the third, which is the more modern approach and is based on the first two options. See the specific docs Combining two subplots using subplots and GridSpec.


Update

A much nicer improvement may be the provisional subplot_mosaic method mentioned in @Jody Klymak's post. It uses a structural, visual approach to mapping out subplots instead of confusing array indices. However it is still based on the latter options mentioned above.

pylang
  • 40,867
  • 14
  • 129
  • 121
4

I can think of 2 more flexible solutions.

  1. The most flexible way: using subplot_mosaic.
f, axes = plt.subplot_mosaic('AAB;CDD;EEE')
# axes = {'A': ..., 'B': ..., ...}

Effect:

subplot_mosaic picture

  1. Using gridspec_kw of subplots. Although it is also inconvenient when different rows need different width ratios.
f, (a0, a1) = plt.subplots(1, 2, gridspec_kw={'width_ratios': [2, 1]})

Effect:

subplots picture

The subplot method of other answers is kind of rigid, IMO. For example, you cannot create two rows with width ratios being 1:2 and 2:1 easily. However, it can help when you need to overwrite some layout of subplots, for example.

Han Zhang
  • 352
  • 2
  • 6