Skip to main content

Making Plots for Publication

Overview

This document extends the discussion of plots and graphs given in A Student’s Guide to Python for Physical Modeling. Here we give some recipes that work on our installation. If they don’t work on yours, please comment on that, especially if you find a workaround.

This post describes the following:

  • Setting figure size
  • Importing the SGPniceGraphics module
  • Functions and parameters defined in the module
  • Fixing crowded graphs
  • Saving graphs for modification in other art software
  • Typesetting labels with LaTeX
  • Other rc settings
  • CMYK color
  • Exporting raster images

Setting Figure Size

You can create a figure window of a specific size before plotting with the command

myfigsize = (6, 3)
plt.figure(frameon=False, figsize=myfigsize)

The first keyword argument suppresses the colored panel in the back of the figure, so you won’t need to delete it by hand in some drawing software. The second keyword argument specifies the desired width and height of the figure, in inches (!!). Give it a name (myfigsize) so that you can set it once and then use it throughout your code (“Define once, and reuse often.”)

Importing the SGPniceGraphics Module

The SGPniceGraphics module contains a few functions which are described below. Their purpose is to make plots look nicer. You can download the module here.

To use the module, you must first put it in a place where Python can find it. The easiest way is just to put it in your Global Working Directory, along with the script you are working on. Almost as easily, however, you can put it in some central location and ask Python to add that folder to your Path. From the python menu bar in Spyder, select PYTHONPATH manager. Click the “Add path” button and fill in the full path to the central location. (Mac users can obtain the path name as follows: Navigate to the desired folder; select it in Finder; use “Copy”; launch the Terminal; use “Paste.” The path name will appear at the command line; now copy again and paste it into the PYTHONPATH manager.)

You can now issue the command

from SGPniceGraphics import *

in your Python scripts.

Functions and Parameters Defined in the Module

Pfixplot

By default, Matplotlib’s graphs sit on top of a background panel, which is at best clutter if you are embedding a figure in some other document. By default, Matplotlib’s graphs are also surrounded by a box consisting of four “spines” and their tick marks. You may not want that style.

After you create a figure, use my_axis=plt.gca() to access its axis object. Then Pfixplot(my_axis) will make the plot background transparent. It also removes the top and right axes (“spines”).

Pfixlegend

If you create a legend, for example by

my_legend = my_axis.legend(mylabels)

then by default, the legend will also sit on top of a background panel (i.e., an opaque background), which may obscure something behind it and in any case is clutter.

After you create the legend, you can use Pfixlegend(my_legend) to make the legend background transparent.

Pcircsize

Matplotlib’s default choice for circle markers is bigger than that for asterisk markers, and in my opinion too big. When you create a graph plt.plot, using the marker option ‘o’, you can add the keyword argument ,markersize=Pcircsize to make the circles smaller.

Psavefig

The command

Psavefig('myfilename.pdf')

saves the current figure to a disk file. It just issues plt.savefig with some useful options. After saving, you can edit this vector-graphics file in your favorite art software, such as Inkscape (free) or Adobe Illustrator (not free). You may instead prefer to save in the .svg format, depending on what art software you use.

Fixing Crowded Graphs

If you try to make a graph that’s small enough for a journal article, particularly if it has multiple panels, Matplotlib may crowd it too much. For example, labels may collide with other things. One approach is to resize the graph by hand with the mouse before saving it. But maybe you already specified the size you want, and don’t want some other size! Before you start tweaking everything by hand, first try issuing the command

plt.tight_layout()

Often this persuades Matplotlib to do what you want.

Saving Graphs for Modification in Other Art Software

Graphs are “line art.” That is, they consist of geometrical objects like lines, circles, and text. For such graphics, you’ll get the best results if you save them in one of the “vector graphics” formats, because then they can be reproduced at any size without loss of quality. In contrast, a photograph is “bitmap art” — an array of individual pixels — and must be saved in one of the “raster image” formats. Converting from the pixellated image stored in such an image to the pixels actually used in the printer or screen, at the specified magnification, requires interpolation and can result in loss of quality. Line art can be converted to and saved in a raster format, but it’s a one-way process.

File formats like .tif and .jpg are strictly raster. File formats like .eps, .svg, and .pdf can accommodate either vector or raster objects, or both in the same file. If you save a graph from Matplotlib in .eps, .svg, or .pdf format, its vector character will be preserved and you can then edit the file in a vector-art program like Inkscape or Adobe Illustrator. That’s what we recommend.

