Search code examples
matplotlibgridwhitespacecenteringaxis-labels

Matplotlib - Change the white space between specific subplots in a grid grid


I have a grid of subplots and I would like to adjust the white space between only two of them such that the shared x labels are centred without overlapping either graph.

This question has a solution for when these are the only two subplots. However I'm struggling to adjust this to two specific subplots in a grid of many.

This code can be used to illustrate my problem.

In [1]
fig = plt.figure(figsize = (15, 10))
gs = fig.add_gridspec(2,4)
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1:3])
ax3 = fig.add_subplot(gs[0, 3])
ax4 = fig.add_subplot(gs[1, 0])
ax5 = fig.add_subplot(gs[1, 1])
ax6 = fig.add_subplot(gs[1, 2])
ax7 = fig.add_subplot(gs[1, 3])


np.random.seed(19680801)

# Example data
people = ('Really Really Long Name', 'Another Long Name', 'Short Name', 'Name', 'N')
y_pos = np.arange(len(people))
performance = 3 + 10 * np.random.rand(len(people))

ax5.barh(y_pos, performance, align='center')
ax5.set_yticks(y_pos, labels=people)
ax5.invert_xaxis()
ax5.set_xlabel('Label')
ax5.set_title('Bar 1')

ax6.barh(y_pos, performance, align='center')
ax6.set_yticks(y_pos, labels=people)
ax6.set_xlabel('Label')
ax6.set_title('Bar 2')

Out [1]

enter image description here

If I apply the solution to the linked question here then every subplot's white space is effected. I know this is because it calls on fig.dpi_scale_trans which effects the whole figure but I'm new to transforms and can't work out what to use in its place

In [2]
fig.tight_layout()
fig.subplots_adjust(wspace=0.7)

plt.setp(axes[0].yaxis.get_majorticklabels(), ha='center')

# Create offset transform by some points in x direction
dx = 60 / 72.
dy = 0 / 72.
offset = mlb.transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)
# apply offset transform to all y ticklabels.
for label in ax6.yaxis.get_majorticklabels():
    label.set_transform(label.get_transform() + offset)

Out [2]

enter image description here


Solution

  • I figured out how to solve this so posting my own answer in case anybody has a similar problem in the future.

    This question and answer from 7 years ago contained the necessary help to solve the problem.

    Essentially you must plot and position different GridSpecs in the figure using GridSpec from matplotlib.gridspec rather than calling one with fig.add_gridspec()

    Link to GridSpec documentation

    Following on from my example above I wanted to create a 2x4 grid. To do that we can plot the following grids in set positions of the figure:

    Left: 1x2
    Top Centre: 1x1
    Bottom Centre: 2x1
    Right: 1x2   
    
    
    In [1]:
    from matplotlib.gridspec import GridSpec
    
    fig = plt.figure(figsize = (15, 10))
    
    # Example Data
    people = ('Really Really Long Name', 'Another Long Name', 'Short Name', 'Name', 
    'N')
    y_pos = np.arange(len(people))
    performance = 3 + 10 * np.random.rand(len(people))
    
    # Left portion of grid (2x1). 
    # 'left' and 'right' tell the grid where it should start and finish
    gs1 = GridSpec(2, 1)
    gs1.update(left = 0, right = 0.2)
    
    # Plotting empty subplots for illustration purposes
    for i in gs1:
        ax = plt.subplot(i)
    
    # Mirroring on the right portion of the grid    
    gs2 = GridSpec(2, 1)
    gs2.update(left = 0.8, right = 1)
    for i in gs2:
        ax = plt.subplot(i)
        
    # Plotting in top center
    # Note here we only need to plot a 1x1
    gs3 = GridSpec(1, 1)
    gs3.update(left = 0.25, right = 0.75, bottom = 0.53) #0.53 aligns with sides
    ax3 = plt.subplot(gs3[0])
    
    # Plotting the barh in the bottom center
    # wsapce only adjusts this grid not the entire figure
    gs4 = GridSpec(1, 2)
    gs4.update(left = 0.2, right = 0.8, top = 0.45, wspace = 0.75) 
    
    
    # Left barh
    ax5 = plt.subplot(gs4[0])
    ax5.barh(y_pos, performance, align='center')
    ax5.set_yticks([])
    ax5.invert_xaxis()
    ax5.set_xlabel('Label')
    ax5.set_title('Bar 1')
    
    # Right barh
    ax6 = plt.subplot(gs4[1])
    ax6.barh(y_pos, performance, align='center')
    ax6.set_yticks(y_pos, labels=people)
    ax6.set_xlabel('Label')
    ax6.set_title('Bar 2')
    
    
    
    plt.show()
    
    Out [1]:
    

    enter image description here