2 Utilities for rootplot including histogram classes. 4 from __future__
import print_function
6 from builtins
import range
8 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu> 10 Permission is hereby granted, free of charge, to any person obtaining a copy 11 of this software and associated documentation files (the "Software"), to deal 12 in the Software without restriction, including without limitation the rights 13 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 copies of the Software, and to permit persons to whom the Software is 15 furnished to do so, subject to the following conditions: 17 The above copyright notice and this permission notice shall be included in 18 all copies or substantial portions of the Software. 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 39 from random
import gauss
44 """A container to hold the parameters from a 2D ROOT histogram.""" 45 def __init__(self, hist, label="__nolabel__", title=None,
46 xlabel=
None, ylabel=
None):
48 if not hist.InheritsFrom(
"TH2"):
49 raise TypeError(
"%s does not inherit from TH2" % hist)
51 raise TypeError(
"%s is not a ROOT object" % hist)
57 for i
in range(1, nx + 1)])
59 self.
nbinsx = nx = self.binlabelsx.index(
'')
62 for i
in range(1, ny + 1)])
64 self.
nbinsy = ny = self.binlabelsy.index(
'')
67 self.
content = [[hist.GetBinContent(i, j)
for i
in range(1, nx + 1)]
68 for j
in range(1, ny + 1)]
69 self.
xedges = [hist.GetXaxis().GetBinLowEdge(i)
70 for i
in range(1, nx + 2)]
71 self.
yedges = [hist.GetYaxis().GetBinLowEdge(i)
72 for i
in range(1, ny + 2)]
77 self.
title = title
or hist.GetTitle()
78 self.
xlabel = xlabel
or hist.GetXaxis().GetTitle()
79 self.
ylabel = ylabel
or hist.GetYaxis().GetTitle()
87 """Return contents of indexth bin: x.__getitem__(y) <==> x[y]""" 90 """Return the number of bins: x.__len__() <==> len(x)""" 93 """Iterate through bins: x.__iter__() <==> iter(x)""" 96 """Return a ROOT.TH2F object with contents of this Hist2D.""" 97 th2f = ROOT.TH2F(name,
"",
101 for ix
in range(self.
nbinsx):
102 for iy
in range(self.
nbinsy):
103 th2f.SetBinContent(ix + 1, iy + 1, self.
content[iy][ix])
107 """A container to hold the parameters from a ROOT histogram.""" 108 def __init__(self, hist, label="__nolabel__",
109 name=
None, title=
None, xlabel=
None, ylabel=
None):
113 except AttributeError:
117 except AttributeError:
118 raise TypeError(
"%s is not a 1D histogram or TGraph" % hist)
120 self.
name = name
or hist.GetName()
122 self.
xlabel = xlabel
or hist.GetXaxis().GetTitle()
123 self.
ylabel = ylabel
or hist.GetYaxis().GetTitle()
128 for i
in range(1, n + 1)])
131 self.
nbins = n = self.binlabels.index(
'')
134 self.
xedges = [hist.GetBinLowEdge(i)
for i
in range(1, n + 2)]
139 self.
y = [hist.GetBinContent(i)
for i
in range(1, n + 1)]
140 self.
yerr = [hist.GetBinError( i)
for i
in range(1, n + 1)]
145 self.
nbins = n = hist.GetN()
146 self.
x, self.
y = [], []
147 x, y = ROOT.Double(0), ROOT.Double(0)
149 hist.GetPoint(i, x, y)
150 self.x.append(copy.copy(x))
151 self.y.append(copy.copy(y))
152 lower = [
max(0, hist.GetErrorXlow(i))
for i
in range(n)]
153 upper = [
max(0, hist.GetErrorXhigh(i))
for i
in range(n)]
154 self.
xerr = [lower[:], upper[:]]
155 lower = [
max(0, hist.GetErrorYlow(i))
for i
in range(n)]
156 upper = [
max(0, hist.GetErrorYhigh(i))
for i
in range(n)]
157 self.
yerr = [lower[:], upper[:]]
158 self.
xedges = [self.
x[i] - self.
xerr[0][i]
for i
in range(n)]
159 self.xedges.append(self.
x[n - 1] + self.
xerr[1][n - 1])
165 """Return the sum of self and b: x.__add__(y) <==> x + y""" 167 for i
in range(len(self)):
169 c.yerr[0][i] += b.yerr[0][i]
170 c.yerr[1][i] += b.yerr[1][i]
171 c.overflow += b.overflow
172 c.underflow += b.underflow
175 """Return the difference of self and b: x.__sub__(y) <==> x - y""" 177 for i
in range(len(self)):
179 c.yerr[0][i] -= b.yerr[0][i]
180 c.yerr[1][i] -= b.yerr[1][i]
181 c.overflow -= b.overflow
182 c.underflow -= b.underflow
185 return self.
divide(denominator)
187 """Return contents of indexth bin: x.__getitem__(y) <==> x[y]""" 190 """Set contents of indexth bin: x.__setitem__(i, y) <==> x[i]=y""" 191 self.
y[index] = value
193 """Return the number of bins: x.__len__() <==> len(x)""" 196 """Iterate through bins: x.__iter__() <==> iter(x)""" 198 def min(self, threshold=None):
199 """Return the y-value of the bottom tip of the lowest errorbar.""" 200 vals = [(yval - yerr)
for yval, yerr
in zip(self.
y, self.
yerr[0])
201 if (yval - yerr) > threshold]
207 """Return average between the upper and lower xerr.""" 208 return [(self.
xerr[0][i] + self.
xerr[1][i]) / 2
209 for i
in range(self.
nbins)]
211 """Return average between the upper and lower yerr.""" 212 return [(self.
yerr[0][i] + self.
yerr[1][i]) / 2
213 for i
in range(self.
nbins)]
216 Scale contents, errors, and over/underflow by the given scale factor. 218 self.
y = [x * factor
for x
in self.
y]
219 self.
yerr[0] = [x * factor
for x
in self.
yerr[0]]
220 self.
yerr[1] = [x * factor
for x
in self.
yerr[1]]
225 Delete a the contents of a bin, sliding all the other data one bin to 226 the left. This can be useful for histograms with labeled bins. 233 self.
xerr[0].pop(index)
234 self.
xerr[1].pop(index)
235 self.
yerr[0].pop(index)
236 self.
yerr[1].pop(index)
238 self.binlabels.pop(index)
240 """Return a ROOT.TH1F object with contents of this Hist""" 241 th1f = ROOT.TH1F(name
or self.
name,
"", self.
nbins,
242 array.array(
'f', self.
xedges))
245 for i
in range(self.
nbins):
246 th1f.SetBinContent(i + 1, self.
y[i])
248 th1f.SetBinError(i + 1, (self.
yerr[0][i] + self.
yerr[1][i]) / 2)
250 th1f.SetBinError(i + 1, self.
yerr[i])
252 th1f.GetXaxis().SetBinLabel(i + 1, self.
binlabels[i])
258 """Return a ROOT.TGraphAsymmErrors object with contents of this Hist""" 259 x = array.array(
'f', self.
x)
260 y = array.array(
'f', self.
y)
261 xl = array.array(
'f', self.
xerr[0])
262 xh = array.array(
'f', self.
xerr[1])
263 yl = array.array(
'f', self.
yerr[0])
264 yh = array.array(
'f', self.
yerr[1])
265 tgraph = ROOT.TGraphAsymmErrors(self.
nbins, x, y, xl, xh, yl, yh)
266 tgraph.SetName(name
or self.
name)
271 Return the simple quotient with errors added in quadrature. 273 This function is called by the division operator: 274 hist3 = hist1.divide_wilson(hist2) <--> hist3 = hist1 / hist2 276 if len(self) != len(denominator):
277 raise TypeError(
"Cannot divide %s with %i bins by " 279 (denominator.name, len(denominator),
280 self.
name, len(self)))
281 quotient = copy.deepcopy(self)
283 den_yerr = denominator.av_yerr()
284 quotient.yerr = [0.
for i
in range(self.
nbins)]
285 for i
in range(self.
nbins):
286 if denominator[i] == 0
or self[i] == 0:
289 quotient.y[i] = self[i] / denominator[i]
290 quotient.yerr[i] = quotient[i]
291 quotient.yerr[i] *= math.sqrt((num_yerr[i] / self[i]) ** 2 +
292 (den_yerr[i] / denominator[i]) ** 2)
293 if quotient.yerr[i] > quotient[i]:
294 quotient.yerr[i] = quotient[i]
295 quotient.yerr = [quotient.yerr, quotient.yerr]
298 """Return an efficiency plot with Wilson score interval errors.""" 299 if len(self) != len(denominator):
300 raise TypeError(
"Cannot divide %s with %i bins by " 302 (denominator.name, len(denominator),
303 self.
name, len(self)))
305 quotient = copy.deepcopy(self)
307 quotient.yerr = [lower_err, upper_err]
312 A container to hold Hist objects for plotting together. 314 When plotting, the title and the x and y labels of the last Hist added 315 will be used unless specified otherwise in the constructor. 317 def __init__(self, hists=None, title=None, xlabel=None, ylabel=None):
327 """Return indexth hist: x.__getitem__(y) <==> x[y]""" 328 return self.
hists[index]
330 """Replace indexth hist with value: x.__setitem__(i, y) <==> x[i]=y""" 331 self.
hists[index] = value
333 """Return the number of hists in the stack: x.__len__() <==> len(x)""" 334 return len(self.
hists)
336 """Iterate through hists in the stack: x.__iter__() <==> iter(x)""" 337 return iter(self.
hists)
339 """Return the value of the highest bin of all hists in the stack.""" 340 maxes = [
max(x)
for x
in self.
hists]
346 """Return the value of the highest bin in the addition of all hists.""" 348 return max([sum([h[i]
for h
in self.
hists])
349 for i
in range(self.
hists[0].nbins)])
353 """Scale all Hists by factor.""" 354 for hist
in self.
hists:
356 def min(self, threshold=None):
358 Return the value of the lowest bin of all hists in the stack. 360 If threshold is specified, only values above the threshold will be 363 mins = [x.min(threshold)
for x
in self.
hists]
365 def add(self, hist, **kwargs):
367 Add a Hist object to this stack. 369 Any additional keyword arguments will be added to just this Hist 370 when the stack is plotted. 372 if "label" in kwargs:
373 hist.label = kwargs[
'label']
376 if hist.xedges != self.
hists[0].xedges:
377 raise ValueError(
"Cannot add %s to stack; all Hists must " 378 "have the same binning." % hist.name)
379 self.hists.append(hist)
380 self.kwargs.append(kwargs)
386 """A wrapper for TFiles, allowing easier access to methods.""" 389 self.
name = name
or filename[:-5]
390 self.
file = ROOT.TFile(filename,
'read')
391 if self.file.IsZombie():
392 raise ValueError(
"Error opening %s" % filename)
393 def cd(self, directory=''):
394 """Make directory the current directory.""" 395 self.file.cd(directory)
396 def get(self, object_name, path=None, type1D=Hist, type2D=Hist2D):
397 """Return a Hist object from the given path within this file.""" 399 path = os.path.dirname(object_name)
400 object_name = os.path.basename(object_name)
402 roothist = self.file.GetDirectory(path).Get(object_name)
403 except ReferenceError
as e:
404 raise ReferenceError(e)
406 return type2D(roothist)
408 return type1D(roothist)
410 def ls(directory=None):
411 """Return a python list of ROOT object names from the given directory.""" 412 if directory ==
None:
413 keys = ROOT.gDirectory.GetListOfKeys()
415 keys = ROOT.gDirectory.GetDirectory(directory).GetListOfKeys()
420 key = keys.After(key)
421 names.append(obj.GetName())
425 """Return ROOT's present working directory.""" 426 return ROOT.gDirectory.GetPath()
429 """Return a Hist object with the given name.""" 430 return Hist(ROOT.gDirectory.Get(object_name))
438 saved_argv = sys.argv[:]
439 argstring =
' '.
join(sys.argv)
440 sys.argv = [sys.argv[0]]
445 The program was unable to access PyROOT. Usually, this just requires switching 446 to the same major version of python that used when compiling ROOT. To 447 determine which version that is, try the following command: 448 root -config 2>&1 | tr ' ' '\\n' | egrep 'python|PYTHON' 449 If this is different from the python version you are currently using, try 450 changing your PATH to point to the new one.""")
455 ROOT.gROOT.SetBatch()
456 ROOT.gErrorIgnoreLevel = ROOT.kWarning
458 if os.path.exists(
'rootlogon.C'):
459 ROOT.gROOT.Macro(
'rootlogon.C')
460 sys.argv = saved_argv[:]
465 Modify a string based on a list of patterns and substitutions. 467 replacements should be a list of two-entry tuples, the first entry giving 468 a string to search for and the second entry giving the string with which 469 to replace it. If replacements includes a pattern entry containing 470 'use_regexp', then all patterns will be treated as regular expressions 475 if 'use_regexp' in [x
for x,y
in replacements]:
476 for pattern, repl
in [x
for x
in replacements
477 if x[0] !=
'use_regexp']:
478 string = re.sub(pattern, repl, string)
480 for pattern, repl
in replacements:
481 string = string.replace(pattern, repl)
482 if re.match(_all_whitespace_string, string):
488 for binlabel
in binlabels:
497 eff, upper_err, lower_err = [], [], []
498 for n, d
in zip(numerator_array, denominator_array):
501 s = math.sqrt(p * (1 - p) / d + 1 / (4 * d * d))
504 upper_err.append(-p + 1/(1 + 1/d) * (t + s))
505 lower_err.append(+p - 1/(1 + 1/d) * (t - s))
506 except ZeroDivisionError:
510 return eff, upper_err, lower_err
515 num_processors = os.sysconf(
'SC_NPROCESSORS_ONLN')
518 num_processors = os.environ[
'NUMBER_OF_PROCESSORS']
521 return num_processors
524 outfile = ROOT.TFile(
"test.root",
"recreate")
526 d = outfile.mkdir(
"dir%i" % (i + 1))
529 hist = ROOT.TH1F(
"hist%i" % (j + 1),
"A Histogram", 10, 0, 10)
537 glob_magic_check = re.compile(
'[*?[]')
540 return glob_magic_check.search(s)
is not None 548 if not tdirectory.GetDirectory(dirname):
550 names = [key.GetName()
for key
in 551 tdirectory.GetDirectory(dirname).GetListOfKeys()]
552 return fnmatch.filter(names, pattern)
555 if tdirectory.Get(os.path.join(dirname, basename)):
560 """Return a list of paths matching a pathname pattern. 562 The pattern may contain simple shell-style wildcards a la fnmatch. 564 >>> import rootplot.utilities 565 >>> f = rootplot.utilities.testfile() 567 ['dir1', 'dir2', 'dir3', 'dir4'] 568 >>> rootglob(f, 'dir1/*') 569 ['dir1/hist1', 'dir1/hist2', 'dir1/hist3', 'dir1/hist4'] 570 >>> rootglob(f, '*/hist1') 571 ['dir1/hist1', 'dir2/hist1', 'dir3/hist1', 'dir4/hist1'] 572 >>> rootglob(f, 'dir1/hist[1-2]') 573 ['dir1/hist1', 'dir1/hist2'] 578 """Return an iterator which yields the paths matching a pathname pattern. 580 The pattern may contain simple shell-style wildcards a la fnmatch. 584 if tdirectory.Get(pathname):
587 dirname, basename = os.path.split(pathname)
593 glob_in_dir = _rootglob1
595 glob_in_dir = _rootglob0
597 for name
in glob_in_dir(tdirectory, dirname, basename):
598 yield os.path.join(dirname, name)
600 if __name__ ==
'__main__':
def __init__(self, hists=None, title=None, xlabel=None, ylabel=None)
def TH1F(self, name=None)
def loadROOT(batch=True)
Define additional helping functions.
def __init_TH1(self, hist)
def process_bin_labels(binlabels)
def _rootglob0(tdirectory, dirname, basename)
def min(self, threshold=None)
def divide(self, denominator)
def delete_bin(self, index)
def __init__(self, hist, label="__nolabel__", name=None, title=None, xlabel=None, ylabel=None)
S & print(S &os, JobReport::InputFile const &f)
def replace(string, replacements)
def __init__(self, filename, name=None)
def __getitem__(self, index)
def irootglob(tdirectory, pathname)
def __getitem__(self, index)
def __getitem__(self, index)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def min(self, threshold=None)
def __setitem__(self, index, value)
def _rootglob1(tdirectory, dirname, pattern)
def find_num_processors()
def wilson_interval(numerator_array, denominator_array)
static std::string join(char **cmd)
def __div__(self, denominator)
def add(self, hist, kwargs)
def __setitem__(self, index, value)
def TGraph(self, name=None)
def rootglob(tdirectory, pathname)
def __init__(self, hist, label="__nolabel__", title=None, xlabel=None, ylabel=None)
def __init_TGraph(self, hist)
def cd(self, directory='')
def get(self, object_name, path=None, type1D=Hist, type2D=Hist2D)
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run
def divide_wilson(self, denominator)