How to set different subplot spacing in pyplot?

Viewed 137

I want to reduce the spacing between the first and second subplots and increase the spacing between the second and third subplots. What should I do?

Three subplots

import numpy as np
import matplotlib.pyplot as plt


plt.figure(figsize=(10, 5))
a = np.random.random((100, 100, 3))
b = np.random.random((100, 100, 3))
grid = plt.GridSpec(2, 3, wspace=0.3, hspace=0.2, height_ratios=[1,1],width_ratios=[1,1,1.8])


plt.subplot(grid[0, 0])
plt.axis('off')
plt.imshow(a)

plt.subplot(grid[0, 1])
plt.axis('off')
plt.imshow(a)

plt.subplot(grid[0, 2])
b = b.reshape((100 * 100 * 3))
plt.hist(b, bins=30, range=(0, 1))


# plt.tight_layout()

plt.show()
3 Answers

Although not a sophisticated approach, why not minimize the spacing and make the y-axis tick to the right?

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

plt.figure(figsize=(10, 5))
a = np.random.random((100, 100, 3))
b = np.random.random((100, 100, 3))
grid = plt.GridSpec(2, 3, wspace=0.2, hspace=0.2, height_ratios=[1,1],width_ratios=[1,1,1.8])


plt.subplot(grid[0, 0])
plt.axis('off')
plt.imshow(a)

plt.subplot(grid[0, 1])
plt.axis('off')
plt.imshow(a)

ax = plt.subplot(grid[0, 2])
b = b.reshape((100 * 100 * 3))
plt.hist(b, bins=30, range=(0, 1))
ax.yaxis.tick_right()

# plt.tight_layout()

plt.show()

enter image description here

First of all, I'd suggest you start storing both the created figure and axes as proper objects, so you can refer to them later. I personally prefer to put the subplots into a dedicated ax={} dictionary for later use (Think about how easily you could e.g. add labels to all subplots within a simple for a in ax.items(): loop).

In any case, the main point here is to get the Bbox (refer to the documentation for Bbox here) of the subplot in question (using .get_position()) and next to give it a new Bbox (using .set_position()) with an updated array :

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.transforms import Bbox

fig = plt.figure(figsize=(10, 5))
grid = plt.GridSpec(2, 3, wspace=0.3, hspace=0.2, height_ratios=[1,1],width_ratios=[1,1,1.8])
ax = {}
ax[0] = fig.add_subplot(grid[0,0])
ax[1] = fig.add_subplot(grid[0,1])
ax[2] = fig.add_subplot(grid[0,2])

a = np.random.random((100, 100, 3))
b = np.random.random((100, 100, 3))

ax[0].axis('off')
ax[0].imshow(a)

old_bb=Bbox(grid[0,1].get_position(fig))
x0,y0,x1,y1 = old_bb.extents

# to debug and see what the Bbox is before changing it
print(x0,y0,x1,y1) 
# we move the x0 anchor by 0.1 to the left:
new_bb=Bbox(np.array([[x0-0.1,y0],[x1,y1]]))
ax[1].set_position(new_bb)
# to debug and see what's going on afterwards
print(ax[1].get_position()) 

ax[1].axis('off')
ax[1].imshow(a)

b = b.reshape((100 * 100 * 3))
ax[2].hist(b, bins=30, range=(0, 1))

plt.show()

Another thought, which perhaps has the advantage of being only a minor adjustment to your original code.

By setting ncols=4 and putting the histogram in the fourth column, you can use the unoccupied third column as empty space, and set its relative width with the variable spacerWidth. Just remember that when you change spacerWidth, you'll likely have to adjust figsize as well, to maintain aspect ratio.

import numpy as np
import matplotlib.pyplot as plt

#For each new value of spacerWidth variable below, adjust figsize to desired aspect ratio
plt.figure(figsize=(15, 3))

a = np.random.random((100, 100, 3))
b = np.random.random((100, 100, 3))

#Set spacerWidth to your desired gap size, and adjust figsize (above) to maintain aspect ratio
spacerWidth = 0.6
grid = plt.GridSpec(1, 4, width_ratios=[1,1,spacerWidth,1.8])

plt.subplot(grid[0, 0])
plt.axis('off')
plt.imshow(a)

plt.subplot(grid[0, 1])
plt.axis('off')
plt.imshow(a)

#The histogram is now in the column with index 3, because our spacer occupies index 2
plt.subplot(grid[0, 3])
b = b.reshape((100 * 100 * 3))
plt.hist(b, bins=30, range=(0, 1))

#Omitting tight_layout() seems to result in better results for this method 
#plt.tight_layout()

plt.show()
Related