Embedding small plots inside subplots in matplotlib
Asked Answered
C

5

75

If you want to insert a small plot inside a bigger one you can use Axes, like here.

The problem is that I don't know how to do the same inside a subplot.

I have several subplots and I would like to plot a small plot inside each subplot. The example code would be something like this:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()

for i in range(4):
    ax = fig.add_subplot(2,2,i)
    ax.plot(np.arange(11),np.arange(11),'b')

    #b = ax.axes([0.7,0.7,0.2,0.2]) 
    #it gives an error, AxesSubplot is not callable

    #b = plt.axes([0.7,0.7,0.2,0.2])
    #plt.plot(np.arange(3),np.arange(3)+11,'g')
    #it plots the small plot in the selected position of the whole figure, not inside the subplot

Any ideas?

Clift answered 3/7, 2013 at 21:41 Comment(3)
See this related postTruculent
Working on the solution, I found another problem... #17478665Azazel
Thank you very much to both of you. I could do what I was looking for with zoomed_inset_axis from AxesGrid as Bill suggested, and also with Pablo's function. Finally I'm using Pablo's function as it is more confortable than the AxesGrid to plot all the small figures with the same size in all subplots. Thanks again!Clift
A
70

I wrote a function very similar to plt.axes. You could use it for plotting yours sub-subplots. There is an example...

import matplotlib.pyplot as plt
import numpy as np

#def add_subplot_axes(ax,rect,facecolor='w'): # matplotlib 2.0+
def add_subplot_axes(ax,rect,axisbg='w'):
    fig = plt.gcf()
    box = ax.get_position()
    width = box.width
    height = box.height
    inax_position  = ax.transAxes.transform(rect[0:2])
    transFigure = fig.transFigure.inverted()
    infig_position = transFigure.transform(inax_position)    
    x = infig_position[0]
    y = infig_position[1]
    width *= rect[2]
    height *= rect[3]  # <= Typo was here
    #subax = fig.add_axes([x,y,width,height],facecolor=facecolor)  # matplotlib 2.0+
    subax = fig.add_axes([x,y,width,height],axisbg=axisbg)
    x_labelsize = subax.get_xticklabels()[0].get_size()
    y_labelsize = subax.get_yticklabels()[0].get_size()
    x_labelsize *= rect[2]**0.5
    y_labelsize *= rect[3]**0.5
    subax.xaxis.set_tick_params(labelsize=x_labelsize)
    subax.yaxis.set_tick_params(labelsize=y_labelsize)
    return subax
    
def example1():
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(111)
    rect = [0.2,0.2,0.7,0.7]
    ax1 = add_subplot_axes(ax,rect)
    ax2 = add_subplot_axes(ax1,rect)
    ax3 = add_subplot_axes(ax2,rect)
    plt.show()

def example2():
    fig = plt.figure(figsize=(10,10))
    axes = []
    subpos = [0.2,0.6,0.3,0.3]
    x = np.linspace(-np.pi,np.pi)
    for i in range(4):
        axes.append(fig.add_subplot(2,2,i))
    for axis in axes:
        axis.set_xlim(-np.pi,np.pi)
        axis.set_ylim(-1,3)
        axis.plot(x,np.sin(x))
        subax1 = add_subplot_axes(axis,subpos)
        subax2 = add_subplot_axes(subax1,subpos)
        subax1.plot(x,np.sin(x))
        subax2.plot(x,np.sin(x))
if __name__ == '__main__':
    example2()
    plt.show()

enter image description here

Azazel answered 4/7, 2013 at 23:58 Comment(0)
D
68

You can now do this with matplotlibs inset_axes method (see docs):

from mpl_toolkits.axes_grid.inset_locator import inset_axes
inset_axes = inset_axes(parent_axes,
                    width="30%", # width = 30% of parent_bbox
                    height=1., # height : 1 inch
                    loc=3)

Update: As Kuti pointed out, for matplotlib version 2.1 or above, you should change the import statement to:

from mpl_toolkits.axes_grid1.inset_locator import inset_axes

There is now also a full example showing all different options available.

Doan answered 18/1, 2016 at 19:7 Comment(4)
What if I need to precisely define the position of the inset_axes? using loc = [1, 2, 3, 4] is not enough. I need more controlCathouse
@Cathouse You can use bbox_to_anchor as seen here: matplotlib.org/1.5.0/examples/axes_grid/…Bourke
@Cathouse Also see this example that gives you control over the positioning: matplotlib.org/examples/pylab_examples/axes_demo.htmlBourke
Link has moved!Ovalle
N
34

