Creation of paper-ready plots with matlotlib

2013-10-03
#matplotlib #python #howto #latex #plots

I used to utilize gnuplot for creation of plots with arbitrary data. The main problem was to postprocess data before plotting: I had to integrate postprocess into calculation code itself or to write additional script to do so. I’d like to provide an example of good-looking plotting with Python and matplotlib. Python has bataries included, so there will be no problem with preparation of data to be plotted.

Example script plot_example.py:

#~/usr/bin/env python
#-*- coding: utf-8 -*-

import matplotlib.pyplot as plt

# set global settings
def init_plotting():
    plt.rcParams['figure.figsize'] = (8, 3)
    plt.rcParams['font.size'] = 10
    plt.rcParams['font.family'] = 'Times New Roman'
    plt.rcParams['axes.labelsize'] = plt.rcParams['font.size']
    plt.rcParams['axes.titlesize'] = 1.5*plt.rcParams['font.size']
    plt.rcParams['legend.fontsize'] = plt.rcParams['font.size']
    plt.rcParams['xtick.labelsize'] = plt.rcParams['font.size']
    plt.rcParams['ytick.labelsize'] = plt.rcParams['font.size']
    plt.rcParams['savefig.dpi'] = 2*plt.rcParams['savefig.dpi']
    plt.rcParams['xtick.major.size'] = 3
    plt.rcParams['xtick.minor.size'] = 3
    plt.rcParams['xtick.major.width'] = 1
    plt.rcParams['xtick.minor.width'] = 1
    plt.rcParams['ytick.major.size'] = 3
    plt.rcParams['ytick.minor.size'] = 3
    plt.rcParams['ytick.major.width'] = 1
    plt.rcParams['ytick.minor.width'] = 1
    plt.rcParams['legend.frameon'] = False
    plt.rcParams['legend.loc'] = 'center left'
    plt.rcParams['axes.linewidth'] = 1

    plt.gca().spines['right'].set_color('none')
    plt.gca().spines['top'].set_color('none')
    plt.gca().xaxis.set_ticks_position('bottom')
    plt.gca().yaxis.set_ticks_position('left')

init_plotting()

# plotting example data
from math import sin
from math import cos

x = [0.31415*xi for xi in xrange(0,10)]
y1 = [sin(xi) for xi in x]
y2 = [cos(xi + 0.5) for xi in x]
y3 = [cos(xi + 0.5) + sin(xi) for xi in x]

# begin subplots region
plt.subplot(121)
plt.gca().margins(0.1, 0.1)
plt.plot(x, y1, linestyle='-', marker='.', linewidth=1, color='r', label='sin')
plt.plot(x, y2, linestyle='.', marker='o', linewidth=1, color='b', label='cos')

plt.gca().annotate(u'point $\\frac{\\tau}{2}$', xy=(x[2], y1[2]),  xycoords='data',
                xytext=(30, -10), textcoords='offset points', size=8,
                arrowprops=dict(arrowstyle='simple', fc='g', ec='none'))

plt.xlabel(u'x label')
plt.ylabel(u'y label')
plt.title(u'First plot title')

plt.gca().legend(bbox_to_anchor = (0.0, 0.1))

plt.subplot(122)
plt.gca().margins(0.1, 0.1)
plt.plot(x, y3, linestyle='--', marker='.', linewidth=1, color='g', label='sum')

plt.gca().annotate(u'$y_x$', xy=(x[2], y3[2]),  xycoords='data',
                xytext=(-30, -20), textcoords='offset points', size=8,
                arrowprops=dict(arrowstyle='simple', fc='orange', ec='none'))

plt.xlabel(u'x label')
plt.ylabel(u'y label')
plt.title(u'Second plot title')

plt.gca().legend(bbox_to_anchor = (0.0, 0.1))
# end subplots region

# output resulting plot to file
plt.tight_layout()
plt.savefig('graph.png')

I’ve set all important parameters for me: font, size of font, width of different types of lines, titles, labels, so you can change them to fit yourself. PNG output can be changed to EPS or PDF ones.

First part of script is an init function. It sets some global matplotlib parameters (so you do not need to edit your matplotlib rc file). You can edit parameters.

Second part is preparation of data to be plotted. You can replace it completely with your own code. For example, to load data from file.

Third part is creating plots. You can add only one plot at figure (to do so, remove second plot and subplot statements). You can add more plots at one figure (change subplot accordingly. Three numbers are rows of plots, columns of plots and the plot number. For 2x2 plots use subplot(22x) where x is plot number.)

Result: