2 Utilities for plotting ROOT histograms in matplotlib.
6 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 from rootplot
import utilities
35 import matplotlib
as mpl
36 import matplotlib.pyplot
as plt
37 import matplotlib.transforms
as transforms
42 _all_whitespace_string = re.compile(
r'\s*$')
48 """A container to hold the parameters from a 2D ROOT histogram."""
51 if 'replacements' in kwargs:
53 utilities.Hist2D.__init__(self, *args, **kwargs)
55 """Draw a contour plot."""
56 cs = plt.contour(self.
x, self.
y, self.
content, **kwargs)
57 plt.clabel(cs, inline=1, fontsize=10)
63 def col(self, **kwargs):
64 """Draw a colored box plot using :func:`matplotlib.pyplot.imshow`."""
66 kwargs[
'cmap'] = plt.get_cmap(kwargs[
'cmap'])
67 plot = plt.imshow(self.
content, interpolation=
'nearest',
70 aspect=
'auto', origin=
'lower', **kwargs)
74 Draw a colored box plot with a colorbar using
75 :func:`matplotlib.pyplot.imshow`.
77 plot = self.
col(**kwargs)
80 def box(self, maxsize=40, **kwargs):
82 Draw a box plot with size indicating content using
83 :func:`matplotlib.pyplot.scatter`.
85 The data will be normalized, with the largest box using a marker of
86 size maxsize (in points).
88 x = np.hstack([self.
x for i
in range(self.
nbinsy)])
89 y = np.hstack([[yval
for i
in range(self.
nbinsx)]
for yval
in self.
y])
94 plot = plt.scatter(x, y, sizes, marker=
's', **kwargs)
97 """Return a ROOT.TH2F object with contents of this Hist2D."""
98 th2f = ROOT.TH2F(name,
"",
102 for ix
in range(self.
nbinsx):
103 for iy
in range(self.
nbinsy):
104 th2f.SetBinContent(ix + 1, iy + 1, self.
content[iy][ix])
108 """A container to hold the parameters from a ROOT histogram."""
111 if 'replacements' in kwargs:
113 utilities.Hist.__init__(self, *args, **kwargs)
115 """Apply bounds and text labels on x axis."""
119 rotation=rotation, ha=alignment)
123 """Apply bounds and text labels on y axis."""
127 rotation=rotation, va=alignment)
131 """Print the title and axis labels to the current figure."""
132 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
136 def hist(self, label_rotation=0, label_alignment='center', **kwargs):
138 Generate a matplotlib hist figure.
140 All additional keyword arguments will be passed to
141 :func:`matplotlib.pyplot.hist`.
143 kwargs.pop(
'fmt',
None)
144 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
148 weights = [1.e-10] * self.
nbins
149 plot = plt.hist(self.
x, weights=weights, bins=self.
xedges,
153 def errorbar(self, xerr=False, yerr=False, label_rotation=0,
154 label_alignment=
'center', **kwargs):
156 Generate a matplotlib errorbar figure.
158 All additional keyword arguments will be passed to
159 :func:`matplotlib.pyplot.errorbar`.
162 kwargs[
'xerr'] = self.
xerr
164 kwargs[
'yerr'] = self.
yerr
165 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
166 errorbar = plt.errorbar(self.
x, self.
y,
171 def errorbarh(self, xerr=False, yerr=False, label_rotation=0,
172 label_alignment=
'center', **kwargs):
174 Generate a horizontal matplotlib errorbar figure.
176 All additional keyword arguments will be passed to
177 :func:`matplotlib.pyplot.errorbar`.
179 if xerr: kwargs[
'xerr'] = self.
yerr
180 if yerr: kwargs[
'yerr'] = self.
xerr
181 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
182 errorbar = plt.errorbar(self.
y, self.
x,
187 def bar(self, xerr=False, yerr=False, xoffset=0., width=0.8,
188 label_rotation=0, label_alignment=
'center', **kwargs):
190 Generate a matplotlib bar figure.
192 All additional keyword arguments will be passed to
193 :func:`matplotlib.pyplot.bar`.
195 kwargs.pop(
'fmt',
None)
196 if xerr: kwargs[
'xerr'] = self.
av_xerr()
197 if yerr: kwargs[
'yerr'] = self.
av_yerr()
198 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
199 ycontent = [self.
xedges[i] + self.
width[i] * xoffset
200 for i
in range(len(self.
xedges) - 1)]
201 width = [x * width
for x
in self.
width]
202 bar = plt.bar(ycontent, self.
y, width,
206 def barh(self, xerr=False, yerr=False, yoffset=0., width=0.8,
207 label_rotation=0, label_alignment=
'center', **kwargs):
209 Generate a horizontal matplotlib bar figure.
211 All additional keyword arguments will be passed to
212 :func:`matplotlib.pyplot.bar`.
214 kwargs.pop(
'fmt',
None)
215 if xerr: kwargs[
'xerr'] = self.
av_yerr()
216 if yerr: kwargs[
'yerr'] = self.
av_xerr()
217 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
218 xcontent = [self.
xedges[i] + self.
width[i] * yoffset
219 for i
in range(len(self.
xedges) - 1)]
220 width = [x * width
for x
in self.
width]
221 barh = plt.barh(xcontent, self.
y, width,
229 A container to hold Hist objects for plotting together.
231 When plotting, the title and the x and y labels of the last Hist added
232 will be used unless specified otherwise in the constructor.
235 if 'replacements' in kwargs:
237 utilities.HistStack.__init__(self, *args, **kwargs)
240 def hist(self, label_rotation=0, **kwargs):
242 Make a matplotlib hist plot.
244 Any additional keyword arguments will be passed to
245 :func:`matplotlib.pyplot.hist`, which allows a vast array of
246 possibilities. Particlularly, the *histtype* values such as
247 ``'barstacked'`` and ``'stepfilled'`` give substantially different
248 results. You will probably want to include a transparency value
249 (i.e. *alpha* = 0.5).
251 contents = np.dstack([hist.y
for hist
in self.
hists])
252 xedges = self.
hists[0].xedges
253 x = np.dstack([hist.x
for hist
in self.
hists])[0]
254 labels = [hist.label
for hist
in self.
hists]
256 clist = [item[
'color']
for item
in self.
kwargs]
257 plt.gca().set_color_cycle(clist)
261 plot = plt.hist(x, weights=contents, bins=xedges,
262 label=labels, **kwargs)
265 from mpl_toolkits.mplot3d
import Axes3D
270 for i, hist
in enumerate(self.
hists):
271 if self.
title is not None: hist.title = self.
title
274 labels.append(hist.label)
275 all_kwargs = copy.copy(kwargs)
276 all_kwargs.update(self.
kwargs[i])
277 bar = ax.bar(hist.x, hist.y, zs=i, zdir=
'y', width=hist.width,
280 from matplotlib.ticker
import FixedLocator
281 locator = FixedLocator(range(len(labels)))
282 ax.w_yaxis.set_major_locator(locator)
283 ax.w_yaxis.set_ticklabels(labels)
284 ax.set_ylim3d(-1, len(labels))
288 Make a matplotlib bar plot, with each Hist stacked upon the last.
290 Any additional keyword arguments will be passed to
291 :func:`matplotlib.pyplot.bar`.
295 for i, hist
in enumerate(self.
hists):
296 if self.
title is not None: hist.title = self.
title
299 all_kwargs = copy.copy(kwargs)
300 all_kwargs.update(self.
kwargs[i])
301 bar = hist.bar(bottom=bottom, **all_kwargs)
303 if not bottom: bottom = [0.
for i
in range(self.
hists[0].nbins)]
304 bottom = [sum(pair)
for pair
in zip(bottom, hist.y)]
308 Make a matplotlib hist plot, with each Hist stacked upon the last.
310 Any additional keyword arguments will be passed to
311 :func:`matplotlib.pyplot.hist`.
316 for i, hist
in enumerate(self.
hists):
318 cumhist = hist + cumhist
320 cumhist = copy.copy(hist)
321 if self.
title is not None: cumhist.title = self.
title
322 if self.
xlabel is not None: cumhist.xlabel = self.
xlabel
323 if self.
ylabel is not None: cumhist.ylabel = self.
ylabel
324 all_kwargs = copy.copy(kwargs)
325 all_kwargs.update(self.
kwargs[i])
326 zorder = 0 + float(len(self) - i)/len(self)
327 plot = cumhist.hist(zorder=zorder, **all_kwargs)
332 Make a clustered bar plot.
334 Any additional keyword arguments will be passed to
335 :func:`matplotlib.pyplot.bar`.
338 spacer = (1. - width) / 2
339 width = width / len(self.
hists)
340 for i, hist
in enumerate(self.
hists):
341 if self.
title is not None: hist.title = self.
title
344 all_kwargs = copy.copy(kwargs)
345 all_kwargs.update(self.
kwargs[i])
346 bar = hist.bar(xoffset=width*i + spacer, width=width, **all_kwargs)
349 def barh(self, width=0.8, **kwargs):
351 Make a horizontal clustered matplotlib bar plot.
353 Any additional keyword arguments will be passed to
354 :func:`matplotlib.pyplot.bar`.
357 spacer = (1. - width) / 2
358 width = width / len(self.
hists)
359 for i, hist
in enumerate(self.
hists):
360 if self.
title is not None: hist.title = self.
title
363 all_kwargs = copy.copy(kwargs)
364 all_kwargs.update(self.
kwargs[i])
365 bar = hist.barh(yoffset=width*i + spacer, width=width, **all_kwargs)
370 Make a bar plot, with all Hists in the stack overlaid.
372 Any additional keyword arguments will be passed to
373 :func:`matplotlib.pyplot.bar`. You will probably want to set a
374 transparency value (i.e. *alpha* = 0.5).
377 for i, hist
in enumerate(self.
hists):
378 if self.
title is not None: hist.title = self.
title
381 all_kwargs = copy.copy(kwargs)
382 all_kwargs.update(self.
kwargs[i])
383 bar = hist.bar(**all_kwargs)
388 Make a matplotlib errorbar plot, with all Hists in the stack overlaid.
390 Passing 'offset=True' will slightly offset each dataset so overlapping
391 errorbars are still visible. Any additional keyword arguments will
392 be passed to :func:`matplotlib.pyplot.errorbar`.
395 for i, hist
in enumerate(self.
hists):
396 if self.
title is not None: hist.title = self.
title
399 all_kwargs = copy.copy(kwargs)
400 all_kwargs.update(self.
kwargs[i])
401 transform = plt.gca().transData
403 index_offset = (len(self.
hists) - 1)/2.
404 pixel_offset = 1./72 * (i - index_offset)
405 transform = transforms.ScaledTranslation(
406 pixel_offset, 0, plt.gcf().dpi_scale_trans)
407 transform = plt.gca().transData + transform
408 errorbar = hist.errorbar(transform=transform, **all_kwargs)
409 plots.append(errorbar)
413 Make a horizontal matplotlib errorbar plot, with all Hists in the
416 Any additional keyword arguments will be passed to
417 :func:`matplotlib.pyplot.errorbar`.
420 for i, hist
in enumerate(self.
hists):
421 if self.
title is not None: hist.title = self.
title
424 all_kwargs = copy.copy(kwargs)
425 all_kwargs.update(self.
kwargs[i])
426 errorbar = hist.errorbarh(**all_kwargs)
427 plots.append(errorbar)
433 """A wrapper for TFiles, allowing easier access to methods."""
434 def get(self, object_name, path=None):
436 return utilities.RootFile.get(self, object_name, path,
438 except ReferenceError
as e:
439 raise ReferenceError(e)
445 Modify a string based on a list of patterns and substitutions.
447 replacements should be a list of two-entry tuples, the first entry giving
448 a string to search for and the second entry giving the string with which
449 to replace it. If replacements includes a pattern entry containing
450 'use_regexp', then all patterns will be treated as regular expressions
455 if 'use_regexp' in [x
for x,y
in replacements]:
456 for pattern, repl
in [x
for x
in replacements
457 if x[0] !=
'use_regexp']:
458 string = re.sub(pattern, repl, string)
460 for pattern, repl
in replacements:
461 string = string.replace(pattern, repl)
462 if re.match(_all_whitespace_string, string):