From matplotlib 3.0 on, you can use matplotlib.axes.Axes.inset_axes:

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2,2)

for ax in axes.flat:
    ax.plot(np.arange(11),np.arange(11))

    ins = ax.inset_axes([0.7,0.7,0.2,0.2])

plt.show()

enter image description here

The difference to mpl_toolkits.axes_grid.inset_locator.inset_axes mentionned in @jrieke's answer is that this is a lot easier to use (no extra imports etc.), but has the drawback of being slightly less flexible (no argument for padding or corner locations).

Nolan answered 7/12, 2018 at 10:33 Comment(0)
S
19

source: https://matplotlib.org/examples/pylab_examples/axes_demo.html

enter image description here

from mpl_toolkits.axes_grid.inset_locator import inset_axes
import matplotlib.pyplot as plt
import numpy as np

# create some data to use for the plot
dt = 0.001
t = np.arange(0.0, 10.0, dt)
r = np.exp(-t[:1000]/0.05)               # impulse response
x = np.random.randn(len(t))
s = np.convolve(x, r)[:len(x)]*dt  # colored noise

fig = plt.figure(figsize=(9, 4),facecolor='white')
ax = fig.add_subplot(121)
# the main axes is subplot(111) by default
plt.plot(t, s)
plt.axis([0, 1, 1.1*np.amin(s), 2*np.amax(s)])
plt.xlabel('time (s)')
plt.ylabel('current (nA)')
plt.title('Subplot 1: \n Gaussian colored noise')

# this is an inset axes over the main axes
inset_axes = inset_axes(ax, 
                    width="50%", # width = 30% of parent_bbox
                    height=1.0, # height : 1 inch
                    loc=1)
n, bins, patches = plt.hist(s, 400, normed=1)
#plt.title('Probability')
plt.xticks([])
plt.yticks([])

ax = fig.add_subplot(122)
# the main axes is subplot(111) by default
plt.plot(t, s)
plt.axis([0, 1, 1.1*np.amin(s), 2*np.amax(s)])
plt.xlabel('time (s)')
plt.ylabel('current (nA)')
plt.title('Subplot 2: \n Gaussian colored noise')

plt.tight_layout()
plt.show()
Stylish answered 13/3, 2016 at 3:40 Comment(4)
What if I need to control the exact position of the inset?Cathouse
Did you copy/paste this answer from somewhere else?Bourke
@tommy.carstensen: yes, matplotlib.org/examples/pylab_examples/axes_demo.htmlAssets
It's bad practice as to which you're masking the function inset_axes().Craft
M
0

The outset library can streamline orchestration of inset plots in matplotlib.

Example

Plot a simple curve, inserting one inset in the upper left and three in the lower right.

example outset plot

from matplotlib import pyplot as plt
import numpy as np
import outset as otst
from outset import util as otst_util


grid = otst.OutsetGrid(  # wrapper around seaborn FacetGrid
    # setup axlim's for inset axes
    # here, same limit for all four insets
    data=[[(-2.2, -1), (2.2, 2)]] * 4,
    aspect=1.5,  # make plots hamburger-shaped
)
otst.inset_outsets(  # arrange layout inset axes
    grid,
    # one inset in upper left, three in lower right
    insets=otst_util.layout_corner_insets(
        1, "NW", inset_grid_size=0.35,
    ) + otst_util.layout_corner_insets(
        3, "SE",
        inset_pad_ratio=0.4,
        inset_margin_size=(0.0, 0.1),
    ),
    # allow different aspect ratios across plots
    equalize_aspect=False,
    strip_ticks=False,
)
grid.broadcast(  # add plot content
    plt.plot,
    np.linspace(-3, 3, 100),
    np.sin(np.linspace(-3, 3, 100)),
    c="blue",
)

plt.show()

To install the library, python3 -m pip install outset.

Additional Features

Inset axes can also plotted on independently --- instead of using broadcast to plot content, access the main axes as grid.source_axes and the nth accessory axes as grid.outset_axes[n].

In addition to explicit specification as above, the library also provides a seaborn-like data-oriented API to infer zoom inserts containing categorical subsets of a dataframe.

Refer to the outset quickstart guide and gallery for more info.

Disclosure: am library author

Midyear answered 24/12, 2023 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.