/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
PhysicsTools/PythonAnalysis/python/rootplot/root2matplotlib.py
465 строк
19 KB
David
xrange mig
09 апр 2019, 21:26
09 апр 2019, 21:26
af7a3a7
Код
Авторство
О чём код?
""" Utilities for plotting ROOT histograms in matplotlib. """ from builtins import range __license__ = '''\ Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' ################ Import python libraries import math import ROOT import re import copy import array from rootplot import utilities import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.transforms as transforms import numpy as np ################ Define constants _all_whitespace_string = re.compile(r'\s*$') ################ Define classes class Hist2D(utilities.Hist2D): """A container to hold the parameters from a 2D ROOT histogram.""" def __init__(self, *args, **kwargs): self.replacements = None if 'replacements' in kwargs: self.replacements = kwargs.pop('replacements') utilities.Hist2D.__init__(self, *args, **kwargs) def contour(self, **kwargs): """Draw a contour plot.""" cs = plt.contour(self.x, self.y, self.content, **kwargs) plt.clabel(cs, inline=1, fontsize=10) if self.binlabelsx is not None: plt.xticks(np.arange(self.nbinsx), self.binlabelsx) if self.binlabelsy is not None: plt.yticks(np.arange(self.nbinsy), self.binlabelsy) return cs def col(self, **kwargs): """Draw a colored box plot using :func:`matplotlib.pyplot.imshow`.""" if 'cmap' in kwargs: kwargs['cmap'] = plt.get_cmap(kwargs['cmap']) plot = plt.imshow(self.content, interpolation='nearest', extent=[self.xedges[0], self.xedges[-1], self.yedges[0], self.yedges[-1]], aspect='auto', origin='lower', **kwargs) return plot def colz(self, **kwargs): """ Draw a colored box plot with a colorbar using :func:`matplotlib.pyplot.imshow`. """ plot = self.col(**kwargs) plt.colorbar(plot) return plot def box(self, maxsize=40, **kwargs): """ Draw a box plot with size indicating content using :func:`matplotlib.pyplot.scatter`. The data will be normalized, with the largest box using a marker of size maxsize (in points). """ x = np.hstack([self.x for i in range(self.nbinsy)]) y = np.hstack([[yval for i in range(self.nbinsx)] for yval in self.y]) maxvalue = np.max(self.content) if maxvalue == 0: maxvalue = 1 sizes = np.array(self.content).flatten() / maxvalue * maxsize plot = plt.scatter(x, y, sizes, marker='s', **kwargs) return plot def TH2F(self, name=""): """Return a ROOT.TH2F object with contents of this Hist2D.""" th2f = ROOT.TH2F(name, "", self.nbinsx, array.array('f', self.xedges), self.nbinsy, array.array('f', self.yedges)) th2f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel)) for ix in range(self.nbinsx): for iy in range(self.nbinsy): th2f.SetBinContent(ix + 1, iy + 1, self.content[iy][ix]) return th2f class Hist(utilities.Hist): """A container to hold the parameters from a ROOT histogram.""" def __init__(self, *args, **kwargs): self.replacements = None if 'replacements' in kwargs: self.replacements = kwargs.pop('replacements') utilities.Hist.__init__(self, *args, **kwargs) def _prepare_xaxis(self, rotation=0, alignment='center'): """Apply bounds and text labels on x axis.""" if self.binlabels is not None: binwidth = (self.xedges[-1] - self.xedges[0]) / self.nbins plt.xticks(self.x, self.binlabels, rotation=rotation, ha=alignment) plt.xlim(self.xedges[0], self.xedges[-1]) def _prepare_yaxis(self, rotation=0, alignment='center'): """Apply bounds and text labels on y axis.""" if self.binlabels is not None: binwidth = (self.xedges[-1] - self.xedges[0]) / self.nbins plt.yticks(self.x, self.binlabels, rotation=rotation, va=alignment) plt.ylim(self.xedges[0], self.xedges[-1]) def show_titles(self, **kwargs): """Print the title and axis labels to the current figure.""" replacements = kwargs.get('replacements', None) or self.replacements plt.title(replace(self.title, replacements)) plt.xlabel(replace(self.xlabel, replacements)) plt.ylabel(replace(self.ylabel, replacements)) def hist(self, label_rotation=0, label_alignment='center', **kwargs): """ Generate a matplotlib hist figure. All additional keyword arguments will be passed to :func:`matplotlib.pyplot.hist`. """ kwargs.pop('fmt', None) replacements = kwargs.get('replacements', None) or self.replacements weights = self.y # Kludge to avoid mpl bug when plotting all zeros if self.y == [0] * self.nbins: weights = [1.e-10] * self.nbins plot = plt.hist(self.x, weights=weights, bins=self.xedges, label=replace(self.label, replacements), **kwargs) self._prepare_xaxis(label_rotation, label_alignment) return plot def errorbar(self, xerr=False, yerr=False, label_rotation=0, label_alignment='center', **kwargs): """ Generate a matplotlib errorbar figure. All additional keyword arguments will be passed to :func:`matplotlib.pyplot.errorbar`. """ if xerr: kwargs['xerr'] = self.xerr if yerr: kwargs['yerr'] = self.yerr replacements = kwargs.get('replacements', None) or self.replacements errorbar = plt.errorbar(self.x, self.y, label=replace(self.label, replacements), **kwargs) self._prepare_xaxis(label_rotation, label_alignment) return errorbar def errorbarh(self, xerr=False, yerr=False, label_rotation=0, label_alignment='center', **kwargs): """ Generate a horizontal matplotlib errorbar figure. All additional keyword arguments will be passed to :func:`matplotlib.pyplot.errorbar`. """ if xerr: kwargs['xerr'] = self.yerr if yerr: kwargs['yerr'] = self.xerr replacements = kwargs.get('replacements', None) or self.replacements errorbar = plt.errorbar(self.y, self.x, label=replace(self.label, replacements), **kwargs) self._prepare_yaxis(label_rotation, label_alignment) return errorbar def bar(self, xerr=False, yerr=False, xoffset=0., width=0.8, label_rotation=0, label_alignment='center', **kwargs): """ Generate a matplotlib bar figure. All additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. """ kwargs.pop('fmt', None) if xerr: kwargs['xerr'] = self.av_xerr() if yerr: kwargs['yerr'] = self.av_yerr() replacements = kwargs.get('replacements', None) or self.replacements ycontent = [self.xedges[i] + self.width[i] * xoffset for i in range(len(self.xedges) - 1)] width = [x * width for x in self.width] bar = plt.bar(ycontent, self.y, width, label=replace(self.label, replacements), **kwargs) self._prepare_xaxis(label_rotation, label_alignment) return bar def barh(self, xerr=False, yerr=False, yoffset=0., width=0.8, label_rotation=0, label_alignment='center', **kwargs): """ Generate a horizontal matplotlib bar figure. All additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. """ kwargs.pop('fmt', None) if xerr: kwargs['xerr'] = self.av_yerr() if yerr: kwargs['yerr'] = self.av_xerr() replacements = kwargs.get('replacements', None) or self.replacements xcontent = [self.xedges[i] + self.width[i] * yoffset for i in range(len(self.xedges) - 1)] width = [x * width for x in self.width] barh = plt.barh(xcontent, self.y, width, label=replace(self.label, replacements), **kwargs) self._prepare_yaxis(label_rotation, label_alignment) return barh class HistStack(utilities.HistStack): """ 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. """ def __init__(self, *args, **kwargs): if 'replacements' in kwargs: self.replacements = kwargs.pop('replacements') utilities.HistStack.__init__(self, *args, **kwargs) def show_titles(self, **kwargs): self.hists[-1].show_titles() def hist(self, label_rotation=0, **kwargs): """ Make a matplotlib hist plot. Any additional keyword arguments will be passed to :func:`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). """ contents = np.dstack([hist.y for hist in self.hists]) xedges = self.hists[0].xedges x = np.dstack([hist.x for hist in self.hists])[0] labels = [hist.label for hist in self.hists] try: clist = [item['color'] for item in self.kwargs] plt.gca().set_color_cycle(clist) ## kwargs['color'] = clist # For newer version of matplotlib except: pass plot = plt.hist(x, weights=contents, bins=xedges, label=labels, **kwargs) def bar3d(self, **kwargs): #### Not yet ready for primetime from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = Axes3D(fig) plots = [] labels = [] for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.xlabel = self.xlabel if self.ylabel is not None: hist.ylabel = self.ylabel labels.append(hist.label) all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) bar = ax.bar(hist.x, hist.y, zs=i, zdir='y', width=hist.width, **all_kwargs) plots.append(bar) from matplotlib.ticker import FixedLocator locator = FixedLocator(list(range(len(labels)))) ax.w_yaxis.set_major_locator(locator) ax.w_yaxis.set_ticklabels(labels) ax.set_ylim3d(-1, len(labels)) return plots def barstack(self, **kwargs): """ Make a matplotlib bar plot, with each Hist stacked upon the last. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. """ bottom = None # if this is set to zeroes, it fails for log y plots = [] for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.xlabel = self.xlabel if self.ylabel is not None: hist.ylabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) bar = hist.bar(bottom=bottom, **all_kwargs) plots.append(bar) if not bottom: bottom = [0. for i in range(self.hists[0].nbins)] bottom = [sum(pair) for pair in zip(bottom, hist.y)] return plots def histstack(self, **kwargs): """ Make a matplotlib hist plot, with each Hist stacked upon the last. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.hist`. """ bottom = None # if this is set to zeroes, it fails for log y plots = [] cumhist = None for i, hist in enumerate(self.hists): if cumhist: cumhist = hist + cumhist else: cumhist = copy.copy(hist) if self.title is not None: cumhist.title = self.title if self.xlabel is not None: cumhist.xlabel = self.xlabel if self.ylabel is not None: cumhist.ylabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) zorder = 0 + float(len(self) - i)/len(self) # plot in reverse order plot = cumhist.hist(zorder=zorder, **all_kwargs) plots.append(plot) return plots def barcluster(self, width=0.8, **kwargs): """ Make a clustered bar plot. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. """ plots = [] spacer = (1. - width) / 2 width = width / len(self.hists) for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.xlabel = self.xlabel if self.ylabel is not None: hist.ylabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) bar = hist.bar(xoffset=width*i + spacer, width=width, **all_kwargs) plots.append(bar) return plots def barh(self, width=0.8, **kwargs): """ Make a horizontal clustered matplotlib bar plot. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. """ plots = [] spacer = (1. - width) / 2 width = width / len(self.hists) for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.ylabel = self.xlabel if self.ylabel is not None: hist.xlabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) bar = hist.barh(yoffset=width*i + spacer, width=width, **all_kwargs) plots.append(bar) return plots def bar(self, **kwargs): """ Make a bar plot, with all Hists in the stack overlaid. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.bar`. You will probably want to set a transparency value (i.e. *alpha* = 0.5). """ plots = [] for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.xlabel = self.xlabel if self.ylabel is not None: hist.ylabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) bar = hist.bar(**all_kwargs) plots.append(bar) return plots def errorbar(self, offset=False, **kwargs): """ 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 :func:`matplotlib.pyplot.errorbar`. """ plots = [] for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.xlabel = self.xlabel if self.ylabel is not None: hist.ylabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) transform = plt.gca().transData if offset: index_offset = (len(self.hists) - 1)/2. pixel_offset = 1./72 * (i - index_offset) transform = transforms.ScaledTranslation( pixel_offset, 0, plt.gcf().dpi_scale_trans) transform = plt.gca().transData + transform errorbar = hist.errorbar(transform=transform, **all_kwargs) plots.append(errorbar) return plots def errorbarh(self, **kwargs): """ Make a horizontal matplotlib errorbar plot, with all Hists in the stack overlaid. Any additional keyword arguments will be passed to :func:`matplotlib.pyplot.errorbar`. """ plots = [] for i, hist in enumerate(self.hists): if self.title is not None: hist.title = self.title if self.xlabel is not None: hist.ylabel = self.xlabel if self.ylabel is not None: hist.xlabel = self.ylabel all_kwargs = copy.copy(kwargs) all_kwargs.update(self.kwargs[i]) errorbar = hist.errorbarh(**all_kwargs) plots.append(errorbar) return plots ################ Define functions and classes for navigating within ROOT class RootFile(utilities.RootFile): """A wrapper for TFiles, allowing easier access to methods.""" def get(self, object_name, path=None): try: return utilities.RootFile.get(self, object_name, path, Hist, Hist2D) except ReferenceError as e: raise ReferenceError(e) ################ Define additional helping functions def replace(string, replacements): """ 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. """ if not replacements: return string if 'use_regexp' in [x for x,y in replacements]: for pattern, repl in [x for x in replacements if x[0] != 'use_regexp']: string = re.sub(pattern, repl, string) else: for pattern, repl in replacements: string = string.replace(pattern, repl) if re.match(_all_whitespace_string, string): return "" return string