How to make publication-quality Matplotlib plots

This guide is intended for Matplotlib<2.0; if you are using a newer version, please go here.

As someone who likes to use Python and Matplotlib, I have been struggling to make my plots look as nice as those created using Supermongo (SM) or IDL.

Here is a very basic SM macro that I wrote to plot this data, and the resulting figure:

And here is the same figure made using this python script and matplotlibrc defaults:

Yikes! There are a number of problems with this figure, including: the text size is too small compared to the other elements, there are no minor tickmarks on the y-axis, the tickmarks are too small, and the fonts are mixed.

Luckily, this can be easily fixed. Almost everything that I found needed adjustment can be changed from one's matplotlibrc file. This is what mine looks like:


    # Set the backend, otherwise the figure won't show up. Note that this will 
    # depend on your system setup; to see which backend is the default,
    # run "matplotlib.get_backend()" in the Python interpreter.
    backend                 :  GTK3Agg    

    # Increase the default DPI, and change the file type from png to pdf 
    savefig.dpi             :   300
    savefig.extension       :   pdf

    # Simplify paths by removing "invisible" points, useful for reducing
    # file size when plotting a large number of points
    path.simplify           :   True

    # Instead of individually increasing font sizes, point sizes, and line 
    # thicknesses, I found it easier to just decrease the figure size so
    # that the line weights of various components still agree 
    figure.figsize          :   4,4

    # In this example I am *not* setting "text.usetex : True", therefore the     
    # following ensures that the fonts in math mode agree with the regular ones.  
    # 
    font.family             :   serif
    mathtext.fontset        :   custom

    # Increase the tick-mark lengths (defaults are 4 and 2)
    xtick.major.size        :   6
    ytick.major.size        :   6 
    xtick.minor.size        :   3   
    ytick.minor.size        :   3

    # Increase the tick-mark widths as well as the widths of lines 
    # used to draw marker edges to be consistent with the other figure
    # linewidths (defaults are all 0.5)
    xtick.major.width       :   1
    ytick.major.width       :   1
    xtick.minor.width       :   1
    ytick.minor.width       :   1
    lines.markeredgewidth   :   1

    # Have the legend only plot one point instead of two, turn off the 
    # frame, and reduce the space between the point and the label  
    legend.numpoints        :   1
    legend.frameon          :   False
    legend.handletextpad    :   0.3
    

The final touch is adding minor tickmarks (this is done automatically for logarithmic axes, but not for linear). This is not possible to set in matplotlibrc, so I just call ax.minorticks_on() on the axis object(s) in the figure.

    import matplotlib.pyplot as plt
    import numpy as np

    x, y = np.loadtxt("data.txt", skiprows = 1, unpack = True)
    x = 10 ** x

    fig, ax = plt.subplots()
    ax.semilogx(x, y, 'o', label = "data")
    ax.legend(loc = 2, title = "Legend")
    ax.set_xlabel(r"Normal text vs. ${\rm math\, text}$")
    ax.set_ylabel(r"A B C $\alpha$ $\beta$ $\gamma$")

    #
    # Turn on minor ticks!!
    #
    ax.minorticks_on()

    fig.savefig("plot.pdf")
    plt.close(fig)
    

And here is the result:

Definitely an improvement!

Finally, a huge thanks to Gabriel-Dominique Marleau who made many helpful suggestions that improved this page.