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)
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)
and not isinstance(o, ROOT.TH2):
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
if wrap(h)
is not None]
321 if len(wrappers) < 1:
326 ref_bins = [ref.xvalues(b)
for b
in range(ref.begin(), ref.end())]
328 wrappers_bins.append(findBins(w, ref_bins))
330 for i, bin
in enumerate(
range(ref.begin(), ref.end())):
331 (scale, ylow, yhigh) = ref.yvalues(bin)
332 for w, bins
in zip(wrappers, wrappers_bins):
335 w.divide(bins[i], scale)
344 if isinstance(obj, ROOT.TH1):
345 xaxis = obj.GetXaxis()
346 if limitToNonZeroContent:
347 for i
in range(1, obj.GetNbinsX()+1):
348 if obj.GetBinContent(i) != 0:
349 return xaxis.GetBinLowEdge(i)
353 return xaxis.GetBinLowEdge(xaxis.GetFirst())
354 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
355 m =
min([obj.GetX()[i]
for i
in range(0, obj.GetN())])
356 return m*0.9
if m > 0
else m*1.1
360 if isinstance(obj, ROOT.TH1):
361 xaxis = obj.GetXaxis()
362 if limitToNonZeroContent:
363 for i
in range(obj.GetNbinsX(), 0, -1):
364 if obj.GetBinContent(i) != 0:
365 return xaxis.GetBinUpEdge(i)
369 return xaxis.GetBinUpEdge(xaxis.GetLast())
370 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
371 m =
max([obj.GetX()[i]
for i
in range(0, obj.GetN())])
372 return m*1.1
if m > 0
else m*0.9
376 if isinstance(obj, ROOT.TH2):
377 yaxis = obj.GetYaxis()
378 return yaxis.GetBinLowEdge(yaxis.GetFirst())
379 elif isinstance(obj, ROOT.TH1):
380 if limitToNonZeroContent:
381 lst = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if obj.GetBinContent(i) != 0 ]
382 return min(lst)
if len(lst) != 0
else 0
384 return obj.GetMinimum()
385 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
386 m =
min([obj.GetY()[i]
for i
in range(0, obj.GetN())])
387 return m*0.9
if m > 0
else m*1.1
391 if isinstance(obj, ROOT.TH2):
392 yaxis = obj.GetYaxis()
393 return yaxis.GetBinUpEdge(yaxis.GetLast())
394 elif isinstance(obj, ROOT.TH1):
395 if limitToNonZeroContent:
396 lst = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if obj.GetBinContent(i) != 0 ]
397 return max(lst)
if len(lst) != 0
else 0
399 return obj.GetMaximum()
400 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
401 m =
max([obj.GetY()[i]
for i
in range(0, obj.GetN())])
402 return m*1.1
if m > 0
else m*0.9
406 return max([th1.GetBinContent(i)+th1.GetBinError(i)
for i
in range(1, th1.GetNbinsX()+1)])
409 yvals = sorted([n
for n
in [th1.GetBinContent(i)
for i
in range(1, th1.GetNbinsX()+1)]
if n>0])
411 return th1.GetMinimum()
416 ind_min = len(yvals)-1 -
int(len(yvals)*0.95)
417 min_val = yvals[ind_min]
418 for i
in range(0, ind_min):
419 if yvals[i] > 0.1*min_val:
425 inRange =
lambda x:
True
426 inRange2 =
lambda xmin,xmax:
True
428 inRange =
lambda x: coverageRange[0] <= x <= coverageRange[1]
429 inRange2 =
lambda xmin,xmax: coverageRange[0] <= xmin
and xmax <= coverageRange[1]
431 if isinstance(obj, ROOT.TH1):
432 yvals = [obj.GetBinContent(i)
for i
in range(1, obj.GetNbinsX()+1)
if inRange2(obj.GetXaxis().GetBinLowEdge(i), obj.GetXaxis().GetBinUpEdge(i))]
433 yvals = [x
for x
in yvals
if x != 0]
434 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
435 yvals = [obj.GetY()[i]
for i
in range(0, obj.GetN())
if inRange(obj.GetX()[i])]
441 return (yvals[0], yvals[0])
443 return (yvals[0], yvals[1])
446 nvals =
int(len(yvals)*coverage)
449 if len(yvals) % 2 == 0:
451 return ( yvals[half-1], yvals[half] )
453 middle = len(yvals)/2
454 return ( yvals[middle-1], yvals[middle+1] )
455 ind_min = (len(yvals)-nvals)/2
456 ind_max = len(yvals)-1 - ind_min
458 return (yvals[ind_min], yvals[ind_max])
460 def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None):
461 """Find x-y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
465 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
468 xmin -- Minimum x value; if None, take the minimum of TH1s
469 xmax -- Maximum x value; if None, take the maximum of TH1s
470 ymin -- Minimum y value; if None, take the minimum of TH1s
471 ymax -- Maximum y value; if None, take the maximum of TH1s
476 if xmin
is None or xmax
is None or isinstance(xmin, list)
or isinstance(max, list):
480 xmins.append(
_getXmin(th1, limitToNonZeroContent=isinstance(xmin, list)))
481 xmaxs.append(
_getXmax(th1, limitToNonZeroContent=isinstance(xmax, list)))
484 xmins = [h
for h
in xmins
if h
is not None]
485 xmaxs = [h
for h
in xmaxs
if h
is not None]
489 elif isinstance(xmin, list):
493 print(
"Histogram is zero, using the smallest given value for xmin from",
str(xmin))
496 xmins_below = [x
for x
in xmin
if x<=xm]
497 if len(xmins_below) == 0:
501 print(
"Histogram minimum x %f is below all given xmin values %s, using the smallest one" % (xm,
str(xmin)))
503 xmin =
max(xmins_below)
507 elif isinstance(xmax, list):
511 print(
"Histogram is zero, using the smallest given value for xmax from",
str(xmin))
514 xmaxs_above = [x
for x
in xmax
if x>xm]
515 if len(xmaxs_above) == 0:
519 print(
"Histogram maximum x %f is above all given xmax values %s, using the maximum one" % (xm,
str(xmax)))
521 xmax =
min(xmaxs_above)
524 th1.GetXaxis().SetRangeUser(xmin, xmax)
526 return (xmin, ymin, xmax, ymax)
528 def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None):
529 """Find y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
533 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
536 ymin -- Minimum y value; if None, take the minimum of TH1s
537 ymax -- Maximum y value; if None, take the maximum of TH1s
538 coverage -- If set, use only values within the 'coverage' part around the median are used for min/max (useful for ratio)
539 coverageRange -- If coverage and this are set, use only the x axis specified by an (xmin,xmax) pair for the coverage
541 if coverage
is not None or isinstance(th1s[0], ROOT.TH2):
547 y_scale_max =
lambda y: y
548 y_scale_min =
lambda y: y
551 y_scale_max =
lambda y: y*1.5
553 y_scale_max =
lambda y: y*1.05
554 y_scale_min =
lambda y: y*0.9
556 if ymin
is None or ymax
is None or isinstance(ymin, list)
or isinstance(ymax, list):
560 if coverage
is not None:
563 if ylog
and isinstance(ymin, list):
566 _ymin =
_getYmin(th1, limitToNonZeroContent=isinstance(ymin, list))
567 _ymax =
_getYmax(th1, limitToNonZeroContent=isinstance(ymax, list))
575 elif isinstance(ymin, list):
576 ym_unscaled =
min(ymins)
577 ym = y_scale_min(ym_unscaled)
578 ymins_below = [y
for y
in ymin
if y<=ym]
579 if len(ymins_below) == 0:
581 if ym_unscaled < ymin:
583 print(
"Histogram minimum y %f is below all given ymin values %s, using the smallest one" % (ym,
str(ymin)))
585 ymin =
max(ymins_below)
590 ymax = y_scale_max(
max(ymaxs+[ymin]))
591 elif isinstance(ymax, list):
592 ym_unscaled =
max(ymaxs)
593 ym = y_scale_max(ym_unscaled)
594 ymaxs_above = [y
for y
in ymax
if y>ym]
595 if len(ymaxs_above) == 0:
597 if ym_unscaled > ymax:
599 print(
"Histogram maximum y %f is above all given ymax values %s, using the maximum one" % (ym_unscaled,
str(ymax)))
601 ymax =
min(ymaxs_above)
604 th1.GetYaxis().SetRangeUser(ymin, ymax)
610 for b
in range(1, histos[0].GetNbinsX()+1):
613 if h.GetBinContent(b) > 0:
619 if len(binsToRemove) > 0:
622 for i
in range(len(xbinlabels)):
623 if (i+1)
not in binsToRemove:
624 xbinlab_new.append(xbinlabels[i])
625 xbinlabels = xbinlab_new
631 for b
in range(1, h.GetNbinsX()+1):
632 if b
not in binsToRemove:
633 values.append( (h.GetXaxis().GetBinLabel(b), h.GetBinContent(b), h.GetBinError(b)) )
636 h_new = h.Clone(h.GetName()+
"_empty")
637 h_new.SetBins(len(values), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(values))
638 for b, (l, v, e)
in enumerate(values):
639 h_new.GetXaxis().SetBinLabel(b+1, l)
640 h_new.SetBinContent(b+1, v)
641 h_new.SetBinError(b+1, e)
643 histos_new.append(h_new)
646 return (histos, xbinlabels)
649 xbinsToRemove = set()
650 ybinsToRemove = set()
651 for ih, h
in enumerate(histos):
652 for bx
in range(1, h.GetNbinsX()+1):
654 for by
in range(1, h.GetNbinsY()+1):
655 if h.GetBinContent(bx, by) > 0:
659 xbinsToRemove.add(bx)
661 xbinsToRemove.discard(bx)
663 for by
in range(1, h.GetNbinsY()+1):
665 for bx
in range(1, h.GetNbinsX()+1):
666 if h.GetBinContent(bx, by) > 0:
670 ybinsToRemove.add(by)
672 ybinsToRemove.discard(by)
674 if len(xbinsToRemove) > 0
or len(ybinsToRemove) > 0:
677 for b
in range(1, len(xbinlabels)+1):
678 if b
not in xbinsToRemove:
679 xbinlabels_new.append(histos[0].GetXaxis().GetBinLabel(b))
681 xbinlabels = xbinlabels_new
684 for b
in range(1, len(ybinlabels)+1):
685 if b
not in ybinsToRemove:
686 ybinlabels.append(histos[0].GetYaxis().GetBinLabel(b))
688 ybinlabels = xbinlabels_new
691 if len(xbinlabels) == 0
or len(ybinlabels) == 0:
692 return (histos_new, xbinlabels, ybinlabels)
694 h_new = ROOT.TH2F(h.GetName()+
"_empty", h.GetTitle(), len(xbinlabels),0,len(xbinlabels), len(ybinlabels),0,len(ybinlabels))
695 for b, l
in enumerate(xbinlabels):
696 h_new.GetXaxis().SetBinLabel(b+1, l)
697 for b, l
in enumerate(ybinlabels):
698 h_new.GetYaxis().SetBinLabel(b+1, l)
700 for ix, bx
in enumerate(xbins):
701 for iy, by
in enumerate(ybins):
702 h_new.SetBinContent(ix+1, iy+1, h.GetBinContent(bx, by))
703 h_new.SetBinError(ix+1, iy+1, h.GetBinError(bx, by))
704 histos_new.append(h_new)
706 return (histos, xbinlabels, ybinlabels)
709 return _mergeBinLabels([[h.GetXaxis().GetBinLabel(i)
for i
in range(1, h.GetNbinsX()+1)]
for h
in histos])
712 return _mergeBinLabels([[h.GetYaxis().GetBinLabel(i)
for i
in range(1, h.GetNbinsY()+1)]
for h
in histos])
715 labels_merged = labelsAll[0]
716 for labels
in labelsAll[1:]:
717 diff = difflib.unified_diff(labels_merged, labels, n=
max(len(labels_merged), len(labels)))
724 operation.append(item[0])
726 if lab
in labels_merged:
728 ind = labels_merged.index(lab)
729 if operation[ind] ==
"-" and operation[-1] ==
"+":
730 labels_merged.remove(lab)
732 elif operation[ind] ==
"+" and operation[-1] ==
"-":
736 raise Exception(
"This should never happen")
737 labels_merged.append(lab)
740 if len(labels_merged) == 0:
741 labels_merged = labels
748 h_new = h.Clone(h.GetName()+
"_xbinlabels")
749 h_new.SetBins(len(xbinlabels), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(xbinlabels))
750 for i, label
in enumerate(xbinlabels):
751 bin = h.GetXaxis().FindFixBin(label)
753 h_new.SetBinContent(i+1, h.GetBinContent(bin))
754 h_new.SetBinError(i+1, h.GetBinError(bin))
756 h_new.SetBinContent(i+1, 0)
757 h_new.SetBinError(i+1, 0)
758 histos_new.append(h_new)
763 """Class for subtracting two histograms"""
768 name -- String for name of the resulting histogram (A-B)
769 nameA -- String for A histogram
770 nameB -- String for B histogram
773 title -- String for a title of the resulting histogram (default "")
775 Uncertainties are calculated with the assumption that B is a
776 subset of A, and the histograms contain event counts.
784 """String representation, returns the name"""
788 """Create and return the fake+duplicate histogram from a TDirectory"""
792 if not histoA
or not histoB:
795 ret = histoA.Clone(self.
_name)
801 ret.SetCanExtend(
False)
803 for i
in range(0, histoA.GetNbinsX()+2):
804 val = histoA.GetBinContent(i)-histoB.GetBinContent(i)
805 ret.SetBinContent(i, val)
806 ret.SetBinError(i, math.sqrt(val))
811 """Class to transform bin contents in an arbitrary way."""
816 name -- String for name of the resulting histogram
817 histo -- String for a source histogram (needs to be cumulative)
818 func -- Function to operate on the bin content
826 """String representation, returns the name"""
830 """Create and return the transformed histogram from a TDirectory"""
835 ret = histo.Clone(self.
_name)
841 ret.SetCanExtend(
False)
843 for i
in range(0, histo.GetNbinsX()+2):
844 ret.SetBinContent(i, self.
_func(histo.GetBinContent(i)))
848 """Class to calculate the fake+duplicate rate"""
849 def __init__(self, name, assoc, dup, reco, title=""):
853 name -- String for the name of the resulting efficiency histogram
854 assoc -- String for the name of the "associated" histogram
855 dup -- String for the name of the "duplicates" histogram
856 reco -- String for the name of the "reco" (denominator) histogram
859 title -- String for a title of the resulting histogram (default "")
861 The result is calculated as 1 - (assoc - dup) / reco
870 """String representation, returns the name"""
874 """Create and return the fake+duplicate histogram from a TDirectory"""
881 if not hassoc
or not hdup
or not hreco:
884 hfakedup = hreco.Clone(self.
_name)
885 hfakedup.SetTitle(self.
_title)
887 for i
in range(1, hassoc.GetNbinsX()+1):
888 numerVal = hassoc.GetBinContent(i) - hdup.GetBinContent(i)
889 denomVal = hreco.GetBinContent(i)
891 fakedupVal = (1 - numerVal / denomVal)
if denomVal != 0.0
else 0.0
892 errVal = math.sqrt(fakedupVal*(1-fakedupVal)/denomVal)
if (denomVal != 0.0
and fakedupVal <= 1)
else 0.0
894 hfakedup.SetBinContent(i, fakedupVal)
895 hfakedup.SetBinError(i, errVal)
900 """Class for making a cut efficiency histograms.
910 name -- String for name of the resulting histogram
911 histo -- String for a source histogram (needs to be cumulative)
918 """String representation, returns the name"""
922 """Create and return the cut efficiency histogram from a TDirectory"""
928 ascending = histo.GetBinContent(0) < histo.GetBinContent(histo.GetNbinsX())
930 n_tot = histo.GetBinContent(histo.GetNbinsX())
932 n_tot = histo.GetBinContent(0)
937 ret = histo.Clone(self.
_name)
941 for i
in range(1, histo.GetNbinsX()+1):
942 n = histo.GetBinContent(i)
944 errVal = math.sqrt(val*(1-val)/n_tot)
945 ret.SetBinContent(i, val)
946 ret.SetBinError(i, errVal)
950 """Class to create a histogram by aggregating bins of another histogram to a bin of the resulting histogram."""
951 def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False, reorder=None):
955 name -- String for the name of the resulting histogram
956 histoName -- String for the name of the source histogram
957 mapping -- Dictionary for mapping the bins (see below)
960 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.
961 scale -- Optional number for scaling the histogram (passed to ROOT.TH1.Scale())
962 renameBin -- Optional function (string -> string) to rename the bins of the input histogram
963 originalOrder -- Boolean for using the order of bins in the histogram (default False)
964 reorder -- Optional function to reorder the bins
966 Mapping structure (mapping):
968 Dictionary (you probably want to use collections.OrderedDict)
969 should be a mapping from the destination bin label to a list
970 of source bin labels ("dst -> [src]").
983 raise Exception(
"reorder is not None and originalOrder is True, please set only one of them")
986 """String representation, returns the name"""
990 """Create and return the histogram from a TDirectory"""
996 binValues = [
None]*len(self.
_mapping)
1002 for i, (key, labels)
in enumerate(six.iteritems(self.
_mapping)):
1008 sumTime += values[l][0]
1009 sumErrorSq += values[l][1]**2
1015 binValues[i] = (sumTime, math.sqrt(sumErrorSq))
1018 ivalue = len(values)+1
1024 ivalue = values.keys().
index(lab)
1026 binIndexOrder.append( (ivalue, i) )
1029 binIndexOrder.sort(key=
lambda t: t[0])
1032 for i
in range(0, len(binValues)):
1033 fromIndex = binIndexOrder[i][1]
1034 tmpVal.append(binValues[fromIndex])
1035 tmpLab.append(binLabels[fromIndex])
1039 order = self.
_reorder(tdirectory, binLabels)
1040 binValues = [binValues[i]
for i
in order]
1041 binLabels = [binLabels[i]
for i
in order]
1047 for i, val
in enumerate(binValues):
1050 binValues = [v
for v
in binValues
if v
is not None]
1051 binLabels = [v
for v
in binLabels
if v
is not None]
1052 if len(binValues) == 0:
1055 result = ROOT.TH1F(self.
_name, self.
_name, len(binValues), 0, len(binValues))
1056 for i, (value, label)
in enumerate(
zip(binValues, binLabels)):
1057 if value
is not None:
1058 result.SetBinContent(i+1, value[0])
1059 result.SetBinError(i+1, value[1])
1060 result.GetXaxis().SetBinLabel(i+1, label)
1067 value = th1.GetBinContent(bin)
1069 result.Scale(1/value)
1071 if self.
_scale is not None:
1072 result.Scale(self.
_scale)
1077 """Class to create a histogram by aggregaging integrals of another histoggrams."""
1082 name -- String for the name of the resulting histogram
1083 mapping -- Dictionary for mapping the bin label to a histogram name
1086 normalizeTo -- Optional string for a histogram. If given, all bins of the resulting histograqm are divided by the integral of this histogram.
1093 """String representation, returns the name"""
1097 """Create and return the histogram from a TDirectory"""
1099 for key, histoName
in six.iteritems(self.
_mapping):
1103 result.append( (key, th1.Integral(0, th1.GetNbinsX()+1)) )
1104 if len(result) == 0:
1107 res = ROOT.TH1F(self.
_name, self.
_name, len(result), 0, len(result))
1109 for i, (name, count)
in enumerate(result):
1110 res.SetBinContent(i+1, count)
1111 res.GetXaxis().SetBinLabel(i+1, name)
1117 scale = th1.Integral(0, th1.GetNbinsX()+1)
1123 """Class to construct a ROC curve (e.g. efficiency vs. fake rate) from two histograms"""
1124 def __init__(self, name, xhistoName, yhistoName, zaxis=False):
1128 name -- String for the name of the resulting histogram
1129 xhistoName -- String for the name of the x-axis histogram (or another "creator" object)
1130 yhistoName -- String for the name of the y-axis histogram (or another "creator" object)
1133 zaxis -- If set to True (default False), create a TGraph2D with z axis showing the cut value (recommended drawStyle 'pcolz')
1141 """String representation, returns the name"""
1145 """Create and return the histogram from a TDirectory"""
1148 if xhisto
is None or yhisto
is None:
1159 for i
in range(1, xhisto.GetNbinsX()+1):
1160 x.append(xhisto.GetBinContent(i))
1161 xerrup.append(xhisto.GetBinError(i))
1162 xerrdown.append(xhisto.GetBinError(i))
1164 y.append(yhisto.GetBinContent(i))
1165 yerrup.append(yhisto.GetBinError(i))
1166 yerrdown.append(yhisto.GetBinError(i))
1168 z.append(xhisto.GetXaxis().GetBinUpEdge(i))
1171 if x.count(0.0) == len(x)
or y.count(0.0) == len(y):
1174 arr =
lambda v: array.array(
"d", v)
1177 gr = ROOT.TGraph2D(len(x), arr(x), arr(y), arr(z))
1179 gr = ROOT.TGraphAsymmErrors(len(x), arr(x), arr(y), arr(xerrdown), arr(xerrup), arr(yerrdown), arr(yerrup))
1185 _plotStylesColor = [4, 2, ROOT.kBlack, ROOT.kOrange+7, ROOT.kMagenta-3, ROOT.kGreen+2]
1186 _plotStylesMarker = [21, 20, 22, 34, 33, 23]
1188 def _drawFrame(pad, bounds, zmax=None, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None, suffix=""):
1189 """Function to draw a frame
1192 pad -- TPad to where the frame is drawn
1193 bounds -- List or 4-tuple for (xmin, ymin, xmax, ymax)
1196 zmax -- Maximum Z, needed for TH2 histograms
1197 xbinlabels -- Optional list of strings for x axis bin labels
1198 xbinlabelsize -- Optional number for the x axis bin label size
1199 xbinlabeloption -- Optional string for the x axis bin options (passed to ROOT.TH1.LabelsOption())
1200 suffix -- Optional string for a postfix of the frame name
1202 if xbinlabels
is None and ybinlabels
is None:
1203 frame = pad.DrawFrame(*bounds)
1206 nbins = len(xbinlabels)
1207 if ybinlabels
is None:
1208 frame = ROOT.TH1F(
"hframe"+suffix,
"", nbins, bounds[0], bounds[2])
1209 frame.SetMinimum(bounds[1])
1210 frame.SetMaximum(bounds[3])
1211 frame.GetYaxis().SetLimits(bounds[1], bounds[3])
1213 ybins = len(ybinlabels)
1214 frame = ROOT.TH2F(
"hframe"+suffix,
"", nbins,bounds[0],bounds[2], ybins,bounds[1],bounds[3])
1215 frame.SetMaximum(zmax)
1217 frame.SetBit(ROOT.TH1.kNoStats)
1218 frame.SetBit(ROOT.kCanDelete)
1221 xaxis = frame.GetXaxis()
1222 for i
in range(nbins):
1223 xaxis.SetBinLabel(i+1, xbinlabels[i])
1224 if xbinlabelsize
is not None:
1225 xaxis.SetLabelSize(xbinlabelsize)
1226 if xbinlabeloption
is not None:
1227 frame.LabelsOption(xbinlabeloption)
1229 if ybinlabels
is not None:
1230 yaxis = frame.GetYaxis()
1231 for i, lab
in enumerate(ybinlabels):
1232 yaxis.SetBinLabel(i+1, lab)
1233 if xbinlabelsize
is not None:
1234 yaxis.SetLabelSize(xbinlabelsize)
1235 if xbinlabeloption
is not None:
1236 frame.LabelsOption(xbinlabeloption,
"Y")
1241 """Class for creating and managing a frame for a simple, one-pad plot"""
1242 def __init__(self, pad, bounds, zmax, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None):
1244 self.
_frame =
_drawFrame(pad, bounds, zmax, xbinlabels, xbinlabelsize, xbinlabeloption, ybinlabels)
1252 yoffsetFactor *= 1.5
1253 xoffsetFactor *= 1.5
1258 self.
_frame.GetYaxis().SetTitleOffset(self.
_frame.GetYaxis().GetTitleOffset()*yoffsetFactor)
1259 self.
_frame.GetXaxis().SetTitleOffset(self.
_frame.GetXaxis().GetTitleOffset()*xoffsetFactor)
1263 self.
_pad.SetLogx(log)
1266 self.
_pad.SetLogy(log)
1269 self.
_pad.SetGridx(grid)
1272 self.
_pad.SetGridy(grid)
1275 self.
_pad.SetLeftMargin(self.
_pad.GetLeftMargin()+adjust)
1281 self.
_pad.SetRightMargin(self.
_pad.GetRightMargin()+adjust)
1287 self.
_frame.SetTitle(title)
1290 self.
_frame.GetXaxis().SetTitle(title)
1293 self.
_frame.GetXaxis().SetTitleSize(size)
1296 self.
_frame.GetXaxis().SetTitleOffset(offset)
1299 self.
_frame.GetXaxis().SetLabelSize(size)
1302 self.
_frame.GetYaxis().SetTitle(title)
1305 self.
_frame.GetYaxis().SetTitleSize(size)
1308 self.
_frame.GetYaxis().SetTitleOffset(offset)
1311 self.
_pad.RedrawAxis()
1314 """Class for creating and managing a frame for a ratio plot with two subpads"""
1315 def __init__(self, pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ratioYTitle=_ratioYTitle):
1318 if xbinlabels
is not None:
1325 self.
_frame.GetXaxis().SetLabelSize(0)
1326 self.
_frame.GetXaxis().SetTitleSize(0)
1328 yoffsetFactor = ratioFactor
1329 divisionPoint = 1-1/ratioFactor
1330 xoffsetFactor = 1/divisionPoint
1333 xoffsetFactor *= 0.6
1336 xoffsetFactor *= 1.5
1339 xoffsetFactor *= 2.3
1344 self.
_frame.GetYaxis().SetTitleOffset(self.
_frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1349 self.
_frameRatio.GetYaxis().SetNdivisions(4, 5, 0)
1354 self.
_pad.SetLogx(log)
1358 self.
_pad.SetLogy(log)
1361 self.
_pad.SetGridx(grid)
1365 self.
_pad.SetGridy(grid)
1369 self.
_pad.SetLeftMargin(self.
_pad.GetLeftMargin()+adjust)
1378 self.
_pad.SetRightMargin(self.
_pad.GetRightMargin()+adjust)
1387 self.
_frame.SetTitle(title)
1396 self.
_frameRatio.GetXaxis().SetTitleOffset(offset)
1402 self.
_frame.GetYaxis().SetTitle(title)
1408 self.
_frame.GetYaxis().SetTitleSize(size)
1412 self.
_frame.GetYaxis().SetTitleOffset(offset)
1413 self.
_frameRatio.GetYaxis().SetTitleOffset(offset)
1417 self.
_pad.RedrawAxis()
1426 self.
_coverPad = ROOT.TPad(
"coverpad",
"coverpad", xmin, ymin, xmax, ymax)
1434 """Class for creating and managing a frame for a plot from TGraph2D"""
1435 def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor):
1438 self.
_pad = pad.cd(1)
1442 (xlow, ylow, width, height) = (self.
_pad.GetXlowNDC(), self.
_pad.GetYlowNDC(), self.
_pad.GetWNDC(), self.
_pad.GetHNDC())
1446 bottomMargin = self.
_pad.GetBottomMargin()
1447 bottomMarginNew = ROOT.gStyle.GetPadBottomMargin()
1449 ylowNew = yup - (1-bottomMargin)/(1-bottomMarginNew) * (yup-ylow)
1450 topMarginNew = self.
_pad.GetTopMargin() * (yup-ylow)/(yup-ylowNew)
1452 self.
_pad.SetPad(xlow, ylowNew, xup, yup)
1453 self.
_pad.SetTopMargin(topMarginNew)
1454 self.
_pad.SetBottomMargin(bottomMarginNew)
1474 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1478 self.
_pad.SetRightMargin(self.
_pad.GetRightMargin()+adjust)
1509 self.
_firstHisto.GetZaxis().SetTitleOffset(offset)
1514 self.
_pad.SetPhi(epsilon)
1515 self.
_pad.SetTheta(90+epsilon)
1520 if hasattr(self,
"_xtitle"):
1522 if hasattr(self,
"_xtitlesize"):
1524 if hasattr(self,
"_xlabelsize"):
1525 self.
_firstHisto.GetXaxis().SetLabelSize(self._labelsize)
1526 if hasattr(self,
"_ytitle"):
1528 if hasattr(self,
"_ytitlesize"):
1530 if hasattr(self,
"_ytitleoffset"):
1534 """Abstraction on top of TLatex"""
1535 def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None):
1539 x -- X coordinate of the text (in NDC)
1540 y -- Y coordinate of the text (in NDC)
1541 text -- String to draw
1542 size -- Size of text (None for the default value, taken from gStyle)
1543 bold -- Should the text be bold?
1544 align -- Alignment of text (left, center, right)
1545 color -- Color of the text
1546 font -- Specify font explicitly
1555 self.
_l.SetTextFont(self.
_l.GetTextFont()-20)
1556 if font
is not None:
1557 self.
_l.SetTextFont(font)
1558 if size
is not None:
1559 self.
_l.SetTextSize(size)
1560 if isinstance(align, str):
1561 if align.lower() ==
"left":
1562 self.
_l.SetTextAlign(11)
1563 elif align.lower() ==
"center":
1564 self.
_l.SetTextAlign(21)
1565 elif align.lower() ==
"right":
1566 self.
_l.SetTextAlign(31)
1568 raise Exception(
"Error: Invalid option '%s' for text alignment! Options are: 'left', 'center', 'right'."%align)
1570 self.
_l.SetTextAlign(align)
1571 self.
_l.SetTextColor(color)
1574 """Draw the text to the current TPad.
1577 options -- For interface compatibility, ignored
1579 Provides interface compatible with ROOT's drawable objects.
1585 """Class for drawing text and a background box."""
1586 def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, **kwargs):
1590 xmin -- X min coordinate of the box (NDC)
1591 ymin -- Y min coordinate of the box (NDC) (if None, deduced automatically)
1592 xmax -- X max coordinate of the box (NDC)
1593 ymax -- Y max coordinate of the box (NDC)
1594 lineheight -- Line height
1595 fillColor -- Fill color of the box
1596 transparent -- Should the box be transparent? (in practive the TPave is not created)
1598 Keyword arguments are forwarded to constructor of PlotText
1615 """Add text to current position"""
1622 def move(self, dx=0, dy=0, dw=0, dh=0):
1623 """Move the box and the contained text objects
1626 dx -- Movement in x (positive is to right)
1627 dy -- Movement in y (positive is to up)
1628 dw -- Increment of width (negative to decrease width)
1629 dh -- Increment of height (negative to decrease height)
1631 dx and dy affect to both box and text objects, dw and dh
1632 affect the box only.
1636 if self.
_ymin is not None:
1641 if self.
_ymin is not None:
1649 """Draw the box and the text to the current TPad.
1652 options -- Forwarded to ROOT.TPave.Draw(), and the Draw() of the contained objects
1657 ymin = self.currenty - 0.01
1658 self.
_pave = ROOT.TPave(self.xmin, self.ymin, self.xmax, self.ymax, 0,
"NDC")
1659 self.
_pave.SetFillColor(self.fillColor)
1666 if hasattr(src,
"GetLineColor")
and hasattr(dst,
"SetLineColor"):
1667 properties.extend([
"LineColor",
"LineStyle",
"LineWidth"])
1668 if hasattr(src,
"GetFillColor")
and hasattr(dst,
"SetFillColor"):
1669 properties.extend([
"FillColor",
"FillStyle"])
1670 if hasattr(src,
"GetMarkerColor")
and hasattr(dst,
"SetMarkerColor"):
1671 properties.extend([
"MarkerColor",
"MarkerSize",
"MarkerStyle"])
1673 for prop
in properties:
1674 getattr(dst,
"Set"+prop)(getattr(src,
"Get"+prop)())
1677 """Denotes an empty place in a group."""
1697 """Represents one plot, comparing one or more histograms."""
1702 name -- String for name of the plot, or Efficiency object
1705 fallback -- Dictionary for specifying fallback (default None)
1706 outname -- String for an output name of the plot (default None for the same as 'name')
1707 title -- String for a title of the plot (default None)
1708 xtitle -- String for x axis title (default None)
1709 xtitlesize -- Float for x axis title size (default None)
1710 xtitleoffset -- Float for x axis title offset (default None)
1711 xlabelsize -- Float for x axis label size (default None)
1712 ytitle -- String for y axis title (default None)
1713 ytitlesize -- Float for y axis title size (default None)
1714 ytitleoffset -- Float for y axis title offset (default None)
1715 ztitle -- String for z axis title (default None)
1716 ztitleoffset -- Float for z axis title offset (default None)
1717 xmin -- Float for x axis minimum (default None, i.e. automatic)
1718 xmax -- Float for x axis maximum (default None, i.e. automatic)
1719 ymin -- Float for y axis minimum (default 0)
1720 ymax -- Float for y axis maximum (default None, i.e. automatic)
1721 xlog -- Bool for x axis log status (default False)
1722 ylog -- Bool for y axis log status (default False)
1723 xgrid -- Bool for x axis grid status (default True)
1724 ygrid -- Bool for y axis grid status (default True)
1725 stat -- Draw stat box? (default False)
1726 fit -- Do gaussian fit? (default False)
1727 statx -- Stat box x coordinate (default 0.65)
1728 staty -- Stat box y coordinate (default 0.8)
1729 statyadjust -- List of floats for stat box y coordinate adjustments (default None)
1730 normalizeToUnitArea -- Normalize histograms to unit area? (default False)
1731 normalizeToNumberOfEvents -- Normalize histograms to number of events? If yes, the PlotFolder needs 'numberOfEventsHistogram' set to a histogram filled once per event (default False)
1732 profileX -- Take histograms via ProfileX()? (default False)
1733 fitSlicesY -- Take histograms via FitSlicesY() (default False)
1734 rebinX -- rebin x axis (default None)
1735 scale -- Scale histograms by a number (default None)
1736 xbinlabels -- List of x axis bin labels (if given, default None)
1737 xbinlabelsize -- Size of x axis bin labels (default None)
1738 xbinlabeloption -- Option string for x axis bin labels (default None)
1739 removeEmptyBins -- Bool for removing empty bins, but only if histogram has bin labels (default False)
1740 printBins -- Bool for printing bin values, but only if histogram has bin labels (default False)
1741 drawStyle -- If "hist", draw as line instead of points (default None)
1742 drawCommand -- Deliver this to Draw() (default: None for same as drawStyle)
1743 lineWidth -- If drawStyle=="hist", the width of line (default 2)
1744 legendDx -- Float for moving TLegend in x direction for separate=True (default None)
1745 legendDy -- Float for moving TLegend in y direction for separate=True (default None)
1746 legendDw -- Float for changing TLegend width for separate=True (default None)
1747 legendDh -- Float for changing TLegend height for separate=True (default None)
1748 legend -- Bool to enable/disable legend (default True)
1749 adjustMarginLeft -- Float for adjusting left margin (default None)
1750 adjustMarginRight -- Float for adjusting right margin (default None)
1751 ratio -- Possibility to disable ratio for this particular plot (default None)
1752 ratioYmin -- Float for y axis minimum in ratio pad (default: list of values)
1753 ratioYmax -- Float for y axis maximum in ratio pad (default: list of values)
1754 ratioFit -- Fit straight line in ratio? (default None)
1755 ratioUncertainty -- Plot uncertainties on ratio? (default True)
1756 ratioCoverageXrange -- Range of x axis values (xmin,xmax) to limit the automatic ratio y axis range calculation to (default None for disabled)
1757 histogramModifier -- Function to be called in create() to modify the histograms (default None)
1761 def _set(attr, default):
1762 setattr(self,
"_"+attr, kwargs.get(attr, default))
1764 _set(
"fallback",
None)
1765 _set(
"outname",
None)
1768 _set(
"xtitle",
None)
1769 _set(
"xtitlesize",
None)
1770 _set(
"xtitleoffset",
None)
1771 _set(
"xlabelsize",
None)
1772 _set(
"ytitle",
None)
1773 _set(
"ytitlesize",
None)
1774 _set(
"ytitleoffset",
None)
1775 _set(
"ztitle",
None)
1776 _set(
"ztitleoffset",
None)
1793 _set(
"statyadjust",
None)
1795 _set(
"normalizeToUnitArea",
False)
1796 _set(
"normalizeToNumberOfEvents",
False)
1797 _set(
"profileX",
False)
1798 _set(
"fitSlicesY",
False)
1799 _set(
"rebinX",
None)
1802 _set(
"xbinlabels",
None)
1803 _set(
"xbinlabelsize",
None)
1804 _set(
"xbinlabeloption",
None)
1805 _set(
"removeEmptyBins",
False)
1806 _set(
"printBins",
False)
1808 _set(
"drawStyle",
"EP")
1809 _set(
"drawCommand",
None)
1810 _set(
"lineWidth", 2)
1812 _set(
"legendDx",
None)
1813 _set(
"legendDy",
None)
1814 _set(
"legendDw",
None)
1815 _set(
"legendDh",
None)
1816 _set(
"legend",
True)
1818 _set(
"adjustMarginLeft",
None)
1819 _set(
"adjustMarginRight",
None)
1822 _set(
"ratioYmin", [0, 0.2, 0.5, 0.7, 0.8, 0.9, 0.95])
1823 _set(
"ratioYmax", [1.05, 1.1, 1.2, 1.3, 1.5, 1.8, 2, 2.5, 3, 4, 5])
1824 _set(
"ratioFit",
None)
1825 _set(
"ratioUncertainty",
True)
1826 _set(
"ratioCoverageXrange",
None)
1828 _set(
"histogramModifier",
None)
1833 for name, value
in six.iteritems(kwargs):
1834 if not hasattr(self,
"_"+name):
1835 raise Exception(
"No attribute '%s'" % name)
1836 setattr(self,
"_"+name, value)
1840 raise Exception(
"Plot can be cloned only before histograms have been created")
1841 cl = copy.copy(self)
1842 cl.setProperties(**kwargs)
1846 """Return number of existing histograms."""
1847 return len([h
for h
in self.
_histograms if h
is not None])
1850 """Return true if there are no histograms created for the plot"""
1855 if isinstance(h, ROOT.TGraph2D):
1860 if self._ratio
is None:
1862 return ratio
and self._ratio
1868 if self._outname
is not None:
1869 return self._outname
1870 if isinstance(self.
_name, list):
1876 """Return true if the ratio uncertainty should be drawn"""
1877 return self._ratioUncertainty
1880 """Create one histogram from a TDirectory."""
1885 if isinstance(name, list):
1889 if h
is not None and self._normalizeToNumberOfEvents
and nevents
is not None and nevents != 0:
1890 h.Scale(1.0/nevents)
1893 def create(self, tdirNEvents, requireAllHistograms=False):
1894 """Create histograms from list of TDirectories"""
1895 self.
_histograms = [self.
_createOne(self.
_name, i, tdirNEvent[0], tdirNEvent[1])
for i, tdirNEvent
in enumerate(tdirNEvents)]
1897 if self._fallback
is not None:
1901 self.
_histograms[i] = self.
_createOne(self._fallback[
"name"], i, tdirNEvents[i][0], tdirNEvents[i][1])
1902 profileX[i] = self._fallback.get(
"profileX", self._profileX)
1904 if self._histogramModifier
is not None:
1908 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)))
1913 def _modifyHisto(th1, profileX):
1918 th1 = th1.ProfileX()
1920 if self._fitSlicesY:
1921 ROOT.TH1.AddDirectory(
True)
1923 th1 = ROOT.gDirectory.Get(th1.GetName()+
"_2")
1924 th1.SetDirectory(
None)
1926 ROOT.TH1.AddDirectory(
False)
1928 if self._title
is not None:
1929 th1.SetTitle(self._title)
1931 if self._scale
is not None:
1932 th1.Scale(self._scale)
1936 if self._fallback
is not None:
1940 if requireAllHistograms
and None in self.
_histograms:
1944 """Set stats box."""
1947 if h
is not None and hasattr(h,
"SetStats"):
1951 def _doStats(h, col, dy):
1956 if self._fit
and h.GetEntries() > 0.5:
1958 f = h.GetListOfFunctions().FindObject(
"gaus")
1966 st = h.GetListOfFunctions().FindObject(
"stats")
1971 st.SetX1NDC(startingX)
1972 st.SetX2NDC(startingX+0.3)
1973 st.SetY1NDC(startingY+dy)
1974 st.SetY2NDC(startingY+dy+0.12)
1975 st.SetTextColor(col)
1978 for i, h
in enumerate(histos):
1979 if self._statyadjust
is not None and i < len(self._statyadjust):
1980 dy += self._statyadjust[i]
1982 _doStats(h, _plotStylesColor[i], dy)
1986 """Normalise histograms to unit area"""
1994 if h.GetSumw2().fN <= 0:
1998 def draw(self, pad, ratio, ratioFactor, nrows):
1999 """Draw the histograms using values for a given algorithm."""
2010 if self._normalizeToUnitArea:
2013 if self._rebinX
is not None:
2015 h.Rebin(self._rebinX)
2017 def _styleMarker(h, msty, col):
2018 h.SetMarkerStyle(msty)
2019 h.SetMarkerColor(col)
2020 h.SetMarkerSize(0.7)
2024 def _styleHist(h, msty, col):
2025 _styleMarker(h, msty, col)
2027 h.SetLineWidth(self._lineWidth)
2030 style = _styleMarker
2031 if "hist" in self._drawStyle.lower():
2034 if "l" in self._drawStyle.lower():
2042 style(h, _plotStylesMarker[i], _plotStylesColor[i])
2044 if len(histos) == 0:
2051 histosHaveBinLabels = len(histos[0].GetXaxis().GetBinLabel(1)) > 0
2052 xbinlabels = self._xbinlabels
2054 if xbinlabels
is None:
2055 if histosHaveBinLabels:
2057 if isinstance(histos[0], ROOT.TH2):
2066 if self._removeEmptyBins
and histosHaveBinLabels:
2069 if isinstance(histos[0], ROOT.TH2):
2074 if len(histos) == 0:
2076 print(
"No histograms with non-empty bins for plot {name}".
format(name=self.
getName()))
2079 if self._printBins
and histosHaveBinLabels:
2080 print(
"####################")
2082 width =
max([len(l)
for l
in xbinlabels])
2083 tmp =
"%%-%ds " % width
2084 for b
in range(1, histos[0].GetNbinsX()+1):
2085 s = tmp % xbinlabels[b-1]
2087 s +=
"%.3f " % h.GetBinContent(b)
2092 xmin=self._xmin, xmax=self._xmax,
2093 ymin=self._ymin, ymax=self._ymax)
2095 if isinstance(histos[0], ROOT.TH2):
2096 zmax =
max([h.GetMaximum()
for h
in histos])
2104 ratioHistos = [h
for h
in [r.getRatio()
for r
in self.
_ratios[1:]]
if h
is not None]
2106 if len(ratioHistos) > 0:
2107 ratioBoundsY =
_findBoundsY(ratioHistos, ylog=
False, ymin=self._ratioYmin, ymax=self._ratioYmax, coverage=0.68, coverageRange=self._ratioCoverageXrange)
2109 ratioBoundsY = (0.9, 1,1)
2111 if self._ratioFit
is not None:
2112 for i, rh
in enumerate(ratioHistos):
2113 tf_line = ROOT.TF1(
"line%d"%i,
"[0]+x*[1]")
2114 tf_line.SetRange(self._ratioFit[
"rangemin"], self._ratioFit[
"rangemax"])
2115 fitres = rh.Fit(tf_line,
"RINSQ")
2116 tf_line.SetLineColor(rh.GetMarkerColor())
2117 tf_line.SetLineWidth(2)
2119 box =
PlotTextBox(xmin=self._ratioFit.get(
"boxXmin", 0.14), ymin=
None,
2120 xmax=self._ratioFit.get(
"boxXmax", 0.35), ymax=self._ratioFit.get(
"boxYmax", 0.09),
2121 color=rh.GetMarkerColor(), font=43, size=11, lineheight=0.02)
2122 box.move(dx=(box.width()+0.01)*i)
2125 box.addText(
"Const: %.4f#pm%.4f" % (fitres.Parameter(0), fitres.ParError(0)))
2126 box.addText(
"Slope: %.4f#pm%.4f" % (fitres.Parameter(1), fitres.ParError(1)))
2137 self.
_setStats(histos, self._statx, self._staty)
2141 frame =
FrameTGraph2D(pad, bounds, histos, ratioOrig, ratioFactor)
2144 ratioBounds = (bounds[0], ratioBoundsY[0], bounds[2], ratioBoundsY[1])
2145 frame =
FrameRatio(pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
2147 frame =
Frame(pad, bounds, zmax, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption, ybinlabels=ybinlabels)
2150 frame.setLogx(self._xlog)
2151 frame.setLogy(self._ylog)
2152 frame.setGridx(self._xgrid)
2153 frame.setGridy(self._ygrid)
2158 if self._drawStyle
is not None:
2159 ds = self._drawStyle
2160 if self._drawCommand
is not None:
2161 ds = self._drawCommand
2166 frame.setTitle(histos[0].GetTitle())
2168 frame.setXTitle( histos[0].GetXaxis().GetTitle() )
2169 elif self.
_xtitle is not None:
2171 if self._xtitlesize
is not None:
2172 frame.setXTitleSize(self._xtitlesize)
2173 if self._xtitleoffset
is not None:
2174 frame.setXTitleOffset(self._xtitleoffset)
2175 if self._xlabelsize
is not None:
2176 frame.setXLabelSize(self._xlabelsize)
2178 frame.setYTitle( histos[0].GetYaxis().GetTitle() )
2179 elif self.
_ytitle is not None:
2181 if self._ytitlesize
is not None:
2182 frame.setYTitleSize(self._ytitlesize)
2183 if self._ytitleoffset
is not None:
2184 frame.setYTitleOffset(self._ytitleoffset)
2185 if self._ztitle
is not None:
2186 frame.setZTitle(self._ztitle)
2187 if self._ztitleoffset
is not None:
2188 frame.setZTitleOffset(self._ztitleoffset)
2189 if self._adjustMarginLeft
is not None:
2190 frame.adjustMarginLeft(self._adjustMarginLeft)
2191 if self._adjustMarginRight
is not None:
2192 frame.adjustMarginRight(self._adjustMarginRight)
2194 frame.adjustMarginLeft(0.03)
2195 frame.adjustMarginRight(0.08)
2201 for i, h
in enumerate(histos):
2203 if isTGraph2D
and i == 0:
2204 o = o.replace(
"sames",
"")
2211 if ratio
and len(self.
_ratios) > 0:
2212 frame._padRatio.cd()
2213 firstRatio = self.
_ratios[0].getRatio()
2214 if self._ratioUncertainty
and firstRatio
is not None:
2215 firstRatio.SetFillStyle(1001)
2216 firstRatio.SetFillColor(ROOT.kGray)
2217 firstRatio.SetLineColor(ROOT.kGray)
2218 firstRatio.SetMarkerColor(ROOT.kGray)
2219 firstRatio.SetMarkerSize(0)
2221 frame._padRatio.RedrawAxis(
"G")
2232 """Add histograms to a legend.
2236 legendLabels -- List of strings for the legend labels
2238 first = denomUncertainty
2247 entry = legend.AddEntry(self.
_forLegend, label,
"lpf")
2250 legend.AddEntry(h, label,
"LP")
2253 """Group of plots, results a TCanvas"""
2258 name -- String for name of the TCanvas, used also as the basename of the picture files
2259 plots -- List of Plot objects
2262 ncols -- Number of columns (default 2)
2263 legendDx -- Float for moving TLegend in x direction (default None)
2264 legendDy -- Float for moving TLegend in y direction (default None)
2265 legendDw -- Float for changing TLegend width (default None)
2266 legendDh -- Float for changing TLegend height (default None)
2267 legend -- Bool for disabling legend (default True for legend being enabled)
2268 overrideLegendLabels -- List of strings for legend labels, if given, these are used instead of the ones coming from Plotter (default None)
2269 onlyForPileup -- Plots this group only for pileup samples
2276 def _set(attr, default):
2277 setattr(self,
"_"+attr, kwargs.get(attr, default))
2281 _set(
"legendDx",
None)
2282 _set(
"legendDy",
None)
2283 _set(
"legendDw",
None)
2284 _set(
"legendDh",
None)
2285 _set(
"legend",
True)
2287 _set(
"overrideLegendLabels",
None)
2289 _set(
"onlyForPileup",
False)
2294 for name, value
in six.iteritems(kwargs):
2295 if not hasattr(self,
"_"+name):
2296 raise Exception(
"No attribute '%s'" % name)
2297 setattr(self,
"_"+name, value)
2306 for i, plot
in enumerate(self.
_plots):
2307 if plot.getName() == name:
2310 raise Exception(
"Did not find Plot '%s' from PlotGroup '%s'" % (name, self.
_name))
2320 if plot.getName() == name:
2322 raise Exception(
"No Plot named '%s'" % name)
2325 """Return True if the PlotGroup is intended only for pileup samples"""
2326 return self._onlyForPileup
2328 def create(self, tdirectoryNEvents, requireAllHistograms=False):
2329 """Create histograms from a list of TDirectories.
2332 tdirectoryNEvents -- List of (TDirectory, nevents) pairs
2333 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2336 plot.create(tdirectoryNEvents, requireAllHistograms)
2338 def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2339 """Draw the histograms using values for a given algorithm.
2342 legendLabels -- List of strings for legend labels (corresponding to the tdirectories in create())
2343 prefix -- Optional string for file name prefix (default None)
2344 separate -- Save the plots of a group to separate files instead of a file per group (default False)
2345 saveFormat -- String specifying the plot format (default '.pdf')
2346 ratio -- Add ratio to the plot (default True)
2347 directory -- Directory where to save the file (default "")
2350 if self._overrideLegendLabels
is not None:
2351 legendLabels = self._overrideLegendLabels
2354 onlyEmptyPlots =
True
2356 if not plot.isEmpty():
2357 onlyEmptyPlots =
False
2363 return self.
_drawSeparate(legendLabels, prefix, saveFormat, ratio, directory)
2365 cwidth = 500*self._ncols
2366 nrows =
int((len(self.
_plots)+self._ncols-1)/self._ncols)
2367 cheight = 500 * nrows
2374 canvas.Divide(self._ncols, nrows)
2376 for i, plot
in enumerate(self.
_plots):
2377 pad = canvas.cd(i+1)
2381 for i, plot
in enumerate(self.
_plots):
2382 pad = canvas.cd(i+1)
2383 if not plot.isEmpty():
2388 if len(self.
_plots) <= 4:
2398 if self._legendDx
is not None:
2399 lx1 += self._legendDx
2400 lx2 += self._legendDx
2401 if self._legendDy
is not None:
2402 ly1 += self._legendDy
2403 ly2 += self._legendDy
2404 if self._legendDw
is not None:
2405 lx2 += self._legendDw
2406 if self._legendDh
is not None:
2407 ly1 -= self._legendDh
2408 plot =
max(self.
_plots, key=
lambda p: p.getNumberOfHistograms())
2409 denomUnc = sum([p.drawRatioUncertainty()
for p
in self.
_plots]) > 0
2410 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2,
2411 denomUncertainty=(ratio
and denomUnc))
2413 return self.
_save(canvas, saveFormat, prefix=prefix, directory=directory)
2416 """Internal method to do the drawing to separate files per Plot instead of a file per PlotGroup"""
2435 for c
in [canvas, canvasRatio]:
2436 c.SetTopMargin(0.05)
2437 c.SetBottomMargin(0.13)
2438 c.SetLeftMargin(0.16)
2439 c.SetRightMargin(0.05)
2441 ratioForThisPlot = plot.isRatio(ratio)
2443 if ratioForThisPlot:
2459 if plot._legendDx
is not None:
2460 lx1 += plot._legendDx
2461 lx2 += plot._legendDx
2462 if plot._legendDy
is not None:
2463 ly1 += plot._legendDy
2464 ly2 += plot._legendDy
2465 if plot._legendDw
is not None:
2466 lx2 += plot._legendDw
2467 if plot._legendDh
is not None:
2468 ly1 -= plot._legendDh
2471 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.03,
2472 denomUncertainty=(ratioForThisPlot
and plot.drawRatioUncertainty))
2474 ret.extend(self.
_save(c, saveFormat, prefix=prefix, postfix=
"/"+plot.getName(), single=
True, directory=directory))
2478 """Internal method to set divide a pad to two for ratio plots"""
2481 def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True):
2482 if not self._legend:
2485 l = ROOT.TLegend(lx1, ly1, lx2, ly2)
2486 l.SetTextSize(textSize)
2493 plot.addToLegend(l, legendLabels, denomUncertainty)
2497 def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False, directory=""):
2500 if not os.path.exists(directory+
'/'+name):
2501 os.makedirs(directory+
'/'+name)
2502 if prefix
is not None:
2504 if postfix
is not None:
2506 name = os.path.join(directory, name)
2509 backup = ROOT.gErrorIgnoreLevel
2510 ROOT.gErrorIgnoreLevel = ROOT.kWarning
2511 canvas.SaveAs(name+saveFormat)
2513 ROOT.gErrorIgnoreLevel = backup
2517 canvas.SetLogx(
False)
2518 canvas.SetLogy(
False)
2522 return [name+saveFormat]
2525 """Resembles DQM GUI's "On side" layout.
2527 Like PlotGroup, but has only a description of a single plot. The
2528 plot is drawn separately for each file. Useful for 2D histograms."""
2530 def __init__(self, name, plot, ncols=2, onlyForPileup=False):
2531 super(PlotOnSideGroup, self).
__init__(name, [], ncols=ncols, legend=
False, onlyForPileup=onlyForPileup)
2536 raise Exception(
"PlotOnSideGroup.append() is not implemented")
2538 def create(self, tdirectoryNEvents, requireAllHistograms=False):
2540 for i, element
in enumerate(tdirectoryNEvents):
2542 pl.create([element], requireAllHistograms)
2543 pl.setName(pl.getName()+
"_"+
str(i))
2547 kargs = copy.copy(kwargs)
2548 kargs[
"ratio"] =
False
2549 return super(PlotOnSideGroup, self).
draw(*args, **kargs)
2553 """Represents a collection of PlotGroups, produced from a single folder in a DQM file"""
2558 plotGroups -- List of PlotGroup objects
2561 loopSubFolders -- Should the subfolders be looped over? (default: True)
2562 onlyForPileup -- Plots this folder only for pileup samples
2563 onlyForElectron -- Plots this folder only for electron samples
2564 onlyForConversion -- Plots this folder only for conversion samples
2565 onlyForBHadron -- Plots this folder only for B-hadron samples
2566 purpose -- html.PlotPurpose member class for the purpose of the folder, used for grouping of the plots to the HTML pages
2567 page -- Optional string for the page in HTML generatin
2568 section -- Optional string for the section within a page in HTML generation
2569 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".
2582 raise Exception(
"Got unexpected keyword arguments: "+
",".
join(kwargs.keys()))
2585 """Return True if the PlotGroups of this folder should be applied to the all subfolders"""
2589 """Return True if the folder is intended only for pileup samples"""
2624 if pg.getName() == name:
2626 raise Exception(
"No PlotGroup named '%s'" % name)
2628 def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False):
2629 """Create histograms from a list of TFiles.
2632 dirsNEvents -- List of (TDirectory, nevents) pairs
2633 labels -- List of strings for legend labels corresponding the files
2634 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2635 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2638 if len(dirsNEvents) != len(labels):
2639 raise Exception(
"len(dirsNEvents) should be len(labels), now they are %d and %d" % (len(dirsNEvents), len(labels)))
2644 if pg.onlyForPileup()
and not isPileupSample:
2646 pg.create(dirsNEvents, requireAllHistograms)
2648 def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2649 """Draw and save all plots using settings of a given algorithm.
2652 prefix -- Optional string for file name prefix (default None)
2653 separate -- Save the plots of a group to separate files instead of a file per group (default False)
2654 saveFormat -- String specifying the plot format (default '.pdf')
2655 ratio -- Add ratio to the plot (default True)
2656 directory -- Directory where to save the file (default "")
2661 ret.extend(pg.draw(self.
_labels, prefix=prefix, separate=separate, saveFormat=saveFormat, ratio=ratio, directory=directory))
2667 """Method called to (possibly) translate a subfolder name to more 'readable' form
2669 The implementation in this (base) class just returns the
2670 argument. The idea is that a deriving class might want to do
2671 something more complex (like trackingPlots.TrackingPlotFolder
2674 return dqmSubFolderName
2677 """Iterate over possible selections name (used in output directory name and legend) from the name of PlotterFolder, and a return value of translateSubFolder"""
2679 if plotFolderName !=
"":
2680 ret +=
"_"+plotFolderName
2681 if translatedDqmSubFolder
is not None:
2682 ret +=
"_"+translatedDqmSubFolder
2686 """Return True if this subfolder should be processed
2689 limitOnlyTo -- List/set/similar containing the translatedDqmSubFolder
2690 translatedDqmSubFolder -- Return value of translateSubFolder
2692 return translatedDqmSubFolder
in limitOnlyTo
2695 """Class to hold the original name and a 'translated' name of a subfolder in the DQM ROOT file"""
2701 """Equality is defined by the 'translated' name"""
2705 """Plotter for one DQM folder.
2707 This class is supposed to be instantiated by the Plotter class (or
2708 PlotterItem, to be more specific), and not used directly by the
2711 def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators):
2716 name -- Name of the folder (is used in the output directory naming)
2717 possibleDqmFolders -- List of strings for possible directories of histograms in TFiles
2718 dqmSubFolders -- List of lists of strings for list of subfolders per input file, or None if no subfolders
2719 plotFolder -- PlotFolder object
2720 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.
2721 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.
2722 tableCreators -- List of PlotterTableItem objects for tables to be created from this folder
2728 if dqmSubFolders
is None:
2734 for sf_list
in dqmSubFolders:
2736 sf_translated = self.
_plotFolder.translateSubFolder(sf)
2737 if sf_translated
is not None and not sf_translated
in subfolders:
2738 subfolders[sf_translated] =
DQMSubFolder(sf, sf_translated)
2775 """Get list of subfolders, possibly limiting to some of them.
2778 limitOnlyTo -- Object depending on the PlotFolder type for limiting the set of subfolders to be processed
2784 if limitOnlyTo
is None:
2793 """Get a generator for the 'selection name', looping over the name and fallbackNames"""
2795 for selname
in self.
_plotFolder.iterSelectionName(name, dqmSubFolder.translated
if dqmSubFolder
is not None else None):
2801 def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False):
2802 """Create histograms from a list of TFiles.
2804 files -- List of TFiles
2805 labels -- List of strings for legend labels corresponding the files
2806 dqmSubFolder -- DQMSubFolder object for a subfolder (or None for no subfolder)
2807 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2808 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2811 subfolder = dqmSubFolder.subfolder
if dqmSubFolder
is not None else None
2812 neventsHisto = self.
_plotFolder.getNumberOfEventsHistogram()
2820 fallback = fallbackFunc(subfolder)
2821 if fallback
is not None:
2825 d = GetDirectoryCode.codesToNone(ret)
2827 if neventsHisto
is not None and tfile
is not None:
2829 if hnev
is not None:
2830 nev = hnev.GetEntries()
2831 dirsNEvents.append( (d, nev) )
2833 self.
_plotFolder.
create(dirsNEvents, labels, isPileupSample, requireAllHistograms)
2836 """Draw and save all plots using settings of a given algorithm."""
2841 """Instance of plotter that knows the directory content, holds many folders."""
2848 if limitSubFoldersOnlyTo
is not None:
2849 limitOnlyTo = limitSubFoldersOnlyTo.get(plotterFolder.getName(),
None)
2851 for dqmSubFolder
in plotterFolder.getDQMSubFolders(limitOnlyTo=limitOnlyTo):
2852 yield plotterFolder, dqmSubFolder
2856 def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[]):
2860 name -- Name of the folder (is used in the output directory naming)
2861 possibleDirs -- List of strings for possible directories of histograms in TFiles
2862 plotFolder -- PlotFolder object
2865 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.
2866 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.
2885 """Read available subfolders from the files
2888 files -- List of strings for paths to files, or list of TFiles
2890 For each file, loop over 'possibleDirs', and read the
2891 subfolders of first one that exists.
2893 Returns a PlotterFolder if at least one file for which one of
2894 'possibleDirs' exists. Otherwise, return None to signal that
2895 there is nothing available for this PlotFolder.
2900 possibleDirFound =
False
2905 isOpenFile = isinstance(fname, ROOT.TFile)
2909 tfile = ROOT.TFile.Open(fname)
2913 possibleDirFound =
True
2914 if subFolders
is not None:
2916 for key
in d.GetListOfKeys():
2917 if isinstance(key.ReadObj(), ROOT.TDirectory):
2918 subf.append(key.GetName())
2919 subFolders.append(subf)
2922 print(
"Did not find directory '%s' from file %s" % (pd, tfile.GetName()))
2927 if not possibleDirFound:
2937 def create(self, openFiles, legendLabels, dqmSubFolder):
2938 if isinstance(dqmSubFolder, list):
2939 if len(dqmSubFolder) != len(openFiles):
2940 raise Exception(
"When dqmSubFolder is a list, len(dqmSubFolder) should be len(openFiles), now they are %d and %d" % (len(dqmSubFolder), len(openFiles)))
2942 dqmSubFolder = [dqmSubFolder]*len(openFiles)
2943 dqmSubFolder = [sf.subfolder
if sf
is not None else None for sf
in dqmSubFolder]
2946 for f, sf
in zip(openFiles, dqmSubFolder):
2949 if tdir
is not None:
2965 for i
in range(len(tbl)):
2967 tbl[i] = [
None]*colLen
2973 """Contains PlotFolders, i.e. the information what plots to do, and creates a helper object to actually produce the plots."""
2977 ROOT.TH1.AddDirectory(
False)
2980 """Append a plot folder to the plotter.
2982 All arguments are forwarded to the constructor of PlotterItem.
2987 for plotterItem
in self.
_plots:
2988 if plotterItem.getName() == attachToFolder:
2991 raise Exception(
"Did not find plot folder '%s' when trying to attach a table creator to it" % attachToFolder)
2994 """Remove all plot folders and tables"""
2998 return [item.getName()
for item
in self.
_plots]
3001 return [item.getPlotFolder()
for item
in self.
_plots]
3005 if item.getName() == name:
3006 return item.getPlotFolder()
3007 raise Exception(
"No PlotFolder named '%s'" % name)
3010 """Returns PlotterInstance object, which knows how exactly to produce the plots for these files"""