When I first started using matplotlib, the output looked very crisp and polished compared to excel, however after seeing ggplot2, I realized that matplotlib’s default presentation settings leave a lot to be desired. I have put together a quick script that will restyle an axes to look more or less like ggplot2′s.
"""Styles an axes to appear like ggplot2
Must be called after all plot and axis manipulation operations have been carried out (needs to know final tick spacing)
"""
#set the style of the major and minor grid lines, filled blocks
ax.grid(True, 'major', color='w', linestyle='-', linewidth=1.4)
ax.grid(True, 'minor', color='0.92', linestyle='-', linewidth=0.7)
ax.patch.set_facecolor('0.85')
ax.set_axisbelow(True)
#set minor tick spacing to 1/2 of the major ticks
ax.xaxis.set_minor_locator(MultipleLocator( (plt.xticks()[0][1]-plt.xticks()[0][0]) / 2.0 ))
ax.yaxis.set_minor_locator(MultipleLocator( (plt.yticks()[0][1]-plt.yticks()[0][0]) / 2.0 ))
#remove axis border
for child in ax.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_alpha(0)
#restyle the tick lines
for line in ax.get_xticklines() + ax.get_yticklines():
line.set_markersize(5)
line.set_color("gray")
line.set_markeredgewidth(1.4)
#remove the minor tick lines
for line in ax.xaxis.get_ticklines(minor=True) + ax.yaxis.get_ticklines(minor=True):
line.set_markersize(0)
#only show bottom left ticks, pointing out of axis
rcParams['xtick.direction'] = 'out'
rcParams['ytick.direction'] = 'out'
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
if ax.legend_ <> None:
lg = ax.legend_
lg.get_frame().set_linewidth(0)
lg.get_frame().set_alpha(0.5)
def rhist(ax, data, **keywords):
"""Creates a histogram with default style parameters to look like ggplot2
Is equivalent to calling ax.hist and accepts the same keyword parameters.
If style parameters are explicitly defined, they will not be overwritten
"""
defaults = {
'facecolor' : '0.3',
'edgecolor' : '0.28',
'linewidth' : '1',
'bins' : 100
}
for k, v in defaults.items():
if k not in keywords: keywords[k] = v
return ax.hist(data, **keywords)
def rbox(ax, data, **keywords):
"""Creates a ggplot2 style boxplot, is eqivalent to calling ax.boxplot with the following additions:
Keyword arguments:
colors -- array-like collection of colours for box fills
names -- array-like collection of box names which are passed on as tick labels
"""
hasColors = 'colors' in keywords
if hasColors:
colors = keywords['colors']
keywords.pop('colors')
if 'names' in keywords:
ax.tickNames = plt.setp(ax, xticklabels=keywords['names'] )
keywords.pop('names')
bp = ax.boxplot(data, **keywords)
pylab.setp(bp['boxes'], color='black')
pylab.setp(bp['whiskers'], color='black', linestyle = 'solid')
pylab.setp(bp['fliers'], color='black', alpha = 0.9, marker= 'o', markersize = 3)
pylab.setp(bp['medians'], color='black')
numBoxes = len(data)
for i in range(numBoxes):
box = bp['boxes'][i]
boxX = []
boxY = []
for j in range(5):
boxX.append(box.get_xdata()[j])
boxY.append(box.get_ydata()[j])
boxCoords = zip(boxX,boxY)
if hasColors:
boxPolygon = Polygon(boxCoords, facecolor = colors[i % len(colors)])
else:
boxPolygon = Polygon(boxCoords, facecolor = '0.95')
ax.add_patch(boxPolygon)
return bp
Usage is very simple, call rstyle(axes) just before showing or saving your figure. It is key to call it after all drawing and axis manipulation has been done, because it will be reading the major tick positions to work out where to put the minors.
import scipy.stats
t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)
fig = plt.figure()
ax = fig.add_subplot(111)
plot(t,s, label = "Original")
plot(t,s*2, label = "Doubled")
ax.legend()
rstyle(ax)
plt.show()
I have also included a function that creates a ggplot style histogram for you. This is nothing more than setting some default parameters to the hist function.
import scipy.stats
t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)
fig = plt.figure()
ax = fig.add_subplot(111)
data = scipy.stats.norm.rvs(size = 1000)
rhist(ax, data, label = "Histogram")
ax.legend()
rstyle(ax)
plt.show()
There is also a slightly more involved boxplot function which handles fill colours and names for you.
import scipy.stats
data = [scipy.stats.norm.rvs(size = 100), scipy.stats.norm.rvs(size = 100), scipy.stats.norm.rvs(size = 100)]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.legend()
rbox(ax, data, names = ("One", "Two", "Three"), colors = ('white', 'cyan'))
rstyle(ax)
Finally, with a bit of help from Justin Peel over at StackOverflow, you can get some really nice graphics going that you won’t be ashamed to put in your published material or presentation.
I have only used these scripts in my fairly limited scenario and there are several obvious things such as the requirement to pass an axes, the enforcement of minor ticks at 1/2 majors, and the fact that I haven’t really done much with the legend, but it should be enough to get you started in your projects. Happy visualizating!