How to add a border or frame around a figure and subplots

Question:

I want to create an image like this, but I’m unable to put the individual plots inside a frame.

enter image description here

Asked By: rohan singh

||

Answers:

Figures and axes have a patch attribute, which is the rectangle that makes up the background. Setting a figure frame is hence pretty straightforward:

import matplotlib.pyplot as plt

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

# add a bit more breathing room around the axes for the frames
fig.subplots_adjust(top=0.85, bottom=0.15, left=0.2, hspace=0.8)

fig.patch.set_linewidth(10)
fig.patch.set_edgecolor('cornflowerblue')

# When saving the figure, the figure patch parameters are overwritten (WTF?).
# Hence we need to specify them again in the save command.
fig.savefig('test.png', edgecolor=fig.get_edgecolor())

enter image description here

Now the axes are a much tougher nut to crack. We could use the same approach as for the figure (which @jody-klymak I think is suggesting), however, the patch only corresponds to the area that is inside the axis limits, i.e. it does not include the tick labels, axis labels, nor the title.

However, axes have a get_tightbbox method, which is what we are after. However, using that also has some gotchas, as explained in the code comments.

# We want to use axis.get_tightbbox to determine the axis dimensions including all
# decorators, i.e. tick labels, axis labels, etc.
# However, get_tightbox requires the figure renderer, which is not initialized
# until the figure is drawn.
plt.ion()
fig.canvas.draw()

for ii, ax in enumerate(axes):
    ax.set_title(f'Title {ii+1}')
    ax.set_ylabel(f'Y-Label {ii+1}')
    ax.set_xlabel(f'X-Label {ii+1}')
    bbox = ax.get_tightbbox(fig.canvas.get_renderer())
    x0, y0, width, height = bbox.transformed(fig.transFigure.inverted()).bounds
    # slightly increase the very tight bounds:
    xpad = 0.05 * width
    ypad = 0.05 * height
    fig.add_artist(plt.Rectangle((x0-xpad, y0-ypad), width+2*xpad, height+2*ypad, edgecolor='red', linewidth=3, fill=False))

fig.savefig('test2.png', edgecolor=fig.get_edgecolor())
plt.show()

enter image description here

Answered By: Paul Brodersen

enter image description hereI found something very similar and somehow configured it out what its doing .

autoAxis1 = ax8i[1].axis() #ax8i[1] is the axis where we want the border 

import matplotlib.patches as ptch

rec = ptch.Rectangle((autoAxis1[0]-12,autoAxis1[2]-30),(autoAxis1[1]- 
autoAxis1[0])+18,(autoAxis1[3]- 
autoAxis1[2])+35,fill=False,lw=2,edgecolor='cyan')

rec = ax8i[1].add_patch(rec)

rec.set_clip_on(False)

The code is a bit complex but once we get to know what part of the bracket inside the Rectangle() is doing what its quite easy to get the code .

Answered By: rohan singh
  • seaborn is a high-level API for matplotlib. For those who wish to place a border around seaborn axes-level functions, the process is the same as the other others. However, figure-level functions require extra steps.
  • This answer shows which matplotlib methods to use, but the figure and axes object must be extracted from the catplot FacetGrid, as shown below.
  • 'pink' was used so the border would show up for those using a black StackOverflow background.
  • To reenable the top and left spines of a FacetGrid, see this answer.

Figure Border

import seaborn as sns

# load sample dataframe and convert it to a long form
df = sns.load_dataset('geyser')
df = df.melt(id_vars='kind', var_name='cat', value_name='time')

# plot the catplot
g = sns.catplot(data=df, x='kind', y='time', col='cat')

# extract the figure object
fig = g.figure

# use standard matplotlib figure methods
fig.patch.set_linewidth(10)
fig.patch.set_edgecolor('pink')  # substitute 'k' for black

enter image description here

Axes Border

g = sns.catplot(data=df, x='kind', y='time', col='cat')

# extract and flatten the numpy array of axes
axes = g.axes.flat

# iterate through each axes and increase the linewidth and add a color
for ax in axes:
    ax.patch.set_linewidth(10)
    ax.patch.set_edgecolor('pink')

enter image description here

Combined Figure and Axes Border

g = sns.catplot(data=df, x='kind', y='time', col='cat')

axes = g.axes.flat
for ax in axes:
    ax.patch.set_linewidth(5)
    ax.patch.set_edgecolor('k')

fig = g.figure
fig.patch.set_linewidth(10)
fig.patch.set_edgecolor('purple')

enter image description here

Answered By: Trenton McKinney
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.