How to Remove Gaps Between Subplots in Matplotlib

How to remove gaps between subplots in matplotlib

You can use gridspec to control the spacing between axes. There's more information here.

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

plt.figure(figsize = (4,4))
gs1 = gridspec.GridSpec(4, 4)
gs1.update(wspace=0.025, hspace=0.05) # set the spacing between axes.

for i in range(16):
# i = i + 1 # grid spec indexes from 0
ax1 = plt.subplot(gs1[i])
plt.axis('on')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect('equal')

plt.show()

axes very close together

How to remove the space between subplots in matplotlib.pyplot?

A note at the beginning: If you want to have full control over spacing, avoid using plt.tight_layout() as it will try to arange the plots in your figure to be equally and nicely distributed. This is mostly fine and produces pleasant results, but adjusts the spacing at its will.

The reason the GridSpec example you're quoting from the Matplotlib example gallery works so well is because the subplots' aspect is not predefined. That is, the subplots will simply expand on the grid and leave the set spacing (in this case wspace=0.0, hspace=0.0) independent of the figure size.

In contrast to that you are plotting images with imshow and the image's aspect is set equal by default (equivalent to ax.set_aspect("equal")). That said, you could of course put set_aspect("auto") to every plot (and additionally add wspace=0.0, hspace=0.0 as arguments to GridSpec as in the gallery example), which would produce a plot without spacings.

However when using images it makes a lot of sense to keep an equal aspect ratio such that every pixel is as wide as high and a square array is shown as a square image.

What you will need to do then is to play with the image size and the figure margins to obtain the expected result. The figsize argument to figure is the figure (width, height) in inch and here the ratio of the two numbers can be played with. And the subplot parameters wspace, hspace, top, bottom, left can be manually adjusted to give the desired result.
Below is an example:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec

nrow = 10
ncol = 3

fig = plt.figure(figsize=(4, 10))

gs = gridspec.GridSpec(nrow, ncol, width_ratios=[1, 1, 1],
wspace=0.0, hspace=0.0, top=0.95, bottom=0.05, left=0.17, right=0.845)

for i in range(10):
for j in range(3):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])

#plt.tight_layout() # do not use this!!
plt.show()

Sample Image

Edit:
It is of course desireable not having to tweak the parameters manually. So one could calculate some optimal ones according to the number of rows and columns.

nrow = 7
ncol = 7

fig = plt.figure(figsize=(ncol+1, nrow+1))

gs = gridspec.GridSpec(nrow, ncol,
wspace=0.0, hspace=0.0,
top=1.-0.5/(nrow+1), bottom=0.5/(nrow+1),
left=0.5/(ncol+1), right=1-0.5/(ncol+1))

for i in range(nrow):
for j in range(ncol):
im = np.random.rand(28,28)
ax= plt.subplot(gs[i,j])
ax.imshow(im)
ax.set_xticklabels([])
ax.set_yticklabels([])

plt.show()

how to remove empty space between subplots?

Have you tried the tight layout functionality?

plt.tight_layout()

See also HERE

EDIT: Alternatively, you can use gridspec:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
images = [np.random.rand(40, 40) for x in range(68)]
gs = mpl.gridspec.GridSpec(17, 4)
gs.update(wspace=0.1, hspace=0.1, left=0.1, right=0.4, bottom=0.1, top=0.9)
for i in range(68):
plt.subplot(gs[i])
plt.imshow(images[i])
plt.axis('off')
plt.show()

Sample Image

Reducing vertical space between subplots in matplotlib

Well, there are many ways to generate a "nice" array of subplots; but assuming that your goal is to, e.g. create two rows of images where len(images)=10:

import matplotlib.pyplot as plt

images=range(10)

## assuming you want e.g. axes on your first row:
ncols = 6
# figure out how many plots will fall into the last row using modulo
ncols_last = (len(images) % ncols)
# and (if mod > 0 !) add one to the floor operation here:
nrows = (len(images) // ncols ) + (ncols_last > 0)

fig = plt.figure()
axes={}
for i in range(len(images)):
# note that for some reason, add_subplot() counts from 1, hence we use i+1 here
axes[i] = fig.add_subplot(nrows,ncols,i+1)

# add some content
for i,ax in axes.items():
ax.text(0,0,i)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)

plt.show()

Which should give you 6 plots on the first row and 4 on the second. You should be able to add your plot content like this:

