Creating Publication-Ready Figures

When you want to create plots for your journal publications, you often end up with problems like wrong font size, too much space around a figure, or all kind of scaling related issues. This becomes even worse if you try to align multiple plots to each other in a grid, where slight differences in size result them to be displaced and look awkward. Although some methods like using tight bounding boxes get rid of the problem of too much margin around a figure, they make the exact size of the figure and hence the font sizes and other properties unpredictable, e.g. if scaled to the column’s line-width.

In order to overcome these issues, the best approach is to create figures that do not need to be re-scaled and come already with the exact figure, axes and font sizes you desire. The basic idea is to configure everything like font size and paddings through Matplotlibs rc parameters (rcParams; ‘rc’ stands for ‘run & configure’), then set up the figure with the desired size (in inches) and resolution (DPI). This way you don’t have to mess around with the tight layout option, as this would cut of an arbitrary amount of white space, leaving the exact final size of the figure unknown.

Long story short – here is the function I use in order to initialize my publication ready figures:

def initializeFigure(width='1col', height=None):
    '''
    Initialize a single plot for publication.
    
    Creates a figure and an axis object that is set to be the 
    current working axis.
    
    @param width: Width of the figure in cm or either '1col' 
                  (default) or '2col' for single our double 
                  column usage. Single column equals 8.8cm and
                  double column 18cm.
    @type width: float or str (either '1col' or '2col')
    @param height: Height of the figure either in cm. If None
                   (default), will be calculated with an 
                   aspect ratio of 7/10 (~1/1.4).
    @type height: float or None
    @return: figure and axis objects.
    @rtype: tuple (figure, axis)
    
    '''
    # Set up Matplotlib parameters for the figure.

    # make sure defaults are used
    mpl.rcParams.update(mpl.rcParamsDefault)

    # adjust parameters
    rcParams = {'text.latex.preamble':[r'\usepackage{amsmath}'],
                'text.usetex': True,
                'savefig.pad_inches': 0.0,
                'figure.autolayout': False,
                'figure.constrained_layout.use': True,
                'figure.constrained_layout.h_pad':  0.05,
                'figure.constrained_layout.w_pad':  0.05,
                'figure.constrained_layout.hspace': 0.0,
                'figure.constrained_layout.wspace': 0.0,
                'font.size':        8,
                'axes.labelsize':   'small',
                'legend.fontsize':  'small',
                'xtick.labelsize':  'x-small',
                'ytick.labelsize':  'x-small',
                'mathtext.default': 'regular',
                'font.family' :     'sans-serif',
                'axes.labelpad': 1,
                'xtick.direction': 'in',
                'ytick.direction': 'in',
                'xtick.major.pad': 2,
                'ytick.major.pad': 2,
                'xtick.minor.pad': 2,
                'ytick.minor.pad': 2
               }
    mpl.rcParams.update(rcParams)
    

    # Prepare figure width and height
    cm_to_inch = 0.393701 # [inch/cm]
    
    # Get figure width in inch
    if width == '1col':
        width = 8.8 # width [cm]
    elif width == '2col':
        width = 18.0 # width [cm]
    figWidth = width * cm_to_inch # width [inch]
    

    # Get figure height in inch
    if height is None:
        fig_aspect_ratio = 7./10.
        figHeight = figWidth * fig_aspect_ratio  # height [inch]
    else:
        figHeight = height * cm_to_inch # height [inch]
    

    # Create figure with right resolution for publication
    fig = plt.figure(figsize=(figWidth, figHeight), dpi=300)
    

    # Add axis object and select as current axis for pyplot
    ax = fig.add_subplot(111)
    plt.sca(ax)
    
    return fig, ax

So, what does it do?

First we have to pass it the desired size of the figure. This can be either the width and height in centimeters, or a string telling it to prepare a figure for either one or two columns. The latter than default to 8.8 or 18 cm for a one or two column figure, compatible to publications in the Astronomy & Astrophysics journal.

Then the Matplotlib defaults are loaded and adjusted for our publication-ready figure. Here you can adjust everything you need, according to the customization guidlines.

Before creating the figure, the exact size in inches is calculated. If no height is given, the height is calculated for an aspect ratio of 7./10. – feel free to adjust this to your preferences.

Finally the figure is created with 300 DPI for publication quality and an axis object is created and set in PyPlot as the currently selected axis. The figure size and resolution could in principle also be set through the rc parameters – this way you could provide two sets of them: one for screen figures e.g. for presentations, and another one for publications. You would then have to load the parameter set you need for your purposes.

You can now work on the figure as usual and save it to an output file:

# A simple demo how to use initializeFigure
import matplotlib as mpl
from matplotlib import pyplot as plt

fig, ax = initializeFigure('1col')

sc = plt.scatter(range(100), range(100), 
                 c=range(100),
                 label='slope')

plt.xlabel(r'Fancy X Value [$X_\odot$]')
plt.ylabel(r'Fancy Y Value [$\frac{A}{B}$]')

plt.legend()

cbar = plt.colorbar(sc, ax=ax, pad=0.0, label='Growing Property')

plt.savefig('test.pdf')

In your Latex document you can now use the figure without having to mess with its scaling:

\begin{figure}[tp!]
    \centering
    \includegraphics[]{test.pdf}
    \caption{Some caption for your figure.}
    \label{fig:test}
\end{figure}

I hope you find this helpful and don’t have to mess with figures for your publications too much anymore.