pythonmatplotlibmatplotlib-gridspec

Plots and images on A4 with gridspec


I would like to create a summary A4 page for the results of some computations I have done.

These include both images and plot in a layout like the one the code below produces. Unfortunately, matplotlib makes the images very small and the plots very wide.

How can I make everything align nicely in a 2x6 grid, as in images are displayed square and plots have shape 1x2 and save it in portrait orientation?

Desired Layout

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

mpl.use('pdf')

img = np.random.standard_normal((20,20))
data = np.linspace(0,1,10000)    

title_fontsize = 'x-small'
fig = plt.figure()
fig.figsize = (6*5, 2*5)

ax = np.zeros(8, dtype=object)
gs = fig.add_gridspec(8, 2, width_ratios=[1,1])
ax[0] = fig.add_subplot(gs[0, 0])
ax[1] = fig.add_subplot(gs[0, 1])
ax[2] = fig.add_subplot(gs[1:3, :])
ax[3] = fig.add_subplot(gs[3, :])
ax[4] = fig.add_subplot(gs[4, 0])
ax[5] = fig.add_subplot(gs[4, 1])
ax[6] = fig.add_subplot(gs[5, :])
ax[7] = fig.add_subplot(gs[6, :])

ax[0].imshow(img)
ax[0].set_title('Covariance Operator', fontsize = title_fontsize)

ax[1].imshow(img)
ax[1].set_title('Sample', fontsize = title_fontsize)

ax[2].imshow(img)
ax[2].set_title('Truth', fontsize = title_fontsize)

ax[3].plot(data)
ax[3].set_title('Measurement', fontsize = title_fontsize)

ax[4].imshow(img)
ax[4].set_title('MCMC Reconstruction', fontsize = title_fontsize)

ax[5].imshow(img)
ax[5].set_title('FBP Reconstruction', fontsize = title_fontsize)

ax[6].plot(data)
ax[6].set_title('Heightscale', fontsize = title_fontsize)

ax[7].plot(data)
ax[7].set_title('Jump Size', fontsize = title_fontsize)

for x in ax.flat:
    for tick in x.xaxis.get_major_ticks():
        tick.label.set_fontsize('xx-small')
    for tick in x.yaxis.get_major_ticks():
        tick.label.set_fontsize('xx-small')

plt.savefig('test.pdf')

For reference, this is how the output looks like now:

Current output (with real data)


Solution

  • So I changed the layout a little, but the solution was to set the figsize to that of A4 paper and adjusting the height ratios of gs. Also fig.set_size_inches takes width as first argument, then height.

    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import numpy as np
    
    mpl.use('pdf')
    
    img = np.random.standard_normal((20,20))
    data = np.random.standard_normal((10000,2))
    theta = [1,2]
    description = 'Size: %s, #Samples: %s, Computation Time: %ss'%(25, 13, int(12.5))
    
    title_fontsize = 'x-small'
    fig = plt.figure(dpi=300, tight_layout=True)
    fig.set_size_inches(8.27, 11.69, forward=True)
    
    plt.figtext(0.02, .99, description, fontsize = 'small')
    
    ax = np.zeros(9, dtype=object)
    gs = fig.add_gridspec(5, 3, height_ratios=[3,2,3,2,2])
    ax[0] = fig.add_subplot(gs[0, 0])
    ax[1] = fig.add_subplot(gs[0, 1])
    ax[2] = fig.add_subplot(gs[0, 2])
    ax[3] = fig.add_subplot(gs[1, :])
    ax[4] = fig.add_subplot(gs[2, 0])
    ax[5] = fig.add_subplot(gs[2, 1])
    ax[6] = fig.add_subplot(gs[2, 2])
    ax[7] = fig.add_subplot(gs[3, :])
    ax[8] = fig.add_subplot(gs[4, :])
    
    ax[0].imshow(img)
    ax[0].set_title('Slice through Covariance Operator', fontsize = title_fontsize)
    
    ax[1].imshow(img)
    ax[1].set_title('Last Sample', fontsize = title_fontsize)
    
    ax[2].imshow(img)
    ax[2].set_title('Truth', fontsize = title_fontsize)
    
    for i, d in enumerate(data.T):
        ax[3].plot(d, label = '%s°'%int(theta[i]))
    
    ax[3].legend(loc='upper right')
    ax[3].set_title('Measurement (Sinogram)', fontsize = title_fontsize)
    
    ax[4].imshow(img)
    ax[4].set_title('MCMC Reconstruction (Sample Mean)', fontsize = title_fontsize)
    
    ax[5].imshow(img)
    ax[5].set_title('MCMC Sample Variance', fontsize = title_fontsize)
    
    ax[6].imshow(img)
    ax[6].set_title('FBP Reconstruction', fontsize = title_fontsize)
    
    ax[7].plot(data)
    ax[7].set_title('Heightscale', fontsize = title_fontsize)
    
    ax[8].plot([b[0] for b in data], label='Layer 1')
    ax[8].plot([b[1] for b in data], label='Layer 0')
    ax[8].legend(loc='upper right')
    ax[8].set_title('Jump Size', fontsize = title_fontsize)
    for x in ax.flat:
        for tick in x.xaxis.get_major_ticks():
            tick.label.set_fontsize('xx-small')
        for tick in x.yaxis.get_major_ticks():
            tick.label.set_fontsize('xx-small')
    
    plt.savefig('test.pdf')
    

    And a picture for reference:

    Correct Layout