for idx, (image, time) in enumerate(zip(images, wcs_request.get_dates())):
# Plot bbox
axes[idx].imshow(image)
# Set title
axes[idx].set_title(time.date().strftime("%d %B %Y"), fontsize=10, fontweight='bold')

Or alternatively, using gridspec in order to get access to further layout options:

import matplotlib.pyplot as plt
from matplotlib import gridspec

images=range(10)

ncols = 6
ncols_last = (len(images) % ncols)
nrows = (len(images) // ncols ) + (ncols_last > 0)

fig = plt.figure()
axes = {}
gs = gridspec.GridSpec(nrows, ncols,
left=0.1,right=.9,
bottom=0.1,top=.9,
wspace=0.25,hspace=0.3,
)

for i,(r,c) in enumerate([(r,c) for r in range(nrows) for c in range(ncols)]):
if i < len(images):
print(f"axes[{i}]: relates to the gridspec at index ({r},{c})")
axes[i] = fig.add_subplot(gs[r,c])

for i,ax in axes.items():
ax.text(0,0,i)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)

plt.show()

Matplotlib: how to remove spacing between a group of subplots

You can use mpl_toolkits.axes_grid1.make_axes_locatable to subdivide the area of a subplot of a 3 x 2 grid.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig = plt.figure()
gs = fig.add_gridspec(nrows=3, ncols=2, hspace=.5,
height_ratios=[4, 3, 3], width_ratios=[7, 4])

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :], sharex=ax1)

ax3 = fig.add_subplot(gs[2, 0])
div = make_axes_locatable(ax3)
ax4 = div.append_axes("top", "40%", pad=0.2, sharex=ax3)
ax5 = div.append_axes("right", "25%", pad=0.2, sharey=ax3)

ax4.tick_params(labelbottom=False)
ax5.tick_params(labelleft=False)

plt.show()

Sample Image

Also, you can create a subgridspec, like

import matplotlib.pyplot as plt
from matplotlib import gridspec

fig = plt.figure()
gs = gridspec.GridSpec(nrows=3, ncols=2, hspace=.5,
height_ratios=[4, 3, 3], width_ratios=[7, 4])

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :], sharex=ax1)

sub_gs = gridspec.GridSpecFromSubplotSpec(2,2, subplot_spec=gs[2,0], hspace=0.3, wspace=0.1,
height_ratios=[1,3], width_ratios=[3,1])
ax3 = fig.add_subplot(sub_gs[1,0])
ax4 = fig.add_subplot(sub_gs[0,0], sharex=ax3)
ax5 = fig.add_subplot(sub_gs[1,1], sharey=ax3)

ax4.tick_params(labelbottom=False)
ax5.tick_params(labelleft=False)

plt.show()

Sample Image

In both cases you will probably want to fine tune the parameters a bit. In general, the matplotlib gridspec tutorial gives a nice overview with many examples on this matter.

How to remove gaps between *images* in matplotlib?

You can set the height_ratios of the subplots using the gridspec_kw argument in the call to plt.subplots, and use the heights of the different images to set that ratio.

From the docs to plt.subplots():

gridspec_kw : dict, optional

Dict with keywords passed to the GridSpec constructor used to create the grid the subplots are placed on.

Note that your example had the exact aspect ratio of the default matplotlib image size, so you wouldn't notice any gaps appearing until you add more rows, or change the shapes of the images.

So, to expand this to a general solution, you will need to set the figure size according to the shapes of the images. For example, lets expand your example to 3 rows, 2 columns. We'll also explicitly set the figure width to 8 inches, and adjust the height based on the image sizes.

from numpy.random import rand
import matplotlib.pyplot as plt

test_data = [[rand(10,10), rand(10,10)],[rand(5,10), rand(5,10)],[rand(2,10), rand(2,10)]]
cmaps = [['viridis', 'binary'], ['plasma', 'coolwarm'], ['Greens', 'copper']]

heights = [a[0].shape[0] for a in test_data]
widths = [a.shape[1] for a in test_data[0]]

fig_width = 8. # inches
fig_height = fig_width * sum(heights) / sum(widths)

f, axarr = plt.subplots(3,2, figsize=(fig_width, fig_height),
gridspec_kw={'height_ratios':heights})

for i in range(3):
for j in range(2):
axarr[i, j].imshow(test_data[i][j], cmap=cmaps[i][j])
axarr[i, j].axis('off')
plt.subplots_adjust(wspace=0, hspace=0, left=0, right=1, bottom=0, top=1)
plt.show()

Sample Image



Related Topics



Leave a reply



Submit