Skip to content

Instantly share code, notes, and snippets.

@LeoHuckvale
Created February 2, 2015 16:53
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LeoHuckvale/89683dc242f871c8e69b to your computer and use it in GitHub Desktop.
Save LeoHuckvale/89683dc242f871c8e69b to your computer and use it in GitHub Desktop.
matplotlib - Add subplots dynamically

Useful tip from the late creator of matplotlib, John Hunter.

http://matplotlib.1069221.n5.nabble.com/dynamically-add-subplots-to-figure-td23571.html

import matplotlib.pyplot as plt

# start with one
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([1,2,3])

# now later you get a new subplot; change the geometry of the existing
n = len(fig.axes)
for i in range(n):
    fig.axes[i].change_geometry(n+1, 1, i+1)

# add the new
ax = fig.add_subplot(n+1, 1, n+1)
ax.plot([4,5,6])

plt.show()
@Splines
Copy link

Splines commented Feb 16, 2022

change_geometry deprecated

The change_geometry function was deprecated in Matplotlib 3.4 and will be removed two minor releases later. Use set_subplotspec instead.

See the official docs.

So, instead you could do something like this (based on this Stackoverflow answer):

import matplotlib.pyplot as plt
from matplotlib import gridspec


class DrawExample():

    def __init__(self, initial_data) -> None:
        """Constructs a DrawExample for one column and multiple rows.

        Note that subplots are added to the bottom.
        """
        self.fig = plt.figure()

        # Start with one subplot
        self.row = 1
        ax = self.fig.add_subplot(self.row, 1, 1)
        ax.plot(initial_data)

    def plot_bottom(self, data: list) -> None:
        """Plots the data to a new subplot at the bottom."""
        self.row += 1
        gs = gridspec.GridSpec(self.row, 1)

        # Reposition existing subplots
        for i, ax in enumerate(self.fig.axes):
            ax.set_position(gs[i].get_position(self.fig))
            ax.set_subplotspec(gs[i])

        # Add new subplot
        new_ax = self.fig.add_subplot(gs[self.row-1])
        new_ax.plot(data)

    def show(self) -> None:
        plt.show()


draw = DrawExample([1, 2, 3])
draw.plot_bottom([1, 4, 100])
draw.plot_bottom([10, -1, -2])
draw.show()

which will give you this result:
DrawExamplePlot

@LeoHuckvale
Copy link
Author

Nice, thanks @Splines !

@ShambreTaylor
Copy link

ShambreTaylor commented Mar 17, 2022

Hi everyone!

I made a two dim version based on @Splines 's code.

from matplotlib import pyplot as plt
from matplotlib.axes import Axes
from matplotlib import gridspec

class subplot():
    def __init__(self,*argv, nrows=None, ncols=None):
        if len(argv)==2:
            nrows=argv[0]
            ncols=argv[1]
        
        # Set figure
        self.fig = plt.figure()
        
        # Set list of geometry
        self.geometry = []
        
        # Set GridSpec
        self.gs = self.fig.add_gridspec(nrows,ncols)
        
        # Update nrows and ncols
        self.nrows = nrows
        self.ncols = ncols

    def __getitem__(self, items) -> Axes:
        self.append2geometry(items)
        return self.fig.add_subplot(self.gs.__getitem__(items))
    
    def show(self) -> None:
        plt.show()
        
    def resize(self, nrows, ncols):
        
        gs = gridspec.GridSpec(nrows, ncols)
        
        for ax, geo in zip(self.fig.axes, self.geometry):
            pos = gs[geo].get_position(self.fig)
            ax.set_position(pos)
            ax.set_subplotspec(gs[geo])
        
        self.gs = gs
        
    def concretize(self, sl,max:int):
        if type(sl) == slice:
            if sl.start == None: start = 0
            else: start = sl.start
            
            if sl.stop == None: stop = max
            else: stop = sl.stop
            
            return slice(start,stop)
        
        elif type(sl) == int: return sl
        else: raise TypeError('I can only concretize only on int or slice')
        
    def append2geometry(self, items):
        x = self.concretize(items[0],self.nrows)
        y = self.concretize(items[1],self.ncols)
        self.geometry.append((x,y))
        
    def ax(self,x,y) -> Axes:
        self.append2geometry((x,y))
        return self.fig.add_subplot(self.gs[x,y])


myfig = subplot(2,2)

ax = myfig[0,:]
ax.plot([2,1,3])

ax = myfig[1,0]
ax.plot([2,1,3])

myfig.show()

myfig.resize(3,3)

ax=myfig[:,2]
ax.plot([3,1,2])

myfig.show()

which gives you this first:
kép

than this:
kép

@ponnhide
Copy link

ponnhide commented Apr 2, 2022

Hi, everyone.
If you are interested in how to add matplotlib subplot dynamically, I want to present patchwoklib, which I'm developing.
It allows arranging matplotlib subplots with only "/" and "|" operators like the patchwork package for ggplot.
Please refer to the following code.

import patchworklib as pw
ax1 = pw.Brick("ax1",figsize=(1,1))
ax2 = pw.Brick("ax2",figsize=(1,3)) 
ax1.set_title("ax1")
ax2.set_title("ax2") 
ax12 = ax1|ax2
ax12.savefig("ax12.png")

ax3 = pw.Brick("ax3",(2,1))
ax4 = pw.Brick("ax4",(2,2))
ax3.set_title("ax3")
ax4.set_title("ax4") 
ax34 = ax3/ax4
ax1234 = ax12 | ax34
ax1234.savefig("ax1234.png")

ax12.png
download

ax1234.png
download

Sure, more complex plots can be generated. For details, please see https://medium.com/p/bd037fe967f4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment