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`."""
65 plot = plt.imshow(self.
content, interpolation=
'nearest',
68 aspect=
'auto', origin=
'lower', **kwargs)
72 Draw a colored box plot with a colorbar using
73 :func:`matplotlib.pyplot.imshow`.
75 plot = self.
col(**kwargs)
78 def box(self, maxsize=40, **kwargs):
80 Draw a box plot with size indicating content using
81 :func:`matplotlib.pyplot.scatter`.
83 The data will be normalized, with the largest box using a marker of
84 size maxsize (in points).
86 x = np.hstack([self.
x for i
in range(self.
nbinsy)])
87 y = np.hstack([[yval
for i
in range(self.
nbinsx)]
for yval
in self.
y])
92 plot = plt.scatter(x, y, sizes, marker=
's', **kwargs)
95 """Return a ROOT.TH2F object with contents of this Hist2D."""
96 th2f = ROOT.TH2F(name,
"",
100 for ix
in range(self.
nbinsx):
101 for iy
in range(self.
nbinsy):
102 th2f.SetBinContent(ix + 1, iy + 1, self.
content[iy][ix])
106 """A container to hold the parameters from a ROOT histogram."""
109 if 'replacements' in kwargs:
111 utilities.Hist.__init__(self, *args, **kwargs)
113 """Apply bounds and text labels on x axis."""
117 rotation=rotation, ha=alignment)
121 """Apply bound and text labels on y axis."""
125 rotation=rotation, va=alignment)
129 """Print the title and axis labels to the current figure."""
130 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
134 def hist(self, label_rotation=0, label_alignment='center', **kwargs):
136 Generate a matplotlib hist figure.
138 All additional keyword arguments will be passed to
139 :func:`matplotlib.pyplot.hist`.
141 kwargs.pop(
'fmt',
None)
142 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
146 weights = [1.e-10] * self.
nbins
147 plot = plt.hist(self.
x, weights=weights, bins=self.
xedges,
151 def errorbar(self, xerr=False, yerr=False, label_rotation=0,
152 label_alignment=
'center', **kwargs):
154 Generate a matplotlib errorbar figure.
156 All additional keyword arguments will be passed to
157 :func:`matplotlib.pyplot.errorbar`.
160 kwargs[
'xerr'] = self.
xerr
162 kwargs[
'yerr'] = self.
yerr
163 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
164 errorbar = plt.errorbar(self.
x, self.
y,
169 def errorbarh(self, xerr=False, yerr=False, label_rotation=0,
170 label_alignment=
'center', **kwargs):
172 Generate a horizontal matplotlib errorbar figure.
174 All additional keyword arguments will be passed to
175 :func:`matplotlib.pyplot.errorbar`.
177 if xerr: kwargs[
'xerr'] = self.
yerr
178 if yerr: kwargs[
'yerr'] = self.
xerr
179 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
180 errorbar = plt.errorbar(self.
y, self.
x,
185 def bar(self, xerr=False, yerr=False, xoffset=0., width=0.8,
186 label_rotation=0, label_alignment=
'center', **kwargs):
188 Generate a matplotlib bar figure.
190 All additional keyword arguments will be passed to
191 :func:`matplotlib.pyplot.bar`.
193 kwargs.pop(
'fmt',
None)
194 if xerr: kwargs[
'xerr'] = self.
av_xerr()
195 if yerr: kwargs[
'yerr'] = self.
av_yerr()
196 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
197 ycontent = [self.
xedges[i] + self.
width[i] * xoffset
198 for i
in range(len(self.
xedges) - 1)]
199 width = [x * width
for x
in self.
width]
200 bar = plt.bar(ycontent, self.
y, width,
204 def barh(self, xerr=False, yerr=False, yoffset=0., width=0.8,
205 label_rotation=0, label_alignment=
'center', **kwargs):
207 Generate a horizontal matplotlib bar figure.
209 All additional keyword arguments will be passed to
210 :func:`matplotlib.pyplot.bar`.
212 kwargs.pop(
'fmt',
None)
213 if xerr: kwargs[
'xerr'] = self.
av_yerr()
214 if yerr: kwargs[
'yerr'] = self.
av_xerr()
215 replacements = kwargs.get(
'replacements',
None)
or self.
replacements
216 xcontent = [self.
xedges[i] + self.
width[i] * yoffset
217 for i
in range(len(self.
xedges) - 1)]
218 width = [x * width
for x
in self.
width]
219 barh = plt.barh(xcontent, self.
y, width,
227 A container to hold Hist objects for plotting together.
229 When plotting, the title and the x and y labels of the last Hist added
230 will be used unless specified otherwise in the constructor.
233 if 'replacements' in kwargs:
235 utilities.HistStack.__init__(self, *args, **kwargs)
238 def hist(self, label_rotation=0, **kwargs):
240 Make a matplotlib hist plot.
242 Any additional keyword arguments will be passed to
243 :func:`matplotlib.pyplot.hist`, which allows a vast array of
244 possibilities. Particlularly, the *histtype* values such as
245 ``'barstacked'`` and ``'stepfilled'`` give substantially different
246 results. You will probably want to include a transparency value
247 (i.e. *alpha* = 0.5).
249 contents = np.dstack([hist.y
for hist
in self.
hists])
250 xedges = self.
hists[0].xedges
251 x = np.dstack([hist.x
for hist
in self.
hists])
252 labels = [hist.label
for hist
in self.
hists]
254 clist = [item[
'color']
for item
in self.
kwargs]
255 plt.gca().set_color_cycle(clist)
259 plot = plt.hist(x, weights=contents, bins=xedges,
260 label=labels, **kwargs)
263 from mpl_toolkits.mplot3d
import Axes3D
268 for i, hist
in enumerate(self.
hists):
269 if self.
title is not None: hist.title = self.
title
272 labels.append(hist.label)
273 all_kwargs = copy.copy(kwargs)
274 all_kwargs.update(self.
kwargs[i])
275 bar = ax.bar(hist.x, hist.y, zs=i, zdir=
'y', width=hist.width,
278 from matplotlib.ticker
import FixedLocator
279 locator = FixedLocator(range(len(labels)))
280 ax.w_yaxis.set_major_locator(locator)
281 ax.w_yaxis.set_ticklabels(labels)
282 ax.set_ylim3d(-1, len(labels))
286 Make a matplotlib bar plot, with each Hist stacked upon the last.
288 Any additional keyword arguments will be passed to
289 :func:`matplotlib.pyplot.bar`.
293 for i, hist
in enumerate(self.
hists):
294 if self.
title is not None: hist.title = self.
title
297 all_kwargs = copy.copy(kwargs)
298 all_kwargs.update(self.
kwargs[i])
299 bar = hist.bar(bottom=bottom, **all_kwargs)
301 if not bottom: bottom = [0.
for i
in range(self.
hists[0].nbins)]
302 bottom = [sum(pair)
for pair
in zip(bottom, hist.y)]
306 Make a clustered bar plot.
308 Any additional keyword arguments will be passed to
309 :func:`matplotlib.pyplot.bar`.
312 spacer = (1. - width) / 2
313 width = width / len(self.
hists)
314 for i, hist
in enumerate(self.
hists):
315 if self.
title is not None: hist.title = self.
title
318 all_kwargs = copy.copy(kwargs)
319 all_kwargs.update(self.
kwargs[i])
320 bar = hist.bar(xoffset=width*i + spacer, width=width, **all_kwargs)
323 def barh(self, width=0.8, **kwargs):
325 Make a horizontal clustered matplotlib bar plot.
327 Any additional keyword arguments will be passed to
328 :func:`matplotlib.pyplot.bar`.
331 spacer = (1. - width) / 2
332 width = width / len(self.
hists)
333 for i, hist
in enumerate(self.
hists):
334 if self.
title is not None: hist.title = self.
title
337 all_kwargs = copy.copy(kwargs)
338 all_kwargs.update(self.
kwargs[i])
339 bar = hist.barh(yoffset=width*i + spacer, width=width, **all_kwargs)
344 Make a bar plot, with all Hists in the stack overlaid.
346 Any additional keyword arguments will be passed to
347 :func:`matplotlib.pyplot.bar`. You will probably want to set a
348 transparency value (i.e. *alpha*=0.5).
351 for i, hist
in enumerate(self.
hists):
352 if self.
title is not None: hist.title = self.
title
355 all_kwargs = copy.copy(kwargs)
356 all_kwargs.update(self.
kwargs[i])
357 bar = hist.bar(**all_kwargs)
362 Make a matplotlib errorbar plot, with all Hists in the stack overlaid.
364 Passing 'offset=True' will slightly offset each dataset so overlapping
365 errorbars are still visible. Any additional keyword arguments will
366 be passed to :func:`matplotlib.pyplot.errorbar`.
369 for i, hist
in enumerate(self.
hists):
370 if self.
title is not None: hist.title = self.
title
373 all_kwargs = copy.copy(kwargs)
374 all_kwargs.update(self.
kwargs[i])
375 transform = plt.gca().transData
377 index_offset = (len(self.
hists) - 1)/2.
378 pixel_offset = 1./72 * (i - index_offset)
379 transform = transforms.ScaledTranslation(
380 pixel_offset, 0, plt.gcf().dpi_scale_trans)
381 transform = plt.gca().transData + transform
382 errorbar = hist.errorbar(transform=transform, **all_kwargs)
383 plots.append(errorbar)
387 Make a horizontal matplotlib errorbar plot, with all Hists in the
390 Any additional keyword arguments will be passed to
391 :func:`matplotlib.pyplot.errorbar`.
394 for i, hist
in enumerate(self.
hists):
395 if self.
title is not None: hist.title = self.
title
398 all_kwargs = copy.copy(kwargs)
399 all_kwargs.update(self.
kwargs[i])
400 errorbar = hist.errorbarh(**all_kwargs)
401 plots.append(errorbar)
407 """A wrapper for TFiles, allowing easier access to methods."""
408 def get(self, object_name, path=None):
410 return utilities.RootFile.get(self, object_name, path,
412 except ReferenceError, e:
413 raise ReferenceError(e)
419 Modify a string based on a list of patterns and substitutions.
421 replacements should be a list of two-entry tuples, the first entry giving
422 a string to search for and the second entry giving the string with which
423 to replace it. If replacements includes a pattern entry containing
424 'use_regexp', then all patterns will be treated as regular expressions
429 if 'use_regexp' in [x
for x,y
in replacements]:
430 for pattern, repl
in [x
for x
in replacements
431 if x[0] !=
'use_regexp']:
432 string = re.sub(pattern, repl, string)
434 for pattern, repl
in replacements:
435 string = string.replace(pattern, repl)
436 if re.match(_all_whitespace_string, string):