1 from __future__
import print_function
2 from __future__
import absolute_import
3 from builtins
import range
14 ROOT.gROOT.SetBatch(
True)
15 ROOT.PyConfig.IgnoreCommandLineOptions =
True 20 _ratioYTitle =
"Ratio" 35 ROOT.gROOT.SetStyle(
"Plain")
36 ROOT.gStyle.SetPadRightMargin(0.07)
37 ROOT.gStyle.SetPadLeftMargin(0.13)
38 ROOT.gStyle.SetTitleFont(font,
"XYZ")
39 ROOT.gStyle.SetTitleSize(titleSize,
"XYZ")
40 ROOT.gStyle.SetTitleOffset(1.2,
"Y")
42 ROOT.gStyle.SetLabelFont(font,
"XYZ")
43 ROOT.gStyle.SetLabelSize(labelSize,
"XYZ")
44 ROOT.gStyle.SetTextSize(labelSize)
45 ROOT.gStyle.SetStatFont(font)
46 ROOT.gStyle.SetStatFontSize(statSize)
48 ROOT.TGaxis.SetMaxDigits(4)
51 obj = tdirectory.Get(name)
54 print(
"Did not find {obj} from {dir}".
format(obj=name, dir=tdirectory.GetPath()))
59 if hasattr(nameOrCreator,
"create"):
60 return nameOrCreator.create(tdirectory)
75 """Get TDirectory from TFile.""" 78 for pdf
in possibleDirs:
81 if subDir
is not None:
88 print(
"Did not find subdirectory '%s' from directory '%s' in file %s" % (subDir, pdf, tfile.GetName()))
95 print(
"Did not find any of directories '%s' from file %s" % (
",".
join(possibleDirs), tfile.GetName()))
102 values = collections.OrderedDict()
103 for i
in range(1, th1.GetNbinsX()+1):
104 binLabel = th1.GetXaxis().GetBinLabel(i)
105 if renameBin
is not None:
106 binLabel = renameBin(binLabel)
107 values[binLabel] = (th1.GetBinContent(i), th1.GetBinError(i))
113 backup = ROOT.gErrorIgnoreLevel
114 ROOT.gErrorIgnoreLevel = ROOT.kError
115 canvas = ROOT.TCanvas(name, name, width, height)
117 ROOT.gErrorIgnoreLevel = backup
123 divisionPoint = 1-1/ratioFactor
125 topMargin = pad.GetTopMargin()
126 bottomMargin = pad.GetBottomMargin()
127 divisionPoint += (1-divisionPoint)*bottomMargin
128 divisionPointForPad1 = 1-( (1-divisionPoint) / (1-0.02) )
133 ylow = divisionPointForPad1
136 pad1.SetPad(xlow, ylow, xup, yup)
137 pad1.SetFillStyle(4000)
138 pad1.SetBottomMargin(0.02)
144 pad2.SetPad(xlow, ylow, xup, yup)
145 pad2.SetFillStyle(4000)
146 pad2.SetTopMargin(0.0)
147 pad2.SetBottomMargin(bottomMargin/(ratioFactor*divisionPoint))
150 """Calculate the ratios for a list of histograms""" 152 def _divideOrZero(numerator, denominator):
155 return numerator/denominator
158 if a == 0.
and b == 0.:
162 def findBins(wrap, bins_xvalues):
164 currBin = wrap.begin()
166 while i < len(bins_xvalues)
and currBin < wrap.end():
167 (xcenter, xlow, xhigh) = bins_xvalues[i]
168 xlowEdge = xcenter-xlow
169 xupEdge = xcenter+xhigh
171 (curr_center, curr_low, curr_high) = wrap.xvalues(currBin)
172 curr_lowEdge = curr_center-curr_low
173 curr_upEdge = curr_center+curr_high
175 if equal(xlowEdge, curr_lowEdge)
and equal(xupEdge, curr_upEdge):
179 elif curr_upEdge <= xlowEdge:
181 elif curr_lowEdge >= xupEdge:
188 if len(ret) != len(bins_xvalues):
189 ret.extend([
None]*( len(bins_xvalues) - len(ret) ))
195 def __init__(self, th1, uncertainty):
197 self._uncertainty = uncertainty
199 xaxis = th1.GetXaxis()
200 xaxis_arr = xaxis.GetXbins()
201 if xaxis_arr.GetSize() > 0:
202 lst = [xaxis_arr[i]
for i
in range(0, xaxis_arr.GetSize())]
203 arr = array.array(
"d", lst)
204 self._ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), arr)
206 self._ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), xaxis.GetXmin(), xaxis.GetXmax())
208 self._ratio.SetStats(0)
209 self._ratio.SetLineColor(ROOT.kBlack)
210 self._ratio.SetLineWidth(1)
211 def draw(self, style=None):
214 if self._uncertainty:
218 self._ratio.Draw(
"same "+st)
222 return self._th1.GetNbinsX()+1
223 def xvalues(self, bin):
224 xval = self._th1.GetBinCenter(bin)
225 xlow = xval-self._th1.GetXaxis().GetBinLowEdge(bin)
226 xhigh = self._th1.GetXaxis().GetBinUpEdge(bin)-xval
227 return (xval, xlow, xhigh)
228 def yvalues(self, bin):
229 yval = self._th1.GetBinContent(bin)
230 yerr = self._th1.GetBinError(bin)
231 return (yval, yerr, yerr)
233 return self._th1.GetBinContent(bin)
234 def divide(self, bin, scale):
235 self._ratio.SetBinContent(bin, _divideOrZero(self._th1.GetBinContent(bin), scale))
236 self._ratio.SetBinError(bin, _divideOrZero(self._th1.GetBinError(bin), scale))
243 def __init__(self, gr, uncertainty):
245 self._uncertainty = uncertainty
252 def draw(self, style=None):
253 if self._ratio
is None:
257 if self._uncertainty:
261 self._ratio.Draw(
"same "+st)
265 return self._gr.GetN()
266 def xvalues(self, bin):
267 return (self._gr.GetX()[bin], self._gr.GetErrorXlow(bin), self._gr.GetErrorXhigh(bin))
268 def yvalues(self, bin):
269 return (self._gr.GetY()[bin], self._gr.GetErrorYlow(bin), self._gr.GetErrorYhigh(bin))
271 return self._gr.GetY()[bin]
272 def divide(self, bin, scale):
277 if bin >= self._gr.GetN():
280 xvals = self.xvalues(bin)
283 self._xvalues.append(xval)
284 self._xerrslow.append(xvals[1])
285 self._xerrshigh.append(xvals[2])
286 yvals = self.yvalues(bin)
287 self._yvalues.append(yvals[0] / scale)
288 if self._uncertainty:
289 self._yerrslow.append(yvals[1] / scale)
290 self._yerrshigh.append(yvals[2] / scale)
292 self._yerrslow.append(0)
293 self._yerrshigh.append(0)
295 if len(self._xvalues) == 0:
298 self._ratio = ROOT.TGraphAsymmErrors(len(self._xvalues), array.array(
"d", self._xvalues), array.array(
"d", self._yvalues),
299 array.array(
"d", self._xerrslow), array.array(
"d", self._xerrshigh),
300 array.array(
"d", self._yerrslow), array.array(
"d", self._yerrshigh))
304 class WrapTGraph2D(WrapTGraph):
305 def __init__(self, gr, uncertainty):
306 WrapTGraph.__init__(self, gr, uncertainty)
307 def xvalues(self, bin):
308 return (self._gr.GetX()[bin], self._gr.GetErrorX(bin), self._gr.GetErrorX(bin))
309 def yvalues(self, bin):
310 return (self._gr.GetY()[bin], self._gr.GetErrorY(bin), self._gr.GetErrorY(bin))
313 if isinstance(o, ROOT.TH1):
314 return WrapTH1(o, ratioUncertainty)
315 elif isinstance(o, ROOT.TGraph):
316 return WrapTGraph(o, ratioUncertainty)
317 elif isinstance(o, ROOT.TGraph2D):
318 return WrapTGraph2D(o, ratioUncertainty)
320 wrappers = [
wrap(h)
for h
in histos]
324 ref_bins = [ref.xvalues(b)
for b
in range(ref.begin(), ref.end())]
326 wrappers_bins.append(findBins(w, ref_bins))
328 for i, bin
in enumerate(
range(ref.begin(), ref.end())):
329 (scale, ylow, yhigh) = ref.yvalues(bin)
330 for w, bins
in zip(wrappers, wrappers_bins):
333 w.divide(bins[i], scale)
342 if isinstance(obj, ROOT.TH1):
343 xaxis = obj.GetXaxis()
344 if limitToNonZeroContent:
345 for i
in range(1, obj.GetNbinsX()+1):
346 if obj.GetBinContent(i) != 0:
347 return xaxis.GetBinLowEdge(i)
351 return xaxis.GetBinLowEdge(xaxis.GetFirst())
352 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
353 m =
min([obj.GetX()[i]
for i
in range(0, obj.GetN())])
354 return m*0.9
if m > 0
else m*1.1
358 if isinstance(obj, ROOT.TH1):
359 xaxis = obj.GetXaxis()
360 if limitToNonZeroContent:
361 for i
in range(obj.GetNbinsX(), 0, -1):
362 if obj.GetBinContent(i) != 0:
363 return xaxis.GetBinUpEdge(i)
367 return xaxis.GetBinUpEdge(xaxis.GetLast())
368 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
369 m =
max([obj.GetX()[i]
for i
in range(0, obj.GetN())])
370 return m*1.1
if m > 0
else m*0.9
374 if isinstance(obj, ROOT.TH2):
375 yaxis = obj.GetYaxis()
376 return yaxis.GetBinLowEdge(yaxis.GetFirst())
377 elif isinstance(obj, ROOT.TH1):
378 if limitToNonZeroContent:
379 lst = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if obj.GetBinContent(i) != 0 ]
380 return min(lst)
if len(lst) != 0
else 0
382 return obj.GetMinimum()
383 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
384 m =
min([obj.GetY()[i]
for i
in range(0, obj.GetN())])
385 return m*0.9
if m > 0
else m*1.1
389 if isinstance(obj, ROOT.TH2):
390 yaxis = obj.GetYaxis()
391 return yaxis.GetBinUpEdge(yaxis.GetLast())
392 elif isinstance(obj, ROOT.TH1):
393 if limitToNonZeroContent:
394 lst = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if obj.GetBinContent(i) != 0 ]
395 return max(lst)
if len(lst) != 0
else 0
397 return obj.GetMaximum()
398 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
399 m =
max([obj.GetY()[i]
for i
in range(0, obj.GetN())])
400 return m*1.1
if m > 0
else m*0.9
404 return max([th1.GetBinContent(i)+th1.GetBinError(i)
for i
in range(1, th1.GetNbinsX()+1)])
407 yvals = sorted([n
for n
in [th1.GetBinContent(i)
for i
in range(1, th1.GetNbinsX()+1)]
if n>0])
409 return th1.GetMinimum()
414 ind_min = len(yvals)-1 -
int(len(yvals)*0.95)
415 min_val = yvals[ind_min]
416 for i
in range(0, ind_min):
417 if yvals[i] > 0.1*min_val:
423 inRange =
lambda x:
True 424 inRange2 =
lambda xmin,xmax:
True 426 inRange =
lambda x: coverageRange[0] <= x <= coverageRange[1]
427 inRange2 =
lambda xmin,xmax: coverageRange[0] <= xmin
and xmax <= coverageRange[1]
429 if isinstance(obj, ROOT.TH1):
430 yvals = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if inRange2(obj.GetXaxis().GetBinLowEdge(i), obj.GetXaxis().GetBinUpEdge(i))]
431 yvals = [x
for x
in yvals
if x != 0]
432 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
433 yvals = [obj.GetY()[i]
for i
in range(0, obj.GetN())
if inRange(obj.GetX()[i])]
439 return (yvals[0], yvals[0])
441 return (yvals[0], yvals[1])
444 nvals =
int(len(yvals)*coverage)
447 if len(yvals) % 2 == 0:
449 return ( yvals[half-1], yvals[half] )
451 middle = len(yvals)/2
452 return ( yvals[middle-1], yvals[middle+1] )
453 ind_min = (len(yvals)-nvals)/2
454 ind_max = len(yvals)-1 - ind_min
456 return (yvals[ind_min], yvals[ind_max])
458 def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None):
459 """Find x-y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments. 463 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax) 466 xmin -- Minimum x value; if None, take the minimum of TH1s 467 xmax -- Maximum x value; if None, take the maximum of TH1s 468 ymin -- Minimum y value; if None, take the minimum of TH1s 469 ymax -- Maximum y value; if None, take the maximum of TH1s 474 if xmin
is None or xmax
is None or isinstance(xmin, list)
or isinstance(max, list):
478 xmins.append(
_getXmin(th1, limitToNonZeroContent=isinstance(xmin, list)))
479 xmaxs.append(
_getXmax(th1, limitToNonZeroContent=isinstance(xmax, list)))
482 xmins = [h
for h
in xmins
if h
is not None]
483 xmaxs = [h
for h
in xmaxs
if h
is not None]
487 elif isinstance(xmin, list):
491 print(
"Histogram is zero, using the smallest given value for xmin from",
str(xmin))
494 xmins_below = [x
for x
in xmin
if x<=xm]
495 if len(xmins_below) == 0:
499 print(
"Histogram minimum x %f is below all given xmin values %s, using the smallest one" % (xm,
str(xmin)))
501 xmin =
max(xmins_below)
505 elif isinstance(xmax, list):
509 print(
"Histogram is zero, using the smallest given value for xmax from",
str(xmin))
512 xmaxs_above = [x
for x
in xmax
if x>xm]
513 if len(xmaxs_above) == 0:
517 print(
"Histogram maximum x %f is above all given xmax values %s, using the maximum one" % (xm,
str(xmax)))
519 xmax =
min(xmaxs_above)
522 th1.GetXaxis().SetRangeUser(xmin, xmax)
524 return (xmin, ymin, xmax, ymax)
526 def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None):
527 """Find y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments. 531 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax) 534 ymin -- Minimum y value; if None, take the minimum of TH1s 535 ymax -- Maximum y value; if None, take the maximum of TH1s 536 coverage -- If set, use only values within the 'coverage' part around the median are used for min/max (useful for ratio) 537 coverageRange -- If coverage and this are set, use only the x axis specified by an (xmin,xmax) pair for the coverage 539 if coverage
is not None or isinstance(th1s[0], ROOT.TH2):
545 y_scale_max =
lambda y: y
546 y_scale_min =
lambda y: y
549 y_scale_max =
lambda y: y*1.5
551 y_scale_max =
lambda y: y*1.05
552 y_scale_min =
lambda y: y*0.9
554 if ymin
is None or ymax
is None or isinstance(ymin, list)
or isinstance(ymax, list):
558 if coverage
is not None:
561 if ylog
and isinstance(ymin, list):
564 _ymin =
_getYmin(th1, limitToNonZeroContent=isinstance(ymin, list))
565 _ymax =
_getYmax(th1, limitToNonZeroContent=isinstance(ymax, list))
573 elif isinstance(ymin, list):
574 ym_unscaled =
min(ymins)
575 ym = y_scale_min(ym_unscaled)
576 ymins_below = [y
for y
in ymin
if y<=ym]
577 if len(ymins_below) == 0:
579 if ym_unscaled < ymin:
581 print(
"Histogram minimum y %f is below all given ymin values %s, using the smallest one" % (ym,
str(ymin)))
583 ymin =
max(ymins_below)
588 ymax = y_scale_max(
max(ymaxs+[ymin]))
589 elif isinstance(ymax, list):
590 ym_unscaled =
max(ymaxs)
591 ym = y_scale_max(ym_unscaled)
592 ymaxs_above = [y
for y
in ymax
if y>ym]
593 if len(ymaxs_above) == 0:
595 if ym_unscaled > ymax:
597 print(
"Histogram maximum y %f is above all given ymax values %s, using the maximum one" % (ym_unscaled,
str(ymax)))
599 ymax =
min(ymaxs_above)
602 th1.GetYaxis().SetRangeUser(ymin, ymax)
608 for b
in range(1, histos[0].GetNbinsX()+1):
611 if h.GetBinContent(b) > 0:
617 if len(binsToRemove) > 0:
620 for i
in range(len(xbinlabels)):
621 if (i+1)
not in binsToRemove:
622 xbinlab_new.append(xbinlabels[i])
623 xbinlabels = xbinlab_new
629 for b
in range(1, h.GetNbinsX()+1):
630 if b
not in binsToRemove:
631 values.append( (h.GetXaxis().GetBinLabel(b), h.GetBinContent(b), h.GetBinError(b)) )
634 h_new = h.Clone(h.GetName()+
"_empty")
635 h_new.SetBins(len(values), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(values))
636 for b, (l, v, e)
in enumerate(values):
637 h_new.GetXaxis().SetBinLabel(b+1, l)
638 h_new.SetBinContent(b+1, v)
639 h_new.SetBinError(b+1, e)
641 histos_new.append(h_new)
644 return (histos, xbinlabels)
647 xbinsToRemove = set()
648 ybinsToRemove = set()
649 for ih, h
in enumerate(histos):
650 for bx
in range(1, h.GetNbinsX()+1):
652 for by
in range(1, h.GetNbinsY()+1):
653 if h.GetBinContent(bx, by) > 0:
657 xbinsToRemove.add(bx)
659 xbinsToRemove.discard(bx)
661 for by
in range(1, h.GetNbinsY()+1):
663 for bx
in range(1, h.GetNbinsX()+1):
664 if h.GetBinContent(bx, by) > 0:
668 ybinsToRemove.add(by)
670 ybinsToRemove.discard(by)
672 if len(xbinsToRemove) > 0
or len(ybinsToRemove) > 0:
675 for b
in range(1, len(xbinlabels)+1):
676 if b
not in xbinsToRemove:
677 xbinlabels_new.append(histos[0].GetXaxis().GetBinLabel(b))
679 xbinlabels = xbinlabels_new
682 for b
in range(1, len(ybinlabels)+1):
683 if b
not in ybinsToRemove:
684 ybinlabels.append(histos[0].GetYaxis().GetBinLabel(b))
686 ybinlabels = xbinlabels_new
689 if len(xbinlabels) == 0
or len(ybinlabels) == 0:
690 return (histos_new, xbinlabels, ybinlabels)
692 h_new = ROOT.TH2F(h.GetName()+
"_empty", h.GetTitle(), len(xbinlabels),0,len(xbinlabels), len(ybinlabels),0,len(ybinlabels))
693 for b, l
in enumerate(xbinlabels):
694 h_new.GetXaxis().SetBinLabel(b+1, l)
695 for b, l
in enumerate(ybinlabels):
696 h_new.GetYaxis().SetBinLabel(b+1, l)
698 for ix, bx
in enumerate(xbins):
699 for iy, by
in enumerate(ybins):
700 h_new.SetBinContent(ix+1, iy+1, h.GetBinContent(bx, by))
701 h_new.SetBinError(ix+1, iy+1, h.GetBinError(bx, by))
702 histos_new.append(h_new)
704 return (histos, xbinlabels, ybinlabels)
707 return _mergeBinLabels([[h.GetXaxis().GetBinLabel(i)
for i
in range(1, h.GetNbinsX()+1)]
for h
in histos])
710 return _mergeBinLabels([[h.GetYaxis().GetBinLabel(i)
for i
in range(1, h.GetNbinsY()+1)]
for h
in histos])
713 labels_merged = labelsAll[0]
714 for labels
in labelsAll[1:]:
715 diff = difflib.unified_diff(labels_merged, labels, n=
max(len(labels_merged), len(labels)))
722 operation.append(item[0])
724 if lab
in labels_merged:
726 ind = labels_merged.index(lab)
727 if operation[ind] ==
"-" and operation[-1] ==
"+":
728 labels_merged.remove(lab)
730 elif operation[ind] ==
"+" and operation[-1] ==
"-":
734 raise Exception(
"This should never happen")
735 labels_merged.append(lab)
738 if len(labels_merged) == 0:
739 labels_merged = labels
746 h_new = h.Clone(h.GetName()+
"_xbinlabels")
747 h_new.SetBins(len(xbinlabels), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(xbinlabels))
748 for i, label
in enumerate(xbinlabels):
749 bin = h.GetXaxis().FindFixBin(label)
751 h_new.SetBinContent(i+1, h.GetBinContent(bin))
752 h_new.SetBinError(i+1, h.GetBinError(bin))
754 h_new.SetBinContent(i+1, 0)
755 h_new.SetBinError(i+1, 0)
756 histos_new.append(h_new)
761 """Class for subtracting two histograms""" 766 name -- String for name of the resulting histogram (A-B) 767 nameA -- String for A histogram 768 nameB -- String for B histogram 771 title -- String for a title of the resulting histogram (default "") 773 Uncertainties are calculated with the assumption that B is a 774 subset of A, and the histograms contain event counts. 782 """String representation, returns the name""" 786 """Create and return the fake+duplicate histogram from a TDirectory""" 790 if not histoA
or not histoB:
793 ret = histoA.Clone(self.
_name)
799 ret.SetCanExtend(
False)
801 for i
in range(0, histoA.GetNbinsX()+2):
802 val = histoA.GetBinContent(i)-histoB.GetBinContent(i)
803 ret.SetBinContent(i, val)
804 ret.SetBinError(i, math.sqrt(val))
809 """Class to transform bin contents in an arbitrary way.""" 814 name -- String for name of the resulting histogram 815 histo -- String for a source histogram (needs to be cumulative) 816 func -- Function to operate on the bin content 824 """String representation, returns the name""" 828 """Create and return the transformed histogram from a TDirectory""" 833 ret = histo.Clone(self.
_name)
839 ret.SetCanExtend(
False)
841 for i
in range(0, histo.GetNbinsX()+2):
842 ret.SetBinContent(i, self.
_func(histo.GetBinContent(i)))
846 """Class to calculate the fake+duplicate rate""" 847 def __init__(self, name, assoc, dup, reco, title=""):
851 name -- String for the name of the resulting efficiency histogram 852 assoc -- String for the name of the "associated" histogram 853 dup -- String for the name of the "duplicates" histogram 854 reco -- String for the name of the "reco" (denominator) histogram 857 title -- String for a title of the resulting histogram (default "") 859 The result is calculated as 1 - (assoc - dup) / reco 868 """String representation, returns the name""" 872 """Create and return the fake+duplicate histogram from a TDirectory""" 879 if not hassoc
or not hdup
or not hreco:
882 hfakedup = hreco.Clone(self.
_name)
883 hfakedup.SetTitle(self.
_title)
885 for i
in range(1, hassoc.GetNbinsX()+1):
886 numerVal = hassoc.GetBinContent(i) - hdup.GetBinContent(i)
887 denomVal = hreco.GetBinContent(i)
889 fakedupVal = (1 - numerVal / denomVal)
if denomVal != 0.0
else 0.0
890 errVal = math.sqrt(fakedupVal*(1-fakedupVal)/denomVal)
if (denomVal != 0.0
and fakedupVal <= 1)
else 0.0
892 hfakedup.SetBinContent(i, fakedupVal)
893 hfakedup.SetBinError(i, errVal)
898 """Class for making a cut efficiency histograms. 908 name -- String for name of the resulting histogram 909 histo -- String for a source histogram (needs to be cumulative) 916 """String representation, returns the name""" 920 """Create and return the cut efficiency histogram from a TDirectory""" 926 ascending = histo.GetBinContent(0) < histo.GetBinContent(histo.GetNbinsX())
928 n_tot = histo.GetBinContent(histo.GetNbinsX())
930 n_tot = histo.GetBinContent(0)
935 ret = histo.Clone(self.
_name)
939 for i
in range(1, histo.GetNbinsX()+1):
940 n = histo.GetBinContent(i)
942 errVal = math.sqrt(val*(1-val)/n_tot)
943 ret.SetBinContent(i, val)
944 ret.SetBinError(i, errVal)
948 """Class to create a histogram by aggregating bins of another histogram to a bin of the resulting histogram.""" 949 def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False, reorder=None):
953 name -- String for the name of the resulting histogram 954 histoName -- String for the name of the source histogram 955 mapping -- Dictionary for mapping the bins (see below) 958 normalizeTo -- Optional string of a bin label in the source histogram. If given, all bins of the resulting histogram are divided by the value of this bin. 959 scale -- Optional number for scaling the histogram (passed to ROOT.TH1.Scale()) 960 renameBin -- Optional function (string -> string) to rename the bins of the input histogram 961 originalOrder -- Boolean for using the order of bins in the histogram (default False) 962 reorder -- Optional function to reorder the bins 964 Mapping structure (mapping): 966 Dictionary (you probably want to use collections.OrderedDict) 967 should be a mapping from the destination bin label to a list 968 of source bin labels ("dst -> [src]"). 981 raise Exception(
"reorder is not None and originalOrder is True, please set only one of them")
984 """String representation, returns the name""" 988 """Create and return the histogram from a TDirectory""" 994 binValues = [
None]*len(self.
_mapping)
1000 for i, (key, labels)
in enumerate(six.iteritems(self.
_mapping)):
1006 sumTime += values[l][0]
1007 sumErrorSq += values[l][1]**2
1013 binValues[i] = (sumTime, math.sqrt(sumErrorSq))
1016 ivalue = len(values)+1
1022 ivalue = values.keys().
index(lab)
1024 binIndexOrder.append( (ivalue, i) )
1027 binIndexOrder.sort(key=
lambda t: t[0])
1030 for i
in range(0, len(binValues)):
1031 fromIndex = binIndexOrder[i][1]
1032 tmpVal.append(binValues[fromIndex])
1033 tmpLab.append(binLabels[fromIndex])
1037 order = self.
_reorder(tdirectory, binLabels)
1038 binValues = [binValues[i]
for i
in order]
1039 binLabels = [binLabels[i]
for i
in order]
1045 for i, val
in enumerate(binValues):
1048 binValues = [v
for v
in binValues
if v
is not None]
1049 binLabels = [v
for v
in binLabels
if v
is not None]
1050 if len(binValues) == 0:
1053 result = ROOT.TH1F(self.
_name, self.
_name, len(binValues), 0, len(binValues))
1054 for i, (value, label)
in enumerate(
zip(binValues, binLabels)):
1055 if value
is not None:
1056 result.SetBinContent(i+1, value[0])
1057 result.SetBinError(i+1, value[1])
1058 result.GetXaxis().SetBinLabel(i+1, label)
1065 value = th1.GetBinContent(bin)
1067 result.Scale(1/value)
1069 if self.
_scale is not None:
1070 result.Scale(self.
_scale)
1075 """Class to create a histogram by aggregaging integrals of another histoggrams.""" 1080 name -- String for the name of the resulting histogram 1081 mapping -- Dictionary for mapping the bin label to a histogram name 1084 normalizeTo -- Optional string for a histogram. If given, all bins of the resulting histograqm are divided by the integral of this histogram. 1091 """String representation, returns the name""" 1095 """Create and return the histogram from a TDirectory""" 1097 for key, histoName
in six.iteritems(self.
_mapping):
1101 result.append( (key, th1.Integral(0, th1.GetNbinsX()+1)) )
1102 if len(result) == 0:
1105 res = ROOT.TH1F(self.
_name, self.
_name, len(result), 0, len(result))
1107 for i, (name, count)
in enumerate(result):
1108 res.SetBinContent(i+1, count)
1109 res.GetXaxis().SetBinLabel(i+1, name)
1115 scale = th1.Integral(0, th1.GetNbinsX()+1)
1121 """Class to construct a ROC curve (e.g. efficiency vs. fake rate) from two histograms""" 1122 def __init__(self, name, xhistoName, yhistoName, zaxis=False):
1126 name -- String for the name of the resulting histogram 1127 xhistoName -- String for the name of the x-axis histogram (or another "creator" object) 1128 yhistoName -- String for the name of the y-axis histogram (or another "creator" object) 1131 zaxis -- If set to True (default False), create a TGraph2D with z axis showing the cut value (recommended drawStyle 'pcolz') 1139 """String representation, returns the name""" 1143 """Create and return the histogram from a TDirectory""" 1146 if xhisto
is None or yhisto
is None:
1157 for i
in range(1, xhisto.GetNbinsX()+1):
1158 x.append(xhisto.GetBinContent(i))
1159 xerrup.append(xhisto.GetBinError(i))
1160 xerrdown.append(xhisto.GetBinError(i))
1162 y.append(yhisto.GetBinContent(i))
1163 yerrup.append(yhisto.GetBinError(i))
1164 yerrdown.append(yhisto.GetBinError(i))
1166 z.append(xhisto.GetXaxis().GetBinUpEdge(i))
1169 if x.count(0.0) == len(x)
or y.count(0.0) == len(y):
1172 arr =
lambda v: array.array(
"d", v)
1175 gr = ROOT.TGraph2D(len(x), arr(x), arr(y), arr(z))
1177 gr = ROOT.TGraphAsymmErrors(len(x), arr(x), arr(y), arr(xerrdown), arr(xerrup), arr(yerrdown), arr(yerrup))
1183 _plotStylesColor = [4, 2, ROOT.kBlack, ROOT.kOrange+7, ROOT.kMagenta-3]
1184 _plotStylesMarker = [21, 20, 22, 34, 33]
1186 def _drawFrame(pad, bounds, zmax=None, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None, suffix=""):
1187 """Function to draw a frame 1190 pad -- TPad to where the frame is drawn 1191 bounds -- List or 4-tuple for (xmin, ymin, xmax, ymax) 1194 zmax -- Maximum Z, needed for TH2 histograms 1195 xbinlabels -- Optional list of strings for x axis bin labels 1196 xbinlabelsize -- Optional number for the x axis bin label size 1197 xbinlabeloption -- Optional string for the x axis bin options (passed to ROOT.TH1.LabelsOption()) 1198 suffix -- Optional string for a postfix of the frame name 1200 if xbinlabels
is None and ybinlabels
is None:
1201 frame = pad.DrawFrame(*bounds)
1204 nbins = len(xbinlabels)
1205 if ybinlabels
is None:
1206 frame = ROOT.TH1F(
"hframe"+suffix,
"", nbins, bounds[0], bounds[2])
1207 frame.SetMinimum(bounds[1])
1208 frame.SetMaximum(bounds[3])
1209 frame.GetYaxis().SetLimits(bounds[1], bounds[3])
1211 ybins = len(ybinlabels)
1212 frame = ROOT.TH2F(
"hframe"+suffix,
"", nbins,bounds[0],bounds[2], ybins,bounds[1],bounds[3])
1213 frame.SetMaximum(zmax)
1215 frame.SetBit(ROOT.TH1.kNoStats)
1216 frame.SetBit(ROOT.kCanDelete)
1219 xaxis = frame.GetXaxis()
1220 for i
in range(nbins):
1221 xaxis.SetBinLabel(i+1, xbinlabels[i])
1222 if xbinlabelsize
is not None:
1223 xaxis.SetLabelSize(xbinlabelsize)
1224 if xbinlabeloption
is not None:
1225 frame.LabelsOption(xbinlabeloption)
1227 if ybinlabels
is not None:
1228 yaxis = frame.GetYaxis()
1229 for i, lab
in enumerate(ybinlabels):
1230 yaxis.SetBinLabel(i+1, lab)
1231 if xbinlabelsize
is not None:
1232 yaxis.SetLabelSize(xbinlabelsize)
1233 if xbinlabeloption
is not None:
1234 frame.LabelsOption(xbinlabeloption,
"Y")
1239 """Class for creating and managing a frame for a simple, one-pad plot""" 1240 def __init__(self, pad, bounds, zmax, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None):
1242 self.
_frame =
_drawFrame(pad, bounds, zmax, xbinlabels, xbinlabelsize, xbinlabeloption, ybinlabels)
1250 yoffsetFactor *= 1.5
1251 xoffsetFactor *= 1.5
1256 self._frame.GetYaxis().SetTitleOffset(self._frame.GetYaxis().GetTitleOffset()*yoffsetFactor)
1257 self._frame.GetXaxis().SetTitleOffset(self._frame.GetXaxis().GetTitleOffset()*xoffsetFactor)
1261 self._pad.SetLogx(log)
1264 self._pad.SetLogy(log)
1267 self._pad.SetGridx(grid)
1270 self._pad.SetGridy(grid)
1273 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1276 self._frame.Draw(
"")
1279 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1282 self._frame.Draw(
"")
1285 self._frame.SetTitle(title)
1288 self._frame.GetXaxis().SetTitle(title)
1291 self._frame.GetXaxis().SetTitleSize(size)
1294 self._frame.GetXaxis().SetTitleOffset(offset)
1297 self._frame.GetXaxis().SetLabelSize(size)
1300 self._frame.GetYaxis().SetTitle(title)
1303 self._frame.GetYaxis().SetTitleSize(size)
1306 self._frame.GetYaxis().SetTitleOffset(offset)
1309 self._pad.RedrawAxis()
1312 """Class for creating and managing a frame for a ratio plot with two subpads""" 1313 def __init__(self, pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ratioYTitle=_ratioYTitle):
1316 if xbinlabels
is not None:
1323 self._frame.GetXaxis().SetLabelSize(0)
1324 self._frame.GetXaxis().SetTitleSize(0)
1326 yoffsetFactor = ratioFactor
1327 divisionPoint = 1-1/ratioFactor
1328 xoffsetFactor = 1/divisionPoint
1331 xoffsetFactor *= 0.6
1334 xoffsetFactor *= 1.5
1337 xoffsetFactor *= 2.3
1342 self._frame.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1343 self._frameRatio.GetYaxis().SetLabelSize(
int(self._frameRatio.GetYaxis().GetLabelSize()*0.8))
1344 self._frameRatio.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1345 self._frameRatio.GetXaxis().SetTitleOffset(self._frameRatio.GetXaxis().GetTitleOffset()*xoffsetFactor)
1347 self._frameRatio.GetYaxis().SetNdivisions(4, 5, 0)
1349 self._frameRatio.GetYaxis().SetTitle(ratioYTitle)
1352 self._pad.SetLogx(log)
1353 self._padRatio.SetLogx(log)
1356 self._pad.SetLogy(log)
1359 self._pad.SetGridx(grid)
1360 self._padRatio.SetGridx(grid)
1363 self._pad.SetGridy(grid)
1364 self._padRatio.SetGridy(grid)
1367 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1368 self._padRatio.SetLeftMargin(self._padRatio.GetLeftMargin()+adjust)
1371 self._frame.Draw(
"")
1373 self._frameRatio.Draw(
"")
1376 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1377 self._padRatio.SetRightMargin(self._padRatio.GetRightMargin()+adjust)
1380 self._frame.Draw(
"")
1382 self._frameRatio.Draw(
"")
1385 self._frame.SetTitle(title)
1388 self._frameRatio.GetXaxis().SetTitle(title)
1391 self._frameRatio.GetXaxis().SetTitleSize(size)
1394 self._frameRatio.GetXaxis().SetTitleOffset(offset)
1397 self._frameRatio.GetXaxis().SetLabelSize(size)
1400 self._frame.GetYaxis().SetTitle(title)
1403 self._frameRatio.GetYaxis().SetTitle(title)
1406 self._frame.GetYaxis().SetTitleSize(size)
1407 self._frameRatio.GetYaxis().SetTitleSize(size)
1410 self._frame.GetYaxis().SetTitleOffset(offset)
1411 self._frameRatio.GetYaxis().SetTitleOffset(offset)
1414 self._padRatio.RedrawAxis()
1415 self._pad.RedrawAxis()
1417 self._parentPad.cd()
1424 self.
_coverPad = ROOT.TPad(
"coverpad",
"coverpad", xmin, ymin, xmax, ymax)
1425 self._coverPad.SetBorderMode(0)
1426 self._coverPad.Draw()
1432 """Class for creating and managing a frame for a plot from TGraph2D""" 1433 def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor):
1436 self.
_pad = pad.cd(1)
1440 (xlow, ylow, width, height) = (self._pad.GetXlowNDC(), self._pad.GetYlowNDC(), self._pad.GetWNDC(), self._pad.GetHNDC())
1444 bottomMargin = self._pad.GetBottomMargin()
1445 bottomMarginNew = ROOT.gStyle.GetPadBottomMargin()
1447 ylowNew = yup - (1-bottomMargin)/(1-bottomMarginNew) * (yup-ylow)
1448 topMarginNew = self._pad.GetTopMargin() * (yup-ylow)/(yup-ylowNew)
1450 self._pad.SetPad(xlow, ylowNew, xup, yup)
1451 self._pad.SetTopMargin(topMarginNew)
1452 self._pad.SetBottomMargin(bottomMarginNew)
1472 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1476 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1504 self._firstHisto.GetZaxis().SetTitle(title)
1507 self._firstHisto.GetZaxis().SetTitleOffset(offset)
1512 self._pad.SetPhi(epsilon)
1513 self._pad.SetTheta(90+epsilon)
1515 self._firstHisto.GetXaxis().SetTitleOffset(self.
_xtitleoffset)
1516 self._firstHisto.GetYaxis().SetTitleOffset(self.
_ytitleoffset)
1518 if hasattr(self,
"_xtitle"):
1519 self._firstHisto.GetXaxis().SetTitle(self.
_xtitle)
1520 if hasattr(self,
"_xtitlesize"):
1521 self._firstHisto.GetXaxis().SetTitleSize(self.
_xtitlesize)
1522 if hasattr(self,
"_xlabelsize"):
1523 self._firstHisto.GetXaxis().SetLabelSize(self._labelsize)
1524 if hasattr(self,
"_ytitle"):
1525 self._firstHisto.GetYaxis().SetTitle(self.
_ytitle)
1526 if hasattr(self,
"_ytitlesize"):
1527 self._firstHisto.GetYaxis().SetTitleSize(self.
_ytitlesize)
1528 if hasattr(self,
"_ytitleoffset"):
1529 self._firstHisto.GetYaxis().SetTitleOffset(self.
_ytitleoffset)
1532 """Abstraction on top of TLatex""" 1533 def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None):
1537 x -- X coordinate of the text (in NDC) 1538 y -- Y coordinate of the text (in NDC) 1539 text -- String to draw 1540 size -- Size of text (None for the default value, taken from gStyle) 1541 bold -- Should the text be bold? 1542 align -- Alignment of text (left, center, right) 1543 color -- Color of the text 1544 font -- Specify font explicitly 1553 self._l.SetTextFont(self._l.GetTextFont()-20)
1554 if font
is not None:
1555 self._l.SetTextFont(font)
1556 if size
is not None:
1557 self._l.SetTextSize(size)
1558 if isinstance(align, str):
1559 if align.lower() ==
"left":
1560 self._l.SetTextAlign(11)
1561 elif align.lower() ==
"center":
1562 self._l.SetTextAlign(21)
1563 elif align.lower() ==
"right":
1564 self._l.SetTextAlign(31)
1566 raise Exception(
"Error: Invalid option '%s' for text alignment! Options are: 'left', 'center', 'right'."%align)
1568 self._l.SetTextAlign(align)
1569 self._l.SetTextColor(color)
1572 """Draw the text to the current TPad. 1575 options -- For interface compatibility, ignored 1577 Provides interface compatible with ROOT's drawable objects. 1579 self._l.DrawLatex(self.
_x, self.
_y, self.
_text)
1583 """Class for drawing text and a background box.""" 1584 def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, **kwargs):
1588 xmin -- X min coordinate of the box (NDC) 1589 ymin -- Y min coordinate of the box (NDC) (if None, deduced automatically) 1590 xmax -- X max coordinate of the box (NDC) 1591 ymax -- Y max coordinate of the box (NDC) 1592 lineheight -- Line height 1593 fillColor -- Fill color of the box 1594 transparent -- Should the box be transparent? (in practive the TPave is not created) 1596 Keyword arguments are forwarded to constructor of PlotText 1608 self._textArgs.update(kwargs)
1613 """Add text to current position""" 1620 def move(self, dx=0, dy=0, dw=0, dh=0):
1621 """Move the box and the contained text objects 1624 dx -- Movement in x (positive is to right) 1625 dy -- Movement in y (positive is to up) 1626 dw -- Increment of width (negative to decrease width) 1627 dh -- Increment of height (negative to decrease height) 1629 dx and dy affect to both box and text objects, dw and dh 1630 affect the box only. 1634 if self.
_ymin is not None:
1639 if self.
_ymin is not None:
1647 """Draw the box and the text to the current TPad. 1650 options -- Forwarded to ROOT.TPave.Draw(), and the Draw() of the contained objects 1655 ymin = self.currenty - 0.01
1656 self.
_pave = ROOT.TPave(self.xmin, self.ymin, self.xmax, self.ymax, 0,
"NDC")
1657 self._pave.SetFillColor(self.fillColor)
1658 self._pave.Draw(options)
1664 if hasattr(src,
"GetLineColor")
and hasattr(dst,
"SetLineColor"):
1665 properties.extend([
"LineColor",
"LineStyle",
"LineWidth"])
1666 if hasattr(src,
"GetFillColor")
and hasattr(dst,
"SetFillColor"):
1667 properties.extend([
"FillColor",
"FillStyle"])
1668 if hasattr(src,
"GetMarkerColor")
and hasattr(dst,
"SetMarkerColor"):
1669 properties.extend([
"MarkerColor",
"MarkerSize",
"MarkerStyle"])
1671 for prop
in properties:
1672 getattr(dst,
"Set"+prop)(getattr(src,
"Get"+prop)())
1675 """Denotes an empty place in a group.""" 1695 """Represents one plot, comparing one or more histograms.""" 1700 name -- String for name of the plot, or Efficiency object 1703 fallback -- Dictionary for specifying fallback (default None) 1704 outname -- String for an output name of the plot (default None for the same as 'name') 1705 title -- String for a title of the plot (default None) 1706 xtitle -- String for x axis title (default None) 1707 xtitlesize -- Float for x axis title size (default None) 1708 xtitleoffset -- Float for x axis title offset (default None) 1709 xlabelsize -- Float for x axis label size (default None) 1710 ytitle -- String for y axis title (default None) 1711 ytitlesize -- Float for y axis title size (default None) 1712 ytitleoffset -- Float for y axis title offset (default None) 1713 ztitle -- String for z axis title (default None) 1714 ztitleoffset -- Float for z axis title offset (default None) 1715 xmin -- Float for x axis minimum (default None, i.e. automatic) 1716 xmax -- Float for x axis maximum (default None, i.e. automatic) 1717 ymin -- Float for y axis minimum (default 0) 1718 ymax -- Float for y axis maximum (default None, i.e. automatic) 1719 xlog -- Bool for x axis log status (default False) 1720 ylog -- Bool for y axis log status (default False) 1721 xgrid -- Bool for x axis grid status (default True) 1722 ygrid -- Bool for y axis grid status (default True) 1723 stat -- Draw stat box? (default False) 1724 fit -- Do gaussian fit? (default False) 1725 statx -- Stat box x coordinate (default 0.65) 1726 staty -- Stat box y coordinate (default 0.8) 1727 statyadjust -- List of floats for stat box y coordinate adjustments (default None) 1728 normalizeToUnitArea -- Normalize histograms to unit area? (default False) 1729 normalizeToNumberOfEvents -- Normalize histograms to number of events? If yes, the PlotFolder needs 'numberOfEventsHistogram' set to a histogram filled once per event (default False) 1730 profileX -- Take histograms via ProfileX()? (default False) 1731 fitSlicesY -- Take histograms via FitSlicesY() (default False) 1732 rebinX -- rebin x axis (default None) 1733 scale -- Scale histograms by a number (default None) 1734 xbinlabels -- List of x axis bin labels (if given, default None) 1735 xbinlabelsize -- Size of x axis bin labels (default None) 1736 xbinlabeloption -- Option string for x axis bin labels (default None) 1737 removeEmptyBins -- Bool for removing empty bins, but only if histogram has bin labels (default False) 1738 printBins -- Bool for printing bin values, but only if histogram has bin labels (default False) 1739 drawStyle -- If "hist", draw as line instead of points (default None) 1740 drawCommand -- Deliver this to Draw() (default: None for same as drawStyle) 1741 lineWidth -- If drawStyle=="hist", the width of line (default 2) 1742 legendDx -- Float for moving TLegend in x direction for separate=True (default None) 1743 legendDy -- Float for moving TLegend in y direction for separate=True (default None) 1744 legendDw -- Float for changing TLegend width for separate=True (default None) 1745 legendDh -- Float for changing TLegend height for separate=True (default None) 1746 legend -- Bool to enable/disable legend (default True) 1747 adjustMarginLeft -- Float for adjusting left margin (default None) 1748 adjustMarginRight -- Float for adjusting right margin (default None) 1749 ratio -- Possibility to disable ratio for this particular plot (default None) 1750 ratioYmin -- Float for y axis minimum in ratio pad (default: list of values) 1751 ratioYmax -- Float for y axis maximum in ratio pad (default: list of values) 1752 ratioFit -- Fit straight line in ratio? (default None) 1753 ratioUncertainty -- Plot uncertainties on ratio? (default True) 1754 ratioCoverageXrange -- Range of x axis values (xmin,xmax) to limit the automatic ratio y axis range calculation to (default None for disabled) 1755 histogramModifier -- Function to be called in create() to modify the histograms (default None) 1759 def _set(attr, default):
1760 setattr(self,
"_"+attr, kwargs.get(attr, default))
1762 _set(
"fallback",
None)
1763 _set(
"outname",
None)
1766 _set(
"xtitle",
None)
1767 _set(
"xtitlesize",
None)
1768 _set(
"xtitleoffset",
None)
1769 _set(
"xlabelsize",
None)
1770 _set(
"ytitle",
None)
1771 _set(
"ytitlesize",
None)
1772 _set(
"ytitleoffset",
None)
1773 _set(
"ztitle",
None)
1774 _set(
"ztitleoffset",
None)
1791 _set(
"statyadjust",
None)
1793 _set(
"normalizeToUnitArea",
False)
1794 _set(
"normalizeToNumberOfEvents",
False)
1795 _set(
"profileX",
False)
1796 _set(
"fitSlicesY",
False)
1797 _set(
"rebinX",
None)
1800 _set(
"xbinlabels",
None)
1801 _set(
"xbinlabelsize",
None)
1802 _set(
"xbinlabeloption",
None)
1803 _set(
"removeEmptyBins",
False)
1804 _set(
"printBins",
False)
1806 _set(
"drawStyle",
"EP")
1807 _set(
"drawCommand",
None)
1808 _set(
"lineWidth", 2)
1810 _set(
"legendDx",
None)
1811 _set(
"legendDy",
None)
1812 _set(
"legendDw",
None)
1813 _set(
"legendDh",
None)
1814 _set(
"legend",
True)
1816 _set(
"adjustMarginLeft",
None)
1817 _set(
"adjustMarginRight",
None)
1820 _set(
"ratioYmin", [0, 0.2, 0.5, 0.7, 0.8, 0.9, 0.95])
1821 _set(
"ratioYmax", [1.05, 1.1, 1.2, 1.3, 1.5, 1.8, 2, 2.5, 3, 4, 5])
1822 _set(
"ratioFit",
None)
1823 _set(
"ratioUncertainty",
True)
1824 _set(
"ratioCoverageXrange",
None)
1826 _set(
"histogramModifier",
None)
1831 for name, value
in six.iteritems(kwargs):
1832 if not hasattr(self,
"_"+name):
1833 raise Exception(
"No attribute '%s'" % name)
1834 setattr(self,
"_"+name, value)
1838 raise Exception(
"Plot can be cloned only before histograms have been created")
1839 cl = copy.copy(self)
1840 cl.setProperties(**kwargs)
1844 """Return number of existing histograms.""" 1845 return len([h
for h
in self.
_histograms if h
is not None])
1848 """Return true if there are no histograms created for the plot""" 1853 if isinstance(h, ROOT.TGraph2D):
1858 if self._ratio
is None:
1860 return ratio
and self._ratio
1863 if self._outname
is not None:
1864 return self._outname
1865 if isinstance(self.
_name, list):
1871 """Return true if the ratio uncertainty should be drawn""" 1872 return self._ratioUncertainty
1875 """Create one histogram from a TDirectory.""" 1880 if isinstance(name, list):
1884 if h
is not None and self._normalizeToNumberOfEvents
and nevents
is not None and nevents != 0:
1885 h.Scale(1.0/nevents)
1888 def create(self, tdirNEvents, requireAllHistograms=False):
1889 """Create histograms from list of TDirectories""" 1890 self.
_histograms = [self.
_createOne(self.
_name, i, tdirNEvent[0], tdirNEvent[1])
for i, tdirNEvent
in enumerate(tdirNEvents)]
1892 if self._fallback
is not None:
1896 self.
_histograms[i] = self.
_createOne(self._fallback[
"name"], i, tdirNEvents[i][0], tdirNEvents[i][1])
1897 profileX[i] = self._fallback.get(
"profileX", self._profileX)
1899 if self._histogramModifier
is not None:
1903 raise Exception(
"More histograms (%d) than there are plot styles (%d) defined. Please define more plot styles in this file" % (len(self.
_histograms), len(_plotStylesColor)))
1908 def _modifyHisto(th1, profileX):
1913 th1 = th1.ProfileX()
1915 if self._fitSlicesY:
1916 ROOT.TH1.AddDirectory(
True)
1918 th1 = ROOT.gDirectory.Get(th1.GetName()+
"_2")
1919 th1.SetDirectory(
None)
1921 ROOT.TH1.AddDirectory(
False)
1923 if self._title
is not None:
1924 th1.SetTitle(self._title)
1926 if self._scale
is not None:
1927 th1.Scale(self._scale)
1931 if self._fallback
is not None:
1935 if requireAllHistograms
and None in self.
_histograms:
1939 """Set stats box.""" 1942 if h
is not None and hasattr(h,
"SetStats"):
1946 def _doStats(h, col, dy):
1951 if self._fit
and h.GetEntries() > 0.5:
1953 f = h.GetListOfFunctions().FindObject(
"gaus")
1961 st = h.GetListOfFunctions().FindObject(
"stats")
1965 st.SetX1NDC(startingX)
1966 st.SetX2NDC(startingX+0.3)
1967 st.SetY1NDC(startingY+dy)
1968 st.SetY2NDC(startingY+dy+0.15)
1969 st.SetTextColor(col)
1972 for i, h
in enumerate(histos):
1973 if self._statyadjust
is not None and i < len(self._statyadjust):
1974 dy += self._statyadjust[i]
1976 _doStats(h, _plotStylesColor[i], dy)
1980 """Normalise histograms to unit area""" 1988 if h.GetSumw2().fN <= 0:
1992 def draw(self, pad, ratio, ratioFactor, nrows):
1993 """Draw the histograms using values for a given algorithm.""" 2004 if self._normalizeToUnitArea:
2007 if self._rebinX
is not None:
2009 h.Rebin(self._rebinX)
2011 def _styleMarker(h, msty, col):
2012 h.SetMarkerStyle(msty)
2013 h.SetMarkerColor(col)
2014 h.SetMarkerSize(0.7)
2018 def _styleHist(h, msty, col):
2019 _styleMarker(h, msty, col)
2021 h.SetLineWidth(self._lineWidth)
2024 style = _styleMarker
2025 if "hist" in self._drawStyle.lower():
2028 if "l" in self._drawStyle.lower():
2036 style(h, _plotStylesMarker[i], _plotStylesColor[i])
2038 if len(histos) == 0:
2045 histosHaveBinLabels = len(histos[0].GetXaxis().GetBinLabel(1)) > 0
2046 xbinlabels = self._xbinlabels
2048 if xbinlabels
is None:
2049 if histosHaveBinLabels:
2051 if isinstance(histos[0], ROOT.TH2):
2060 if self._removeEmptyBins
and histosHaveBinLabels:
2063 if isinstance(histos[0], ROOT.TH2):
2068 if len(histos) == 0:
2070 print(
"No histograms with non-empty bins for plot {name}".
format(name=self.
getName()))
2073 if self._printBins
and histosHaveBinLabels:
2074 print(
"####################")
2076 width =
max([len(l)
for l
in xbinlabels])
2077 tmp =
"%%-%ds " % width
2078 for b
in range(1, histos[0].GetNbinsX()+1):
2079 s = tmp % xbinlabels[b-1]
2081 s +=
"%.3f " % h.GetBinContent(b)
2086 xmin=self._xmin, xmax=self._xmax,
2087 ymin=self._ymin, ymax=self._ymax)
2089 if isinstance(histos[0], ROOT.TH2):
2090 zmax =
max([h.GetMaximum()
for h
in histos])
2098 ratioHistos = [h
for h
in [r.getRatio()
for r
in self.
_ratios[1:]]
if h
is not None]
2100 if len(ratioHistos) > 0:
2101 ratioBoundsY =
_findBoundsY(ratioHistos, ylog=
False, ymin=self._ratioYmin, ymax=self._ratioYmax, coverage=0.68, coverageRange=self._ratioCoverageXrange)
2103 ratioBoundsY = (0.9, 1,1)
2105 if self._ratioFit
is not None:
2106 for i, rh
in enumerate(ratioHistos):
2107 tf_line = ROOT.TF1(
"line%d"%i,
"[0]+x*[1]")
2108 tf_line.SetRange(self._ratioFit[
"rangemin"], self._ratioFit[
"rangemax"])
2109 fitres = rh.Fit(tf_line,
"RINSQ")
2110 tf_line.SetLineColor(rh.GetMarkerColor())
2111 tf_line.SetLineWidth(2)
2112 self._ratioAdditional.append(tf_line)
2113 box =
PlotTextBox(xmin=self._ratioFit.get(
"boxXmin", 0.14), ymin=
None,
2114 xmax=self._ratioFit.get(
"boxXmax", 0.35), ymax=self._ratioFit.get(
"boxYmax", 0.09),
2115 color=rh.GetMarkerColor(), font=43, size=11, lineheight=0.02)
2116 box.move(dx=(box.width()+0.01)*i)
2119 box.addText(
"Const: %.4f#pm%.4f" % (fitres.Parameter(0), fitres.ParError(0)))
2120 box.addText(
"Slope: %.4f#pm%.4f" % (fitres.Parameter(1), fitres.ParError(1)))
2121 self._mainAdditional.append(box)
2131 self.
_setStats(histos, self._statx, self._staty)
2135 frame =
FrameTGraph2D(pad, bounds, histos, ratioOrig, ratioFactor)
2138 ratioBounds = (bounds[0], ratioBoundsY[0], bounds[2], ratioBoundsY[1])
2139 frame =
FrameRatio(pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
2141 frame =
Frame(pad, bounds, zmax, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption, ybinlabels=ybinlabels)
2144 frame.setLogx(self._xlog)
2145 frame.setLogy(self._ylog)
2146 frame.setGridx(self._xgrid)
2147 frame.setGridy(self._ygrid)
2152 if self._drawStyle
is not None:
2153 ds = self._drawStyle
2154 if self._drawCommand
is not None:
2155 ds = self._drawCommand
2160 frame.setTitle(histos[0].GetTitle())
2162 frame.setXTitle( histos[0].GetXaxis().GetTitle() )
2163 elif self.
_xtitle is not None:
2165 if self._xtitlesize
is not None:
2166 frame.setXTitleSize(self._xtitlesize)
2167 if self._xtitleoffset
is not None:
2168 frame.setXTitleOffset(self._xtitleoffset)
2169 if self._xlabelsize
is not None:
2170 frame.setXLabelSize(self._xlabelsize)
2172 frame.setYTitle( histos[0].GetYaxis().GetTitle() )
2173 elif self.
_ytitle is not None:
2175 if self._ytitlesize
is not None:
2176 frame.setYTitleSize(self._ytitlesize)
2177 if self._ytitleoffset
is not None:
2178 frame.setYTitleOffset(self._ytitleoffset)
2179 if self._ztitle
is not None:
2180 frame.setZTitle(self._ztitle)
2181 if self._ztitleoffset
is not None:
2182 frame.setZTitleOffset(self._ztitleoffset)
2183 if self._adjustMarginLeft
is not None:
2184 frame.adjustMarginLeft(self._adjustMarginLeft)
2185 if self._adjustMarginRight
is not None:
2186 frame.adjustMarginRight(self._adjustMarginRight)
2188 frame.adjustMarginLeft(0.03)
2189 frame.adjustMarginRight(0.08)
2195 for i, h
in enumerate(histos):
2197 if isTGraph2D
and i == 0:
2198 o = o.replace(
"sames",
"")
2205 if ratio
and len(histos) > 0:
2206 frame._padRatio.cd()
2207 firstRatio = self.
_ratios[0].getRatio()
2208 if self._ratioUncertainty
and firstRatio
is not None:
2209 firstRatio.SetFillStyle(1001)
2210 firstRatio.SetFillColor(ROOT.kGray)
2211 firstRatio.SetLineColor(ROOT.kGray)
2212 firstRatio.SetMarkerColor(ROOT.kGray)
2213 firstRatio.SetMarkerSize(0)
2215 frame._padRatio.RedrawAxis(
"G")
2226 """Add histograms to a legend. 2230 legendLabels -- List of strings for the legend labels 2232 first = denomUncertainty
2239 self._forLegend.SetFillStyle(1001)
2240 self._forLegend.SetFillColor(ROOT.kGray)
2241 entry = legend.AddEntry(self.
_forLegend, label,
"lpf")
2244 legend.AddEntry(h, label,
"LP")
2247 """Group of plots, results a TCanvas""" 2252 name -- String for name of the TCanvas, used also as the basename of the picture files 2253 plots -- List of Plot objects 2256 ncols -- Number of columns (default 2) 2257 legendDx -- Float for moving TLegend in x direction (default None) 2258 legendDy -- Float for moving TLegend in y direction (default None) 2259 legendDw -- Float for changing TLegend width (default None) 2260 legendDh -- Float for changing TLegend height (default None) 2261 legend -- Bool for disabling legend (default True for legend being enabled) 2262 overrideLegendLabels -- List of strings for legend labels, if given, these are used instead of the ones coming from Plotter (default None) 2263 onlyForPileup -- Plots this group only for pileup samples 2270 def _set(attr, default):
2271 setattr(self,
"_"+attr, kwargs.get(attr, default))
2275 _set(
"legendDx",
None)
2276 _set(
"legendDy",
None)
2277 _set(
"legendDw",
None)
2278 _set(
"legendDh",
None)
2279 _set(
"legend",
True)
2281 _set(
"overrideLegendLabels",
None)
2283 _set(
"onlyForPileup",
False)
2288 for name, value
in six.iteritems(kwargs):
2289 if not hasattr(self,
"_"+name):
2290 raise Exception(
"No attribute '%s'" % name)
2291 setattr(self,
"_"+name, value)
2300 for i, plot
in enumerate(self.
_plots):
2301 if plot.getName() == name:
2304 raise Exception(
"Did not find Plot '%s' from PlotGroup '%s'" % (name, self.
_name))
2310 self._plots.append(plot)
2314 if plot.getName() == name:
2316 raise Exception(
"No Plot named '%s'" % name)
2319 """Return True if the PlotGroup is intended only for pileup samples""" 2320 return self._onlyForPileup
2322 def create(self, tdirectoryNEvents, requireAllHistograms=False):
2323 """Create histograms from a list of TDirectories. 2326 tdirectoryNEvents -- List of (TDirectory, nevents) pairs 2327 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False) 2330 plot.create(tdirectoryNEvents, requireAllHistograms)
2332 def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2333 """Draw the histograms using values for a given algorithm. 2336 legendLabels -- List of strings for legend labels (corresponding to the tdirectories in create()) 2337 prefix -- Optional string for file name prefix (default None) 2338 separate -- Save the plots of a group to separate files instead of a file per group (default False) 2339 saveFormat -- String specifying the plot format (default '.pdf') 2340 ratio -- Add ratio to the plot (default True) 2341 directory -- Directory where to save the file (default "") 2344 if self._overrideLegendLabels
is not None:
2345 legendLabels = self._overrideLegendLabels
2348 onlyEmptyPlots =
True 2350 if not plot.isEmpty():
2351 onlyEmptyPlots =
False 2357 return self.
_drawSeparate(legendLabels, prefix, saveFormat, ratio, directory)
2359 cwidth = 500*self._ncols
2360 nrows =
int((len(self.
_plots)+self._ncols-1)/self._ncols)
2361 cheight = 500 * nrows
2368 canvas.Divide(self._ncols, nrows)
2370 for i, plot
in enumerate(self.
_plots):
2371 pad = canvas.cd(i+1)
2375 for i, plot
in enumerate(self.
_plots):
2376 pad = canvas.cd(i+1)
2377 if not plot.isEmpty():
2382 if len(self.
_plots) <= 4:
2392 if self._legendDx
is not None:
2393 lx1 += self._legendDx
2394 lx2 += self._legendDx
2395 if self._legendDy
is not None:
2396 ly1 += self._legendDy
2397 ly2 += self._legendDy
2398 if self._legendDw
is not None:
2399 lx2 += self._legendDw
2400 if self._legendDh
is not None:
2401 ly1 -= self._legendDh
2402 plot =
max(self.
_plots, key=
lambda p: p.getNumberOfHistograms())
2403 denomUnc = sum([p.drawRatioUncertainty()
for p
in self.
_plots]) > 0
2404 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2,
2405 denomUncertainty=(ratio
and denomUnc))
2407 return self.
_save(canvas, saveFormat, prefix=prefix, directory=directory)
2410 """Internal method to do the drawing to separate files per Plot instead of a file per PlotGroup""" 2418 for c
in [canvas, canvasRatio]:
2419 c.SetTopMargin(0.05)
2420 c.SetBottomMargin(0.13)
2421 c.SetLeftMargin(0.16)
2422 c.SetRightMargin(0.05)
2435 ratioForThisPlot = plot.isRatio(ratio)
2437 if ratioForThisPlot:
2453 if plot._legendDx
is not None:
2454 lx1 += plot._legendDx
2455 lx2 += plot._legendDx
2456 if plot._legendDy
is not None:
2457 ly1 += plot._legendDy
2458 ly2 += plot._legendDy
2459 if plot._legendDw
is not None:
2460 lx2 += plot._legendDw
2461 if plot._legendDh
is not None:
2462 ly1 -= plot._legendDh
2465 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.03,
2466 denomUncertainty=(ratioForThisPlot
and plot.drawRatioUncertainty))
2468 ret.extend(self.
_save(c, saveFormat, prefix=prefix, postfix=
"_"+plot.getName(), single=
True, directory=directory))
2472 """Internal method to set divide a pad to two for ratio plots""" 2475 def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True):
2476 if not self._legend:
2479 l = ROOT.TLegend(lx1, ly1, lx2, ly2)
2480 l.SetTextSize(textSize)
2487 plot.addToLegend(l, legendLabels, denomUncertainty)
2491 def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False, directory=""):
2494 if prefix
is not None:
2496 if postfix
is not None:
2498 name = os.path.join(directory, name)
2501 backup = ROOT.gErrorIgnoreLevel
2502 ROOT.gErrorIgnoreLevel = ROOT.kWarning
2503 canvas.SaveAs(name+saveFormat)
2505 ROOT.gErrorIgnoreLevel = backup
2509 canvas.SetLogx(
False)
2510 canvas.SetLogy(
False)
2514 return [name+saveFormat]
2517 """Resembles DQM GUI's "On side" layout. 2519 Like PlotGroup, but has only a description of a single plot. The 2520 plot is drawn separately for each file. Useful for 2D histograms.""" 2522 def __init__(self, name, plot, ncols=2, onlyForPileup=False):
2523 super(PlotOnSideGroup, self).
__init__(name, [], ncols=ncols, legend=
False, onlyForPileup=onlyForPileup)
2525 self._plot.setProperties(ratio=
False)
2528 raise Exception(
"PlotOnSideGroup.append() is not implemented")
2530 def create(self, tdirectoryNEvents, requireAllHistograms=False):
2532 for element
in tdirectoryNEvents:
2533 pl = self._plot.clone()
2534 pl.create([element], requireAllHistograms)
2535 self._plots.append(pl)
2538 kargs = copy.copy(kwargs)
2539 kargs[
"ratio"] =
False 2540 kargs[
"separate"] =
False 2541 return super(PlotOnSideGroup, self).
draw(*args, **kargs)
2545 """Represents a collection of PlotGroups, produced from a single folder in a DQM file""" 2550 plotGroups -- List of PlotGroup objects 2553 loopSubFolders -- Should the subfolders be looped over? (default: True) 2554 onlyForPileup -- Plots this folder only for pileup samples 2555 onlyForElectron -- Plots this folder only for electron samples 2556 onlyForConversion -- Plots this folder only for conversion samples 2557 onlyForBHadron -- Plots this folder only for B-hadron samples 2558 purpose -- html.PlotPurpose member class for the purpose of the folder, used for grouping of the plots to the HTML pages 2559 page -- Optional string for the page in HTML generatin 2560 section -- Optional string for the section within a page in HTML generation 2561 numberOfEventsHistogram -- Optional path to histogram filled once per event. Needed if there are any plots normalized by number of events. Path is relative to "possibleDqmFolders". 2574 raise Exception(
"Got unexpected keyword arguments: "+
",".
join(kwargs.keys()))
2577 """Return True if the PlotGroups of this folder should be applied to the all subfolders""" 2581 """Return True if the folder is intended only for pileup samples""" 2606 self._plotGroups.append(plotGroup)
2616 if pg.getName() == name:
2618 raise Exception(
"No PlotGroup named '%s'" % name)
2620 def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False):
2621 """Create histograms from a list of TFiles. 2624 dirsNEvents -- List of (TDirectory, nevents) pairs 2625 labels -- List of strings for legend labels corresponding the files 2626 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup) 2627 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False) 2630 if len(dirsNEvents) != len(labels):
2631 raise Exception(
"len(dirsNEvents) should be len(labels), now they are %d and %d" % (len(dirsNEvents), len(labels)))
2636 if pg.onlyForPileup()
and not isPileupSample:
2638 pg.create(dirsNEvents, requireAllHistograms)
2640 def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2641 """Draw and save all plots using settings of a given algorithm. 2644 prefix -- Optional string for file name prefix (default None) 2645 separate -- Save the plots of a group to separate files instead of a file per group (default False) 2646 saveFormat -- String specifying the plot format (default '.pdf') 2647 ratio -- Add ratio to the plot (default True) 2648 directory -- Directory where to save the file (default "") 2653 ret.extend(pg.draw(self.
_labels, prefix=prefix, separate=separate, saveFormat=saveFormat, ratio=ratio, directory=directory))
2659 """Method called to (possibly) translate a subfolder name to more 'readable' form 2661 The implementation in this (base) class just returns the 2662 argument. The idea is that a deriving class might want to do 2663 something more complex (like trackingPlots.TrackingPlotFolder 2666 return dqmSubFolderName
2669 """Iterate over possible selections name (used in output directory name and legend) from the name of PlotterFolder, and a return value of translateSubFolder""" 2671 if plotFolderName !=
"":
2672 ret +=
"_"+plotFolderName
2673 if translatedDqmSubFolder
is not None:
2674 ret +=
"_"+translatedDqmSubFolder
2678 """Return True if this subfolder should be processed 2681 limitOnlyTo -- List/set/similar containing the translatedDqmSubFolder 2682 translatedDqmSubFolder -- Return value of translateSubFolder 2684 return translatedDqmSubFolder
in limitOnlyTo
2687 """Class to hold the original name and a 'translated' name of a subfolder in the DQM ROOT file""" 2693 """Equality is defined by the 'translated' name""" 2697 """Plotter for one DQM folder. 2699 This class is supposed to be instantiated by the Plotter class (or 2700 PlotterItem, to be more specific), and not used directly by the 2703 def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators):
2708 name -- Name of the folder (is used in the output directory naming) 2709 possibleDqmFolders -- List of strings for possible directories of histograms in TFiles 2710 dqmSubFolders -- List of lists of strings for list of subfolders per input file, or None if no subfolders 2711 plotFolder -- PlotFolder object 2712 fallbackNames -- List of names for backward compatibility (can be empty). These are used only by validation.Validation (class responsible of the release validation workflow) in case the reference file pointed by 'name' does not exist. 2713 fallbackDqmSubFolders -- List of dicts of (string->string) for mapping the subfolder names found in the first file to another names. Use case is comparing files that have different iteration naming convention. 2714 tableCreators -- List of PlotterTableItem objects for tables to be created from this folder 2720 if dqmSubFolders
is None:
2726 for sf_list
in dqmSubFolders:
2728 sf_translated = self._plotFolder.translateSubFolder(sf)
2729 if sf_translated
is not None and not sf_translated
in subfolders:
2730 subfolders[sf_translated] =
DQMSubFolder(sf, sf_translated)
2733 self._dqmSubFolders.sort(key=
lambda sf: sf.subfolder)
2743 return self._plotFolder.getPurpose()
2746 return self._plotFolder.getPage()
2749 return self._plotFolder.getSection()
2752 return self._plotFolder.onlyForPileup()
2755 return self._plotFolder.onlyForElectron()
2758 return self._plotFolder.onlyForConversion()
2761 return self._plotFolder.onlyForBHadron()
2767 """Get list of subfolders, possibly limiting to some of them. 2770 limitOnlyTo -- Object depending on the PlotFolder type for limiting the set of subfolders to be processed 2776 if limitOnlyTo
is None:
2779 return [s
for s
in self.
_dqmSubFolders if self._plotFolder.limitSubFolder(limitOnlyTo, s.translated)]
2785 """Get a generator for the 'selection name', looping over the name and fallbackNames""" 2787 for selname
in self._plotFolder.iterSelectionName(name, dqmSubFolder.translated
if dqmSubFolder
is not None else None):
2793 def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False):
2794 """Create histograms from a list of TFiles. 2796 files -- List of TFiles 2797 labels -- List of strings for legend labels corresponding the files 2798 dqmSubFolder -- DQMSubFolder object for a subfolder (or None for no subfolder) 2799 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup) 2800 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False) 2803 subfolder = dqmSubFolder.subfolder
if dqmSubFolder
is not None else None 2804 neventsHisto = self._plotFolder.getNumberOfEventsHistogram()
2812 fallback = fallbackFunc(subfolder)
2813 if fallback
is not None:
2817 d = GetDirectoryCode.codesToNone(ret)
2819 if neventsHisto
is not None and tfile
is not None:
2821 if hnev
is not None:
2822 nev = hnev.GetEntries()
2823 dirsNEvents.append( (d, nev) )
2825 self._plotFolder.create(dirsNEvents, labels, isPileupSample, requireAllHistograms)
2828 """Draw and save all plots using settings of a given algorithm.""" 2829 return self._plotFolder.draw(*args, **kwargs)
2833 """Instance of plotter that knows the directory content, holds many folders.""" 2840 if limitSubFoldersOnlyTo
is not None:
2841 limitOnlyTo = limitSubFoldersOnlyTo.get(plotterFolder.getName(),
None)
2843 for dqmSubFolder
in plotterFolder.getDQMSubFolders(limitOnlyTo=limitOnlyTo):
2844 yield plotterFolder, dqmSubFolder
2848 def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[]):
2852 name -- Name of the folder (is used in the output directory naming) 2853 possibleDirs -- List of strings for possible directories of histograms in TFiles 2854 plotFolder -- PlotFolder object 2857 fallbackNames -- Optional list of names for backward compatibility. These are used only by validation.Validation (class responsible of the release validation workflow) in case the reference file pointed by 'name' does not exist. 2858 fallbackDqmSubFolders -- Optional list of functions for (string->string) mapping the subfolder names found in the first file to another names (function should return None for no mapping). Use case is comparing files that have different iteration naming convention. 2874 self._tableCreators.append(tc)
2877 """Read available subfolders from the files 2880 files -- List of strings for paths to files, or list of TFiles 2882 For each file, loop over 'possibleDirs', and read the 2883 subfolders of first one that exists. 2885 Returns a PlotterFolder if at least one file for which one of 2886 'possibleDirs' exists. Otherwise, return None to signal that 2887 there is nothing available for this PlotFolder. 2890 if self._plotFolder.loopSubFolders():
2892 possibleDirFound =
False 2897 isOpenFile = isinstance(fname, ROOT.TFile)
2901 tfile = ROOT.TFile.Open(fname)
2905 possibleDirFound =
True 2906 if subFolders
is not None:
2908 for key
in d.GetListOfKeys():
2909 if isinstance(key.ReadObj(), ROOT.TDirectory):
2910 subf.append(key.GetName())
2911 subFolders.append(subf)
2917 if not possibleDirFound:
2927 def create(self, openFiles, legendLabels, dqmSubFolder):
2928 if isinstance(dqmSubFolder, list):
2929 if len(dqmSubFolder) != len(openFiles):
2930 raise Exception(
"When dqmSubFolder is a list, len(dqmSubFolder) should be len(openFiles), now they are %d and %d" % (len(dqmSubFolder), len(openFiles)))
2932 dqmSubFolder = [dqmSubFolder]*len(openFiles)
2933 dqmSubFolder = [sf.subfolder
if sf
is not None else None for sf
in dqmSubFolder]
2936 for f, sf
in zip(openFiles, dqmSubFolder):
2939 if tdir
is not None:
2940 data = self._tableCreator.create(tdir)
2955 for i
in range(len(tbl)):
2957 tbl[i] = [
None]*colLen
2959 return html.Table(columnHeaders=legendLabels, rowHeaders=self._tableCreator.headers(), table=tbl,
2960 purpose=self._tableCreator.getPurpose(), page=self._tableCreator.getPage(), section=self._tableCreator.getSection(dqmSubFolder[0]))
2963 """Contains PlotFolders, i.e. the information what plots to do, and creates a helper object to actually produce the plots.""" 2967 ROOT.TH1.AddDirectory(
False)
2970 """Append a plot folder to the plotter. 2972 All arguments are forwarded to the constructor of PlotterItem. 2977 for plotterItem
in self.
_plots:
2978 if plotterItem.getName() == attachToFolder:
2981 raise Exception(
"Did not find plot folder '%s' when trying to attach a table creator to it" % attachToFolder)
2984 """Remove all plot folders and tables""" 2988 return [item.getName()
for item
in self.
_plots]
2991 return [item.getPlotFolder()
for item
in self.
_plots]
2995 if item.getName() == name:
2996 return item.getPlotFolder()
2997 raise Exception(
"No PlotFolder named '%s'" % name)
3000 """Returns PlotterInstance object, which knows how exactly to produce the plots for these files"""
def create(self, tdirectory)
def iterSelectionName(self, plotFolderName, translatedDqmSubFolder)
def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None)
def append(self, plotGroup)
def _getYmax(obj, limitToNonZeroContent=False)
def __init__(self, name, plots, kwargs)
def __init__(self, name, nameA, nameB, title="")
def getPossibleDQMFolders(self)
def adjustMarginLeft(self, adjust)
def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None)
def set(self, plotGroups)
def setXTitle(self, title)
def readDirs(self, files)
def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory="")
def setXLabelSize(self, size)
def _drawFrame(pad, bounds, zmax=None, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None, suffix="")
def _getOrCreateObject(tdirectory, nameOrCreator)
def iterFolders(self, limitSubFoldersOnlyTo=None)
def setYTitleSize(self, size)
def setXLabelSize(self, size)
def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None)
def _modifyPadForRatio(self, pad)
def Draw(self, options="")
def __init__(self, name, xhistoName, yhistoName, zaxis=False)
def adjustMarginRight(self, adjust)
def __init__(self, possibleDirs, tableCreator)
def getTableCreators(self)
def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, kwargs)
def _getYmin(obj, limitToNonZeroContent=False)
def create(self, args, kwargs)
def setXTitleSize(self, size)
def create(self, tdirectory)
def adjustMarginLeft(self, adjust)
def setXLabelSize(self, size)
def _getYminIgnoreOutlier(th1)
def append(self, args, kwargs)
def getDQMSubFolders(self, limitOnlyTo=None)
def _getXmax(obj, limitToNonZeroContent=False)
def __init__(self, pad, bounds, zmax, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None)
def drawRatioUncertainty(self)
def setZTitle(self, title)
S & print(S &os, JobReport::InputFile const &f)
def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor)
def setZTitleOffset(self, offset)
def setXTitleSize(self, size)
def __init__(self, pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ratioYTitle=_ratioYTitle)
def getNumberOfHistograms(self)
def create(self, tdirectoryNEvents, requireAllHistograms=False)
bool equal(const T &first, const T &second)
def move(self, dx=0, dy=0, dw=0, dh=0)
def __init__(self, name, kwargs)
void divide(dqm::legacy::MonitorElement *eff, const dqm::legacy::MonitorElement *numerator, const dqm::legacy::MonitorElement *denominator)
Function to fill an efficiency histograms with binomial errors.
def create(self, openFiles, legendLabels, dqmSubFolder)
def _calculateRatios(histos, ratioUncertainty=False)
def appendTable(self, attachToFolder, args, kwargs)
def setYTitleOffset(self, offset)
def setYTitle(self, title)
def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False, directory="")
def setYTitleSize(self, size)
def onlyForElectron(self)
def draw(name, histos, styles=_defaultStyles, legendLabels=[], kwargs)
def _setStats(self, histos, startingX, startingY)
def getSelectionName(self, dqmSubFolder)
def _getDirectory(args, kwargs)
def setYTitle(self, title)
def Draw(self, options=None)
def draw(self, pad, ratio, ratioFactor, nrows)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def setTitle(self, title)
def setXTitle(self, title)
def create(self, tdirectory)
def readDirs(self, files)
def _mergeBinLabelsY(histos)
Abs< T >::type abs(const T &t)
def adjustMarginRight(self, adjust)
def adjustMarginLeft(self, adjust)
def setProperties(self, kwargs)
def addToLegend(self, legend, legendLabels, denomUncertainty)
def _getYmaxWithError(th1)
def setXTitleOffset(self, offset)
def _getDirectoryDetailed(tfile, possibleDirs, subDir=None)
def setXTitleOffset(self, size)
def draw(self, args, kwargs)
def create(self, tdirectory)
def onlyForConversion(self)
def _getYminMaxAroundMedian(obj, coverage, coverageRange=None)
def getPlotFolder(self, name)
def setYTitleOffset(self, offset)
def __init__(self, name, histo, title="")
def onlyForConversion(self)
def limitSubFolder(self, limitOnlyTo, translatedDqmSubFolder)
def setXTitleSize(self, size)
def setYTitleRatio(self, title)
def drawRatioUncertainty(self)
static std::string join(char **cmd)
def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False)
def getNumberOfHistograms(self)
def __init__(self, name, assoc, dup, reco, title="")
def adjustMarginRight(self, adjust)
def __init__(self, subfolder, translated)
def create(self, tdirectory)
def setXTitleOffset(self, offset)
def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False, reorder=None)
def __init__(self, name, mapping, normalizeTo=None)
def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True)
def translateSubFolder(self, dqmSubFolderName)
def _drawSeparate(self, legendLabels, prefix, saveFormat, ratio, directory)
def __init__(self, folders)
def setTitle(self, title)
def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators)
def setXTitle(self, title)
def setYTitleSize(self, size)
def setYTitle(self, title)
def _th1RemoveEmptyBins(histos, xbinlabels)
def _getXmin(obj, limitToNonZeroContent=False)
def onlyForElectron(self)
def append(self, args, kwargs)
def create(self, tdirNEvents, requireAllHistograms=False)
def getPlotGroup(self, name)
def appendTableCreator(self, tc)
def _modifyPadForRatio(pad, ratioFactor)
def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False)
def create(self, tdirectoryNEvents, requireAllHistograms=False)
def _createCanvas(name, width, height)
def setYTitleOffset(self, offset)
auto wrap(F iFunc) -> decltype(iFunc())
def __init__(self, name, plot, ncols=2, onlyForPileup=False)
def getPlotFolderNames(self)
def getNumberOfEventsHistogram(self)
def _mergeBinLabels(labelsAll)
def __init__(self, plotGroups, kwargs)
def draw(self, args, kwargs)
def _th1ToOrderedDict(th1, renameBin=None)
def _th2RemoveEmptyBins(histos, xbinlabels, ybinlabels)
def getSelectionNameIterator(self, dqmSubFolder)
def create(self, tdirectory)
def setProperties(self, kwargs)
def _mergeBinLabelsX(histos)
def _createOne(self, name, index, tdir, nevents)
def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory="")
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 setTitle(self, title)
def _th1IncludeOnlyBins(histos, xbinlabels)
def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[])
def _getObject(tdirectory, name)