Beautifying ROOT with matplotlib

The goal of root2matplotlib is to enable easy plotting of ROOT histograms using the full-featured and mature matplotlib library.

Some possibilities in matplotlib that are unavailable in ROOT include transparent fills and text output using LaTeX.

You may want to use root2matplotlib to achieve effects or complex diagrams that would be difficult or impossible in ROOT, or you may simply want to recreate a ROOT figure with the higher-quality text available through a LaTeX engine.

For immediate figures with a minimum of effort (with output directly from ROOT or through matplotlib, take a look at the section on the rootplot command-line tool.

Examples

First plot

A first example:

import rootplot.root2matplotlib as r2m
import ROOT
from matplotlib import pyplot as plt
from random import gauss

th1f = ROOT.TH1F("hpx", "Distribution of p_{x};p_{x};Events", 40, -4, 4)
for i in range(25000):
    th1f.Fill(gauss(0., 1.))

# Make a figure with width 6 inches and height 4 inches
plt.figure(1, (8, 6))
# Create an axes instance
ax1 = plt.axes()
hist = r2m.Hist(th1f)
# Plot a bar chart in red
hist.bar(color='r')
hist.show_titles()

plt.savefig('first')
plt.show()

[source code, hires.png, pdf]

_images/first11.png

So far, this is probably not the prettiest output you’ve seen, but we’re relying on all of matplotlib‘s defaults. The real power of the solution is the amount of customization that’s possible, and how comparitively easy it is to achieve the results you desire.

Notice that ROOT’s simplified TeX-esque syntax doesn’t isn’t understood by matplotlib. By default, matplotlib uses an internal typesetting engine that allows simple expressions following the official TeX syntax, using $ to denote math mode and \ characters to start commands. To handle conversion from the names you gave the histograms in ROOT to names that are compatible with matplotlib, you can provide a list of ordered pairs in the show_titles method with the replacements keyword:

replacements = [('p_{x}', r'$p_x$'),
                ('#eta', r'$\eta$')]
hist.bar(color='r')
hist.show_titles(replacements=replacements)

This will replace all instances of p_{x} with $p_x$, generating the correct LaTeX expressions, and you should give it a try. Notice the r prefix on the second string, which denotes a “raw” string. In this case, it doesn’t matter, but if the LaTeX string were to contain any commands using \, a normal string would interpret these as escaped characters.

Now, let’s add the replacements above and make a more ROOT-style filled histogram with a single line along the top of the bins:

import rootplot.root2matplotlib as r2m
import ROOT
import matplotlib
from matplotlib import pyplot as plt
from random import gauss

th1f = ROOT.TH1F("hpx", "Distribution of p_{x};p_{x};Events", 40, -4, 4)
for i in range(25000):
    th1f.Fill(gauss(0., 1.))

plt.figure()
replacements = [('p_{x}', r'$p_x$'),
           ('#eta', r'$\eta$')]
hist = r2m.Hist(th1f)
hist.hist(color='r', histtype='stepfilled')
hist.show_titles(replacements=replacements)
plt.savefig('firstrep', dpi=50)
plt.show()

[source code, hires.png, pdf]

_images/firstrep11.png

A more complicated example

Imagine that you are preparing a LaTeX document using the mathpazo package, which sets up Palatino as the default font. With matplotlib, you can generate an output PS or PDF that has all its text rendered with the full-blown LaTeX distribution on your system, so that your plots match exactly with the rest of your document. The following example loads the PDF backend, and enables the text.usetex option to tell matplotlib to use your real LaTeX engine. It also highlights some extra matplotlib functions, like transparent fills, legends, and colormaps:

import matplotlib
## matplotlib.use("PDF") ## Include this line to make PDF output
matplotlib.rc('text', usetex=True)
matplotlib.rc('font', family="serif", serif="palatino")
from matplotlib import pyplot as plt
import ROOT
import rootplot.root2matplotlib as r2m
from random import gauss

th1f_1 = ROOT.TH1F("hpt1", "Distribution of pT;pT;Events", 40, 0, 8)
th1f_2 = ROOT.TH1F("hpt2", "Distribution of pT;pT;Events", 40, 0, 8)
for i in range(2500):
    th1f_1.Fill(gauss(4, 1.))
    th1f_2.Fill(gauss(6, 1.))

cmap = plt.get_cmap('Set3')
colors = [cmap(i/5.) for i in range(5)]

plt.figure(1, (6, 4))
ax1 = plt.axes()
replace = [('pT', r'$p_\mathrm{T}$')]
hist1 = r2m.Hist(th1f_1)
hist2 = r2m.Hist(th1f_2)
stack = r2m.HistStack()
stack.add(hist1, color=colors[0], label=r"$Z\rightarrow \mu\mu$")
stack.add(hist2, color=colors[1], label=r"$t\bar{t}$")
stack.bar(alpha=0.5)
stack.show_titles(replacements=replace)
plt.legend(loc='upper left')
plt.xlim(0,8)

plt.show()
## plt.savefig('second') ## Include this line to save image

[source code, hires.png, pdf]

_images/second11.png

A Two-Dimensional Histogram

root2matplotlib also supports 2D histograms. A Hist2D object has functions such as box, col, or colz to replicate ROOT drawing options, but also the ability to make contour plots:

import rootplot.root2matplotlib as r2m
import ROOT
from matplotlib import pyplot as plt
from random import gauss

th2f = ROOT.TH2F("data", "", 20, -3, 3, 20, -3, 3)
for i in range(20000):
    th2f.Fill(gauss(0., 1.), gauss(0., 1.))

hist = r2m.Hist2D(th2f)
fig = plt.figure(figsize=[6,3])
ax1 = plt.subplot(121)
hist.colz()
ax2 = plt.subplot(122)
hist.contour()

plt.show()

[source code, hires.png, pdf]

_images/demo2D11.png

The root2matplotlib Library

Utilities for plotting ROOT histograms in matplotlib.

class rootplot.root2matplotlib.Hist(*args, **kwargs)[source]

A container to hold the parameters from a ROOT histogram.

bar(xerr=False, yerr=False, xoffset=0.0, width=0.8, label_rotation=0, label_alignment='center', **kwargs)[source]

Generate a matplotlib bar figure.

All additional keyword arguments will be passed to matplotlib.pyplot.bar().

barh(xerr=False, yerr=False, yoffset=0.0, width=0.8, label_rotation=0, label_alignment='center', **kwargs)[source]

Generate a horizontal matplotlib bar figure.

All additional keyword arguments will be passed to matplotlib.pyplot.bar().

errorbar(xerr=False, yerr=False, label_rotation=0, label_alignment='center', **kwargs)[source]

Generate a matplotlib errorbar figure.

All additional keyword arguments will be passed to matplotlib.pyplot.errorbar().

errorbarh(xerr=False, yerr=False, label_rotation=0, label_alignment='center', **kwargs)[source]

Generate a horizontal matplotlib errorbar figure.

All additional keyword arguments will be passed to matplotlib.pyplot.errorbar().

hist(label_rotation=0, label_alignment='center', **kwargs)[source]

Generate a matplotlib hist figure.

All additional keyword arguments will be passed to matplotlib.pyplot.hist().

show_titles(**kwargs)[source]

Print the title and axis labels to the current figure.

class rootplot.root2matplotlib.Hist2D(*args, **kwargs)[source]

A container to hold the parameters from a 2D ROOT histogram.

TH2F(name='')[source]

Return a ROOT.TH2F object with contents of this Hist2D.

box(maxsize=40, **kwargs)[source]

Draw a box plot with size indicating content using matplotlib.pyplot.scatter().

The data will be normalized, with the largest box using a marker of size maxsize (in points).

col(**kwargs)[source]

Draw a colored box plot using matplotlib.pyplot.imshow().

colz(**kwargs)[source]

Draw a colored box plot with a colorbar using matplotlib.pyplot.imshow().

contour(**kwargs)[source]

Draw a contour plot.

contourf(**kwargs)[source]

Draw a contourf plot.

class rootplot.root2matplotlib.HistStack(*args, **kwargs)[source]

A container to hold Hist objects for plotting together.

When plotting, the title and the x and y labels of the last Hist added will be used unless specified otherwise in the constructor.

bar(**kwargs)[source]

Make a bar plot, with all Hists in the stack overlaid.

Any additional keyword arguments will be passed to matplotlib.pyplot.bar(). You will probably want to set a transparency value (i.e. alpha = 0.5).

barcluster(width=0.8, **kwargs)[source]

Make a clustered bar plot.

Any additional keyword arguments will be passed to matplotlib.pyplot.bar().

barh(width=0.8, **kwargs)[source]

Make a horizontal clustered matplotlib bar plot.

Any additional keyword arguments will be passed to matplotlib.pyplot.bar().

barstack(**kwargs)[source]

Make a matplotlib bar plot, with each Hist stacked upon the last.

Any additional keyword arguments will be passed to matplotlib.pyplot.bar().

errorbar(offset=False, **kwargs)[source]

Make a matplotlib errorbar plot, with all Hists in the stack overlaid.

Passing ‘offset=True’ will slightly offset each dataset so overlapping errorbars are still visible. Any additional keyword arguments will be passed to matplotlib.pyplot.errorbar().

errorbarh(**kwargs)[source]

Make a horizontal matplotlib errorbar plot, with all Hists in the stack overlaid.

Any additional keyword arguments will be passed to matplotlib.pyplot.errorbar().

hist(label_rotation=0, **kwargs)[source]

Make a matplotlib hist plot.

Any additional keyword arguments will be passed to matplotlib.pyplot.hist(), which allows a vast array of possibilities. Particlularly, the histtype values such as 'barstacked' and 'stepfilled' give substantially different results. You will probably want to include a transparency value (i.e. alpha = 0.5).

histstack(**kwargs)[source]

Make a matplotlib hist plot, with each Hist stacked upon the last.

Any additional keyword arguments will be passed to matplotlib.pyplot.hist().

class rootplot.root2matplotlib.RootFile(filename, name=None)[source]

A wrapper for TFiles, allowing easier access to methods.

rootplot.root2matplotlib.replace(string, replacements)[source]

Modify a string based on a list of patterns and substitutions.

replacements should be a list of two-entry tuples, the first entry giving a string to search for and the second entry giving the string with which to replace it. If replacements includes a pattern entry containing ‘use_regexp’, then all patterns will be treated as regular expressions using re.sub.