CMS 3D CMS Logo

root2matplotlib.py
Go to the documentation of this file.
1 """
2 Utilities for plotting ROOT histograms in matplotlib.
3 """
4 
5 from builtins import range
6 __license__ = '''\
7 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 '''
27 
28 
29 
30 import math
31 import ROOT
32 import re
33 import copy
34 import array
35 from rootplot import utilities
36 import matplotlib as mpl
37 import matplotlib.pyplot as plt
38 import matplotlib.transforms as transforms
39 import numpy as np
40 
41 
42 
43 _all_whitespace_string = re.compile(r'\s*$')
44 
45 
46 
47 
49  """A container to hold the parameters from a 2D ROOT histogram."""
50  def __init__(self, *args, **kwargs):
51  self.replacements = None
52  if 'replacements' in kwargs:
53  self.replacements = kwargs.pop('replacements')
54  utilities.Hist2D.__init__(self, *args, **kwargs)
55  def contour(self, **kwargs):
56  """Draw a contour plot."""
57  cs = plt.contour(self.x, self.y, self.content, **kwargs)
58  plt.clabel(cs, inline=1, fontsize=10)
59  if self.binlabelsx is not None:
60  plt.xticks(np.arange(self.nbinsx), self.binlabelsx)
61  if self.binlabelsy is not None:
62  plt.yticks(np.arange(self.nbinsy), self.binlabelsy)
63  return cs
64  def col(self, **kwargs):
65  """Draw a colored box plot using :func:`matplotlib.pyplot.imshow`."""
66  if 'cmap' in kwargs:
67  kwargs['cmap'] = plt.get_cmap(kwargs['cmap'])
68  plot = plt.imshow(self.content, interpolation='nearest',
69  extent=[self.xedges[0], self.xedges[-1],
70  self.yedges[0], self.yedges[-1]],
71  aspect='auto', origin='lower', **kwargs)
72  return plot
73  def colz(self, **kwargs):
74  """
75  Draw a colored box plot with a colorbar using
76  :func:`matplotlib.pyplot.imshow`.
77  """
78  plot = self.col(**kwargs)
79  plt.colorbar(plot)
80  return plot
81  def box(self, maxsize=40, **kwargs):
82  """
83  Draw a box plot with size indicating content using
84  :func:`matplotlib.pyplot.scatter`.
85 
86  The data will be normalized, with the largest box using a marker of
87  size maxsize (in points).
88  """
89  x = np.hstack([self.x for i in range(self.nbinsy)])
90  y = np.hstack([[yval for i in range(self.nbinsx)] for yval in self.y])
91  maxvalue = np.max(self.content)
92  if maxvalue == 0:
93  maxvalue = 1
94  sizes = np.array(self.content).flatten() / maxvalue * maxsize
95  plot = plt.scatter(x, y, sizes, marker='s', **kwargs)
96  return plot
97  def TH2F(self, name=""):
98  """Return a ROOT.TH2F object with contents of this Hist2D."""
99  th2f = ROOT.TH2F(name, "",
100  self.nbinsx, array.array('f', self.xedges),
101  self.nbinsy, array.array('f', self.yedges))
102  th2f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
103  for ix in range(self.nbinsx):
104  for iy in range(self.nbinsy):
105  th2f.SetBinContent(ix + 1, iy + 1, self.content[iy][ix])
106  return th2f
107 
109  """A container to hold the parameters from a ROOT histogram."""
110  def __init__(self, *args, **kwargs):
111  self.replacements = None
112  if 'replacements' in kwargs:
113  self.replacements = kwargs.pop('replacements')
114  utilities.Hist.__init__(self, *args, **kwargs)
115  def _prepare_xaxis(self, rotation=0, alignment='center'):
116  """Apply bounds and text labels on x axis."""
117  if self.binlabels is not None:
118  binwidth = (self.xedges[-1] - self.xedges[0]) / self.nbins
119  plt.xticks(self.x, self.binlabels,
120  rotation=rotation, ha=alignment)
121  plt.xlim(self.xedges[0], self.xedges[-1])
122 
123  def _prepare_yaxis(self, rotation=0, alignment='center'):
124  """Apply bounds and text labels on y axis."""
125  if self.binlabels is not None:
126  binwidth = (self.xedges[-1] - self.xedges[0]) / self.nbins
127  plt.yticks(self.x, self.binlabels,
128  rotation=rotation, va=alignment)
129  plt.ylim(self.xedges[0], self.xedges[-1])
130 
131  def show_titles(self, **kwargs):
132  """Print the title and axis labels to the current figure."""
133  replacements = kwargs.get('replacements', None) or self.replacements
134  plt.title(replace(self.title, replacements))
135  plt.xlabel(replace(self.xlabel, replacements))
136  plt.ylabel(replace(self.ylabel, replacements))
137  def hist(self, label_rotation=0, label_alignment='center', **kwargs):
138  """
139  Generate a matplotlib hist figure.
140 
141  All additional keyword arguments will be passed to
142  :func:`matplotlib.pyplot.hist`.
143  """
144  kwargs.pop('fmt', None)
145  replacements = kwargs.get('replacements', None) or self.replacements
146  weights = self.y
147  # Kludge to avoid mpl bug when plotting all zeros
148  if self.y == [0] * self.nbins:
149  weights = [1.e-10] * self.nbins
150  plot = plt.hist(self.x, weights=weights, bins=self.xedges,
151  label=replace(self.label, replacements), **kwargs)
152  self._prepare_xaxis(label_rotation, label_alignment)
153  return plot
154  def errorbar(self, xerr=False, yerr=False, label_rotation=0,
155  label_alignment='center', **kwargs):
156  """
157  Generate a matplotlib errorbar figure.
158 
159  All additional keyword arguments will be passed to
160  :func:`matplotlib.pyplot.errorbar`.
161  """
162  if xerr:
163  kwargs['xerr'] = self.xerr
164  if yerr:
165  kwargs['yerr'] = self.yerr
166  replacements = kwargs.get('replacements', None) or self.replacements
167  errorbar = plt.errorbar(self.x, self.y,
168  label=replace(self.label, replacements),
169  **kwargs)
170  self._prepare_xaxis(label_rotation, label_alignment)
171  return errorbar
172  def errorbarh(self, xerr=False, yerr=False, label_rotation=0,
173  label_alignment='center', **kwargs):
174  """
175  Generate a horizontal matplotlib errorbar figure.
176 
177  All additional keyword arguments will be passed to
178  :func:`matplotlib.pyplot.errorbar`.
179  """
180  if xerr: kwargs['xerr'] = self.yerr
181  if yerr: kwargs['yerr'] = self.xerr
182  replacements = kwargs.get('replacements', None) or self.replacements
183  errorbar = plt.errorbar(self.y, self.x,
184  label=replace(self.label, replacements),
185  **kwargs)
186  self._prepare_yaxis(label_rotation, label_alignment)
187  return errorbar
188  def bar(self, xerr=False, yerr=False, xoffset=0., width=0.8,
189  label_rotation=0, label_alignment='center', **kwargs):
190  """
191  Generate a matplotlib bar figure.
192 
193  All additional keyword arguments will be passed to
194  :func:`matplotlib.pyplot.bar`.
195  """
196  kwargs.pop('fmt', None)
197  if xerr: kwargs['xerr'] = self.av_xerr()
198  if yerr: kwargs['yerr'] = self.av_yerr()
199  replacements = kwargs.get('replacements', None) or self.replacements
200  ycontent = [self.xedges[i] + self.width[i] * xoffset
201  for i in range(len(self.xedges) - 1)]
202  width = [x * width for x in self.width]
203  bar = plt.bar(ycontent, self.y, width,
204  label=replace(self.label, replacements), **kwargs)
205  self._prepare_xaxis(label_rotation, label_alignment)
206  return bar
207  def barh(self, xerr=False, yerr=False, yoffset=0., width=0.8,
208  label_rotation=0, label_alignment='center', **kwargs):
209  """
210  Generate a horizontal matplotlib bar figure.
211 
212  All additional keyword arguments will be passed to
213  :func:`matplotlib.pyplot.bar`.
214  """
215  kwargs.pop('fmt', None)
216  if xerr: kwargs['xerr'] = self.av_yerr()
217  if yerr: kwargs['yerr'] = self.av_xerr()
218  replacements = kwargs.get('replacements', None) or self.replacements
219  xcontent = [self.xedges[i] + self.width[i] * yoffset
220  for i in range(len(self.xedges) - 1)]
221  width = [x * width for x in self.width]
222  barh = plt.barh(xcontent, self.y, width,
223  label=replace(self.label, replacements),
224  **kwargs)
225  self._prepare_yaxis(label_rotation, label_alignment)
226  return barh
227 
229  """
230  A container to hold Hist objects for plotting together.
231 
232  When plotting, the title and the x and y labels of the last Hist added
233  will be used unless specified otherwise in the constructor.
234  """
235  def __init__(self, *args, **kwargs):
236  if 'replacements' in kwargs:
237  self.replacements = kwargs.pop('replacements')
238  utilities.HistStack.__init__(self, *args, **kwargs)
239  def show_titles(self, **kwargs):
240  self.hists[-1].show_titles()
241  def hist(self, label_rotation=0, **kwargs):
242  """
243  Make a matplotlib hist plot.
244 
245  Any additional keyword arguments will be passed to
246  :func:`matplotlib.pyplot.hist`, which allows a vast array of
247  possibilities. Particlularly, the *histtype* values such as
248  ``'barstacked'`` and ``'stepfilled'`` give substantially different
249  results. You will probably want to include a transparency value
250  (i.e. *alpha* = 0.5).
251  """
252  contents = np.dstack([hist.y for hist in self.hists])
253  xedges = self.hists[0].xedges
254  x = np.dstack([hist.x for hist in self.hists])[0]
255  labels = [hist.label for hist in self.hists]
256  try:
257  clist = [item['color'] for item in self.kwargs]
258  plt.gca().set_color_cycle(clist)
259 
260  except:
261  pass
262  plot = plt.hist(x, weights=contents, bins=xedges,
263  label=labels, **kwargs)
264  def bar3d(self, **kwargs):
265 
266  from mpl_toolkits.mplot3d import Axes3D
267  fig = plt.figure()
268  ax = Axes3D(fig)
269  plots = []
270  labels = []
271  for i, hist in enumerate(self.hists):
272  if self.title is not None: hist.title = self.title
273  if self.xlabel is not None: hist.xlabel = self.xlabel
274  if self.ylabel is not None: hist.ylabel = self.ylabel
275  labels.append(hist.label)
276  all_kwargs = copy.copy(kwargs)
277  all_kwargs.update(self.kwargs[i])
278  bar = ax.bar(hist.x, hist.y, zs=i, zdir='y', width=hist.width,
279  **all_kwargs)
280  plots.append(bar)
281  from matplotlib.ticker import FixedLocator
282  locator = FixedLocator(list(range(len(labels))))
283  ax.w_yaxis.set_major_locator(locator)
284  ax.w_yaxis.set_ticklabels(labels)
285  ax.set_ylim3d(-1, len(labels))
286  return plots
287  def barstack(self, **kwargs):
288  """
289  Make a matplotlib bar plot, with each Hist stacked upon the last.
290 
291  Any additional keyword arguments will be passed to
292  :func:`matplotlib.pyplot.bar`.
293  """
294  bottom = None # if this is set to zeroes, it fails for log y
295  plots = []
296  for i, hist in enumerate(self.hists):
297  if self.title is not None: hist.title = self.title
298  if self.xlabel is not None: hist.xlabel = self.xlabel
299  if self.ylabel is not None: hist.ylabel = self.ylabel
300  all_kwargs = copy.copy(kwargs)
301  all_kwargs.update(self.kwargs[i])
302  bar = hist.bar(bottom=bottom, **all_kwargs)
303  plots.append(bar)
304  if not bottom: bottom = [0. for i in range(self.hists[0].nbins)]
305  bottom = [sum(pair) for pair in zip(bottom, hist.y)]
306  return plots
307  def histstack(self, **kwargs):
308  """
309  Make a matplotlib hist plot, with each Hist stacked upon the last.
310 
311  Any additional keyword arguments will be passed to
312  :func:`matplotlib.pyplot.hist`.
313  """
314  bottom = None # if this is set to zeroes, it fails for log y
315  plots = []
316  cumhist = None
317  for i, hist in enumerate(self.hists):
318  if cumhist:
319  cumhist = hist + cumhist
320  else:
321  cumhist = copy.copy(hist)
322  if self.title is not None: cumhist.title = self.title
323  if self.xlabel is not None: cumhist.xlabel = self.xlabel
324  if self.ylabel is not None: cumhist.ylabel = self.ylabel
325  all_kwargs = copy.copy(kwargs)
326  all_kwargs.update(self.kwargs[i])
327  zorder = 0 + float(len(self) - i)/len(self) # plot in reverse order
328  plot = cumhist.hist(zorder=zorder, **all_kwargs)
329  plots.append(plot)
330  return plots
331  def barcluster(self, width=0.8, **kwargs):
332  """
333  Make a clustered bar plot.
334 
335  Any additional keyword arguments will be passed to
336  :func:`matplotlib.pyplot.bar`.
337  """
338  plots = []
339  spacer = (1. - width) / 2
340  width = width / len(self.hists)
341  for i, hist in enumerate(self.hists):
342  if self.title is not None: hist.title = self.title
343  if self.xlabel is not None: hist.xlabel = self.xlabel
344  if self.ylabel is not None: hist.ylabel = self.ylabel
345  all_kwargs = copy.copy(kwargs)
346  all_kwargs.update(self.kwargs[i])
347  bar = hist.bar(xoffset=width*i + spacer, width=width, **all_kwargs)
348  plots.append(bar)
349  return plots
350  def barh(self, width=0.8, **kwargs):
351  """
352  Make a horizontal clustered matplotlib bar plot.
353 
354  Any additional keyword arguments will be passed to
355  :func:`matplotlib.pyplot.bar`.
356  """
357  plots = []
358  spacer = (1. - width) / 2
359  width = width / len(self.hists)
360  for i, hist in enumerate(self.hists):
361  if self.title is not None: hist.title = self.title
362  if self.xlabel is not None: hist.ylabel = self.xlabel
363  if self.ylabel is not None: hist.xlabel = self.ylabel
364  all_kwargs = copy.copy(kwargs)
365  all_kwargs.update(self.kwargs[i])
366  bar = hist.barh(yoffset=width*i + spacer, width=width, **all_kwargs)
367  plots.append(bar)
368  return plots
369  def bar(self, **kwargs):
370  """
371  Make a bar plot, with all Hists in the stack overlaid.
372 
373  Any additional keyword arguments will be passed to
374  :func:`matplotlib.pyplot.bar`. You will probably want to set a
375  transparency value (i.e. *alpha* = 0.5).
376  """
377  plots = []
378  for i, hist in enumerate(self.hists):
379  if self.title is not None: hist.title = self.title
380  if self.xlabel is not None: hist.xlabel = self.xlabel
381  if self.ylabel is not None: hist.ylabel = self.ylabel
382  all_kwargs = copy.copy(kwargs)
383  all_kwargs.update(self.kwargs[i])
384  bar = hist.bar(**all_kwargs)
385  plots.append(bar)
386  return plots
387  def errorbar(self, offset=False, **kwargs):
388  """
389  Make a matplotlib errorbar plot, with all Hists in the stack overlaid.
390 
391  Passing 'offset=True' will slightly offset each dataset so overlapping
392  errorbars are still visible. Any additional keyword arguments will
393  be passed to :func:`matplotlib.pyplot.errorbar`.
394  """
395  plots = []
396  for i, hist in enumerate(self.hists):
397  if self.title is not None: hist.title = self.title
398  if self.xlabel is not None: hist.xlabel = self.xlabel
399  if self.ylabel is not None: hist.ylabel = self.ylabel
400  all_kwargs = copy.copy(kwargs)
401  all_kwargs.update(self.kwargs[i])
402  transform = plt.gca().transData
403  if offset:
404  index_offset = (len(self.hists) - 1)/2.
405  pixel_offset = 1./72 * (i - index_offset)
406  transform = transforms.ScaledTranslation(
407  pixel_offset, 0, plt.gcf().dpi_scale_trans)
408  transform = plt.gca().transData + transform
409  errorbar = hist.errorbar(transform=transform, **all_kwargs)
410  plots.append(errorbar)
411  return plots
412  def errorbarh(self, **kwargs):
413  """
414  Make a horizontal matplotlib errorbar plot, with all Hists in the
415  stack overlaid.
416 
417  Any additional keyword arguments will be passed to
418  :func:`matplotlib.pyplot.errorbar`.
419  """
420  plots = []
421  for i, hist in enumerate(self.hists):
422  if self.title is not None: hist.title = self.title
423  if self.xlabel is not None: hist.ylabel = self.xlabel
424  if self.ylabel is not None: hist.xlabel = self.ylabel
425  all_kwargs = copy.copy(kwargs)
426  all_kwargs.update(self.kwargs[i])
427  errorbar = hist.errorbarh(**all_kwargs)
428  plots.append(errorbar)
429  return plots
430 
431 
432 
434  """A wrapper for TFiles, allowing easier access to methods."""
435  def get(self, object_name, path=None):
436  try:
437  return utilities.RootFile.get(self, object_name, path,
438  Hist, Hist2D)
439  except ReferenceError as e:
440  raise ReferenceError(e)
441 
442 
443 
444 def replace(string, replacements):
445  """
446  Modify a string based on a list of patterns and substitutions.
447 
448  replacements should be a list of two-entry tuples, the first entry giving
449  a string to search for and the second entry giving the string with which
450  to replace it. If replacements includes a pattern entry containing
451  'use_regexp', then all patterns will be treated as regular expressions
452  using re.sub.
453  """
454  if not replacements:
455  return string
456  if 'use_regexp' in [x for x,y in replacements]:
457  for pattern, repl in [x for x in replacements
458  if x[0] != 'use_regexp']:
459  string = re.sub(pattern, repl, string)
460  else:
461  for pattern, repl in replacements:
462  string = string.replace(pattern, repl)
463  if re.match(_all_whitespace_string, string):
464  return ""
465  return string
466 
def errorbar(self, offset=False, kwargs)
def box(self, maxsize=40, kwargs)
ALPAKA_FN_HOST_ACC ALPAKA_FN_INLINE constexpr float zip(ConstView const &tracks, int32_t i)
Definition: TracksSoA.h:90
def replace(string, replacements)
def hist(self, label_rotation=0, kwargs)
def _prepare_yaxis(self, rotation=0, alignment='center')
def _prepare_xaxis(self, rotation=0, alignment='center')
def hist(self, label_rotation=0, label_alignment='center', kwargs)
def get(self, object_name, path=None)
def errorbar(self, xerr=False, yerr=False, label_rotation=0, label_alignment='center', kwargs)
def barh(self, xerr=False, yerr=False, yoffset=0., width=0.8, label_rotation=0, label_alignment='center', kwargs)
def bar(self, xerr=False, yerr=False, xoffset=0., width=0.8, label_rotation=0, label_alignment='center', kwargs)
def errorbarh(self, xerr=False, yerr=False, label_rotation=0, label_alignment='center', kwargs)
def barcluster(self, width=0.8, kwargs)