24

Is it possible to set the size/position of a matplotlib subplot after the axes are created? I know that I can do:

import matplotlib.pyplot as plt

ax = plt.subplot(111)
ax.change_geometry(3,1,1)

to put the axes on the top row of three. But I want the axes to span the first two rows. I have tried this:

import matplotlib.gridspec as gridspec

ax = plt.subplot(111)
gs = gridspec.GridSpec(3,1)
ax.set_subplotspec(gs[0:2])

but the axes still fill the whole window.

Update for clarity I want to change the position of an existing axes instance rather than set it when it is created. This is because the extent of the axes will be modified each time I add data (plotting data on a map using cartopy). The map may turn out tall and narrow, or short and wide (or something in between). So the decision on the grid layout will happen after the plotting function.

RuthC
  • 1,269
  • 1
  • 10
  • 21
  • 1
    Thanks for such a question, I've been searching for hours before finding your question. For info, my use case : dynamic addition/deletion of subplots in a `Figure` displayed in `PyQt`, avoiding new calls to items creation and subsequent data loading when such modifications occur. – Joël Oct 23 '18 at 14:46

3 Answers3

19

Thanks to Molly pointing me in the right direction, I have a solution:

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

fig = plt.figure()

ax = fig.add_subplot(111)

gs = gridspec.GridSpec(3,1)
ax.set_position(gs[0:2].get_position(fig))
ax.set_subplotspec(gs[0:2])              # only necessary if using tight_layout()

fig.add_subplot(gs[2])

fig.tight_layout()                       # not strictly part of the question

plt.show()
RuthC
  • 1,269
  • 1
  • 10
  • 21
  • 3
    The important stuff here is `1)` create a new `GridSpec` instance with the expected layout, as its number of rows or cols cannot be updated (even with its `update()` method); `2)` call `ax.set_position(gs[0:2].get_position(fig))` to resize; calling `set_subplotspec` only create ref, and does not update position. – Joël Oct 23 '18 at 14:49
7

You can create a figure with one subplot that spans two rows and one subplot that spans one row using the rowspan argument to subplot2grid:

import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = plt.subplot2grid((3,1), (0,0), rowspan=2)
ax2 = plt.subplot2grid((3,1), (2,0))
plt.show()

enter image description here

If you want to change the subplot size and position after it's been created you can use the set_position method.

ax1.set_position([0.1,0.1, 0.5, 0.5])

Bu you don't need this to create the figure you described.

Molly
  • 13,240
  • 4
  • 44
  • 45
  • Thanks Molly. It looks like ax.set_position(plt.subplot(gs[0:2]).get_position()) should do what I want. – RuthC Apr 05 '14 at 16:55
  • ax.set_position() works, but the extra call to plt.subplot() in my previous comment is only useful if you want an empty plot! – RuthC Apr 05 '14 at 17:37
1

You can avoid ax.set_position() by using fig.tight_layout() instead which recalculates the new gridspec:

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# create the first axes without knowing of further subplot creation
fig, ax = plt.subplots()
ax.plot(range(5), 'o-')

# now update the existing gridspec ...
gs = gridspec.GridSpec(3, 1)
ax.set_subplotspec(gs[0:2])
# ... and recalculate the positions
fig.tight_layout()

# add a new subplot
fig.add_subplot(gs[2])
fig.tight_layout()
plt.show()
fhgd
  • 402
  • 2
  • 10