Customizing Matplotlib

I have been using matplotlib a lot for my static plotting, and although it's a great free open source plotting package, I've complained in the past about the cumbersome API. We need to get all low-level with axes, fonts, etc. like that because all of the default plot options frankly basically suck across the board. Luckily, matplotlib allows you to modify the default options in a central file, so you can change your default figures to suck less. Here's the official how-to for customizing matplotlib, but we can get to a decent plot with a tiny fraction of the options they discuss there.

Here is my entire matplotlibrc file, in order of the variables' appearance in the sample file at the matplotlib link above.

lines.linewidth   :  2.0     # line width in points
font.size           : 18.0
text.color          : 777777
axes.facecolor      : f7f7f5   # axes background color
axes.edgecolor      : 111111   # axes edge color
axes.grid           : True   # display grid or not
axes.titlesize      : 18   # fontsize of the axes title
axes.labelsize      : 18  # fontsize of the x any y labels
axes.labelcolor     : 777777
axes.color_cycle    : 268bd2, dc322f, 859900, b58900, 
                      d33682, 2aa198, cb4b16, 002b36
xtick.color          : 777777      # color of the tick labels
ytick.color          : 777777      # color of the tick labels
grid.color       :   777777   # grid color
legend.fontsize      : 18
figure.figsize   : 11, 8    # figure size in inches
figure.facecolor : f7f7f5    # figure facecolor
figure.edgecolor : None   # figure edgecolor
savefig.facecolor   : f7f7f5    # facecolor when saving
savefig.edgecolor   : None   # edgecolor when saving
backend             : TkAgg

In addition to styling the plots (background color, text color/size) I also set the list of colors that line plots will cycle through to a set of colors from the solarized palette, instead of the default pure red/green/blue.

Every plot will share these settings now, and therefore hopefully suck less, but also can be produced with a fraction of the code. From the post I linked earlier, here's how the plot calls would change:

Previously (old and busted):

import matplotlib.pyplot as plt

""" do some stuff here to calculate things """

# color vars
bg = "#f7f7f5"
color = "#252525"
gray = "#777777"

# make a fig
fig = plt.figure(figsize=(11, 8), facecolor=bg)

# plot
plt.plot(testNum, tRecElapsed, '-', color="#dc322f", 
  linewidth=2.0, label='Recursive')
plt.plot(testNum, tAggElapsed, '-', color="#b58900", 
  linewidth=2.0, label='Aggregate')
plt.plot(testNum, tClfElapsed, '-', color="#268bd2", 
  linewidth=2.0, label='Closed Form')

# color settings
ax = plt.gca()
ax.set_axis_bgcolor(bg)
ax.spines['left'].set_color(color)
ax.spines['right'].set_color(color)
ax.spines['bottom'].set_color(color)
ax.spines['top'].set_color(color)
ax.xaxis.label.set_color(color)
ax.tick_params(axis='x', colors=color)
ax.yaxis.label.set_color(color)
ax.tick_params(axis='y', colors=color)

# axis ranges and names
plt.axis([0, nFibs, -1, max(tRecElapsed)+1])
plt.xlabel('nth Fibonacci Number', fontsize=18)
plt.ylabel('Time to Compute, s', fontsize=18)
plt.grid(True, color=gray)

# legend
leg = plt.legend(loc=2)
for text in leg.get_texts():
    text.set_color(color)
frame = leg.get_frame()
frame.set_facecolor(bg)
frame.set_edgecolor(color)

# save it off
fig.savefig('fibonacci.png', 
  facecolor=fig.get_facecolor(),
  edgecolor='none')

Now (new hotness):

import matplotlib.pyplot as plt

""" do some stuff here to calculate things """

# plot
fig = plt.figure()

# plotting itself is the same
plt.plot(testNum, tRecElapsed, '-', label='Recursive')
plt.plot(testNum, tAggElapsed, '-', label='Aggregate')
plt.plot(testNum, tClfElapsed, '-', label='Closed Form')

# axis ranges too
plt.axis([0, nFibs, -1, max(tRecElapsed)+1])

# axis titles
plt.xlabel('nth Fibonacci Number')
plt.ylabel('Time to Compute, s')

# legend
plt.legend(loc=2)

# save it off
fig.savefig('fibonacci2.png')

(Notice how we don't even have to specify color anymore, since we're looping through good colors instead of the shitty stock red, green, blue).

We can also compare the default settings to either of the above code snippets (they will produce the same plot, difference is syntactical only):

Default matplotlib:

Modified:

Infinitely better.

Moral of the story, the default options in matplotlib (and many other plot packages) are really bad. Luckily matplotlib makes it easy to set some decent defaults; but for an even more thorough improvement wrapping matplotlib with something like seaborn looks pretty promising.