Even if you save your graph as a .pdf or .svg file, however, text labels (title, axis labels, tick labels, etc.) will probably be rendered as “outlines.” These are genuine vector art, and will remain crisp upon reduction or enlargement. However, they are not “type,” that is, you can’t easily edit them in another software, change font, and so on. If you want that functionality, you’ll need to override a default parameter setting in Matplotlib. One way to do this, for .pdf files, is to include the line

matplotlib.rcParams['pdf.fonttype'] = 42

in your “rc” file (see below). For .svg files, the corresponding line is

matplotlib.rcParams['svg.fonttype'] = 'none'

Even with these lines, your art software may complain that it isn’t able to access (and hence to edit) some of the fonts used in the graphics file. The following step may be needed:

Duplicate all the folders in

~/anaconda/lib/python3.4/site-packages/matplotlib/mpl-data/fonts

and put the copies in a system font folder. In MacOSX, it’s

~/Library/Fonts

(Troubleshooting tips for Mac OS X can be found here.) In Windows, it’s

c:/windows/fonts 

For our installation, the folders in question were called afm/, pdfcorefonts/, and ttf/.

Finally, you can find other advice on fonts here and here. To implement their suggestions, use the following commands:

matplotlib.rcParams['ps.useafm'] = True
matplotlib.rcParams['pdf.use14corefonts'] = True
matplotlib.rcParams['text.usetex'] = True

These commands force the use of Type 1 fonts in saved figures.

Typesetting Labels with LaTeX

You can ask Matplotlib to render all your axis labels etc. using the LaTeX typesetting system. If you know that system, this option can be especially nice for graphics that you intend to embed in a bigger LaTeX document. To choose this option, use these two lines prior to creating any graph:

plt.rc('text', usetex=True)
plt.rc('font', family='serif')

Note that these lines may make your code run slowly. You may wish to leave them commented out until your graph nears its final form.

Even with the above lines, you may find that some labels come out in non-LaTeX fonts. You can try setting them in LaTeX math mode, like this:

ax.set_xlabel(r'$\mathrm{wavelength,\ \ [}\mathsf{nm}\mathrm{]}$')
ax.set_ylabel(r'$\mathrm{reflectance}$')

Notice the “raw string” syntax

r'stuff'

Placing the letter ‘r’ in front of a string means that we don’t need to escape the backslashes, which in this context will be sent to LaTeX for processing.

Other matplotlibrc Settings

A vast number of style settings have defaults that can be changed. For details, visit http://matplotlib.org/users/customizing.html.

Briefly, alternatives to the default settings are maintained in an initialization file called matplotlibrc. You can download a copy of this file here.

Alternatively, proceed as follows. At the IPython prompt, type

import matplotlib as mpl
mpl.get_configdir()

Python will give you a path to a folder containing your initialization file. If that folder’s name, or any component of its path, begins with a period, it may be “hidden” (invisible to your computer’s regular file finder). However, you can copy the path from the IPython console, open a terminal window, and change directory to it, for example

$ cd <<paste path to your folder here>>
$ cp matplotlibrc ~/
$ cp matplotlibrc backup_matplotlibrc

The first line above changes directory (cd) to the hidden folder. The second line duplicates (copies, “cp”) the initialization file and puts the new copy in your home directory (abbreviated “~/”), where you can see it. The third line makes a backup, just in case. (The dollar signs are command prompts; you don’t type them.)

Whichever of these methods you use to obtain matplotlibrc, it’s a plain-text file that you can examine in your text editor. It is well documented, but most of it is commented out. If you see a default setting that you’d like to change, you have two options:

  1. You can uncomment lines, change values to what you’d like, save the file, and copy it back to where Python expects to find it:

    $ cp ~/matplotlibrc <>

    You may want to use this option if you have a big project and want to enforce uniform graph style throughout it. Restart IPython after this step.

  2. You can instead leave the matplotlibrc file alone and issue override commands within your individual Python scripts. For example,

    import matplotlib as mpl
    mpl.rcParams['lines.linewidth'] = 2
    

Some other useful overrides were mentioned in “Typesetting labels with LaTeX” and “Saving graphs for modification in other art software” above. In addition, for publication you probably will want to override the default font size for labels, and so on.

CMYK Color

There are various ways to specify color. Some publishers demand that submitted color graphics be supplied in the “CMYK” color space, because it corresponds (a bit) more closely to printer inks than the “RGB” color space (which corresponds a bit more closely to the signals sent to a monitor). Matplotlib does not seem to support CMYK color directly. You must postprocess your graphics with some art program, for example Adobe Illustrator for vector graphics like those discussed in this post. Other postprocessing recipes can be found via a web search.

Exporting Raster Images

You may wish to visualize a matrix of numbers as a grayscale image. Chapter 7 of the book recommends using the function plt.imshow for this purpose. You may then wish to draw additional things on your image, for example by using plt.plot, and then save it to a pdf file with plt.savefig. A problem arises, however, if (as often happens with CCD camera data) your pixel grid is very coarse. The figure will look fine in the Python figure window, and it may even look fine when you save it to a file and open it in certain applications, such as Adobe Illustrator. Nevertheless, when embedded into a document, for example using LaTeX, it may look terrible: A very obvious new grid, finer than your pixels but much coarser than the standard 300 pixels per inch, has been imposed on the image.

Here is one method to address this problem. In Illustrator, create a new layer behind the main one. Delete the pixel part of your image from the main layer and paste it (<shift-cmd-V>) to the new layer. Lock the main layer. Now select the image and choose Edit > Rasterize. Choose an appropriate resolution (at least 300 ppi). Then save the image.

Comments

Unknown said…
As an alternative you can also export the data and plot it in LaTeX with Pgfplots. It requires some more effort but can provide neat results.
Congratulations for the amazing book.
Where can I find the SGPniceGraphics module?
Thank you very much.
Jesse said…
Thanks for bringing this to our attention. The link in the post above should now take you to the SGPniceGraphics.py module.

If not, you can access it here:

https://drive.google.com/open?id=0B2xzR6o-pol_N0lVaE5ZbHZ4UGM

Popular posts from this blog

Paths in Python

How do you get your Python interpreter to find modules that are not located in your current working directory? The answer is … you tell it where to look. When you type something like from my_module import my_function Python searches a collection of directories (i.e., folders) on your computer. If the directory containing <my_module.py> is not in this collection, you will receive an ImportError . This can be frustrating if you know the file exists, and even more so if you know where it exists. In this post, we will take a brief look at how to add paths to the collection of directories searched by Python. Paths A path is essentially a set of directions to a file: /Users/username/modules/my_module.py [Mac OS X, Linux] C:\modules\my_module.py [Windows] It tells your operating system how to navigate from a fixed starting point — the “root directory” / in Unix-based systems, or C:\ in Windows — through a collection of folders to the de

Illuminating Surface Plots

Matplotlib provides functions for visualizing three-dimensional data sets. One useful tool is a surface plot. A surface plot is a two-dimensional projection of a three-dimensional object. Much like a sketch artist, Python uses techniques like perspective and shading to give the illusion of a three-dimensional object in space. In this post, I describe how you can control the lighting of a surface plot. Surface Plots First, let’s look at some of the options available with the default three-dimensional plotting tools. This script will create a surface plot of a Bessel function. Its ripples will emphasize the effects of lighting later. import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # Import 3D plotting tools. from scipy.special import jn # Import Bessel function. # Define grid of points. points = np.linspace(-10, 10, 51) X, Y = np.meshgrid(points, points) R = np.sqrt(X**2 + Y**2) Z = jn(0,R) # Create 3D surface plo

Raising a Figure Window to the Foreground

This post describes a utility function that will raise a plot window to the foreground of your screen. The function will only work with the Qt graphics backend, so I will start with a brief overview of graphics backends. If you just want to use the function as a black box, you can do the following: Set the Graphics Backend to “Qt” in the Spyder preferences menu. Copy this function into your working directory. Graphics Backends You may have have written a script to produce precisely the data you need, but a lot of processing is required to transform these numbers into a figure. You need to create a plot window, draw a figure inside of it, and manage all of the attributes that control a figure’s appearance: title, axis labels, line widths, colors, tick marks, etc. All of this happens in the background when you type a command like plt.plot(x,y) . A graphics backend is the software that Python uses to physically draw a figure on your computer screen. If you have already import