9 ROOT.gROOT.SetBatch(
True)
10 ROOT.PyConfig.IgnoreCommandLineOptions =
True
30 ROOT.gROOT.SetStyle(
"Plain")
31 ROOT.gStyle.SetPadRightMargin(0.07)
32 ROOT.gStyle.SetPadLeftMargin(0.13)
33 ROOT.gStyle.SetTitleFont(font,
"XYZ")
34 ROOT.gStyle.SetTitleSize(titleSize,
"XYZ")
35 ROOT.gStyle.SetTitleOffset(1.2,
"Y")
37 ROOT.gStyle.SetLabelFont(font,
"XYZ")
38 ROOT.gStyle.SetLabelSize(labelSize,
"XYZ")
39 ROOT.gStyle.SetTextSize(labelSize)
40 ROOT.gStyle.SetStatFont(font)
41 ROOT.gStyle.SetStatFontSize(statSize)
43 ROOT.TGaxis.SetMaxDigits(4)
46 obj = tdirectory.Get(name)
49 print "Did not find {obj} from {dir}".
format(obj=name, dir=tdirectory.GetPath())
54 if hasattr(nameOrCreator,
"create"):
55 return nameOrCreator.create(tdirectory)
70 """Get TDirectory from TFile."""
73 for pdf
in possibleDirs:
76 if subDir
is not None:
83 print "Did not find subdirectory '%s' from directory '%s' in file %s" % (subDir, pdf, tfile.GetName())
90 print "Did not find any of directories '%s' from file %s" % (
",".
join(possibleDirs), tfile.GetName())
97 values = collections.OrderedDict()
98 for i
in xrange(1, th1.GetNbinsX()+1):
99 binLabel = th1.GetXaxis().GetBinLabel(i)
100 if renameBin
is not None:
101 binLabel = renameBin(binLabel)
102 values[binLabel] = (th1.GetBinContent(i), th1.GetBinError(i))
108 backup = ROOT.gErrorIgnoreLevel
109 ROOT.gErrorIgnoreLevel = ROOT.kError
110 canvas = ROOT.TCanvas(name, name, width, height)
112 ROOT.gErrorIgnoreLevel = backup
118 divisionPoint = 1-1/ratioFactor
120 topMargin = pad.GetTopMargin()
121 bottomMargin = pad.GetBottomMargin()
122 divisionPoint += (1-divisionPoint)*bottomMargin
123 divisionPointForPad1 = 1-( (1-divisionPoint) / (1-0.02) )
128 ylow = divisionPointForPad1
131 pad1.SetPad(xlow, ylow, xup, yup)
132 pad1.SetFillStyle(4000)
133 pad1.SetBottomMargin(0.02)
139 pad2.SetPad(xlow, ylow, xup, yup)
140 pad2.SetFillStyle(4000)
141 pad2.SetTopMargin(0.0)
142 pad2.SetBottomMargin(bottomMargin/(ratioFactor*divisionPoint))
145 """Calculate the ratios for a list of histograms"""
147 def _divideOrZero(numerator, denominator):
150 return numerator/denominator
153 if a == 0.
and b == 0.:
157 def findBins(wrap, bins_xvalues):
159 currBin = wrap.begin()
161 while i < len(bins_xvalues)
and currBin < wrap.end():
162 (xcenter, xlow, xhigh) = bins_xvalues[i]
163 xlowEdge = xcenter-xlow
164 xupEdge = xcenter+xhigh
166 (curr_center, curr_low, curr_high) = wrap.xvalues(currBin)
167 curr_lowEdge = curr_center-curr_low
168 curr_upEdge = curr_center+curr_high
170 if equal(xlowEdge, curr_lowEdge)
and equal(xupEdge, curr_upEdge):
174 elif curr_upEdge <= xlowEdge:
176 elif curr_lowEdge >= xupEdge:
183 if len(ret) != len(bins_xvalues):
184 ret.extend([
None]*( len(bins_xvalues) - len(ret) ))
190 def __init__(self, th1, uncertainty):
192 self._uncertainty = uncertainty
194 xaxis = th1.GetXaxis()
195 xaxis_arr = xaxis.GetXbins()
196 if xaxis_arr.GetSize() > 0:
197 lst = [xaxis_arr[i]
for i
in xrange(0, xaxis_arr.GetSize())]
198 arr = array.array(
"d", lst)
199 self._ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), arr)
201 self._ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), xaxis.GetXmin(), xaxis.GetXmax())
203 self._ratio.SetStats(0)
204 self._ratio.SetLineColor(ROOT.kBlack)
205 self._ratio.SetLineWidth(1)
206 def draw(self, style=None):
209 if self._uncertainty:
213 self._ratio.Draw(
"same "+st)
217 return self._th1.GetNbinsX()+1
218 def xvalues(self, bin):
219 xval = self._th1.GetBinCenter(bin)
220 xlow = xval-self._th1.GetXaxis().GetBinLowEdge(bin)
221 xhigh = self._th1.GetXaxis().GetBinUpEdge(bin)-xval
222 return (xval, xlow, xhigh)
223 def yvalues(self, bin):
224 yval = self._th1.GetBinContent(bin)
225 yerr = self._th1.GetBinError(bin)
226 return (yval, yerr, yerr)
228 return self._th1.GetBinContent(bin)
229 def divide(self, bin, scale):
230 self._ratio.SetBinContent(bin, _divideOrZero(self._th1.GetBinContent(bin), scale))
231 self._ratio.SetBinError(bin, _divideOrZero(self._th1.GetBinError(bin), scale))
238 def __init__(self, gr, uncertainty):
240 self._uncertainty = uncertainty
247 def draw(self, style=None):
248 if self._ratio
is None:
252 if self._uncertainty:
256 self._ratio.Draw(
"same "+st)
260 return self._gr.GetN()
261 def xvalues(self, bin):
262 return (self._gr.GetX()[bin], self._gr.GetErrorXlow(bin), self._gr.GetErrorXhigh(bin))
263 def yvalues(self, bin):
264 return (self._gr.GetY()[bin], self._gr.GetErrorYlow(bin), self._gr.GetErrorYhigh(bin))
266 return self._gr.GetY()[bin]
267 def divide(self, bin, scale):
272 if bin >= self._gr.GetN():
275 xvals = self.xvalues(bin)
278 self._xvalues.append(xval)
279 self._xerrslow.append(xvals[1])
280 self._xerrshigh.append(xvals[2])
281 yvals = self.yvalues(bin)
282 self._yvalues.append(yvals[0] / scale)
283 if self._uncertainty:
284 self._yerrslow.append(yvals[1] / scale)
285 self._yerrshigh.append(yvals[2] / scale)
287 self._yerrslow.append(0)
288 self._yerrshigh.append(0)
290 if len(self._xvalues) == 0:
293 self._ratio = ROOT.TGraphAsymmErrors(len(self._xvalues), array.array(
"d", self._xvalues), array.array(
"d", self._yvalues),
294 array.array(
"d", self._xerrslow), array.array(
"d", self._xerrshigh),
295 array.array(
"d", self._yerrslow), array.array(
"d", self._yerrshigh))
299 class WrapTGraph2D(WrapTGraph):
300 def __init__(self, gr, uncertainty):
301 WrapTGraph.__init__(self, gr, uncertainty)
302 def xvalues(self, bin):
303 return (self._gr.GetX()[bin], self._gr.GetErrorX(bin), self._gr.GetErrorX(bin))
304 def yvalues(self, bin):
305 return (self._gr.GetY()[bin], self._gr.GetErrorY(bin), self._gr.GetErrorY(bin))
308 if isinstance(o, ROOT.TH1):
309 return WrapTH1(o, ratioUncertainty)
310 elif isinstance(o, ROOT.TGraph):
311 return WrapTGraph(o, ratioUncertainty)
312 elif isinstance(o, ROOT.TGraph2D):
313 return WrapTGraph2D(o, ratioUncertainty)
315 wrappers = [
wrap(h)
for h
in histos]
319 ref_bins = [ref.xvalues(b)
for b
in xrange(ref.begin(), ref.end())]
321 wrappers_bins.append(findBins(w, ref_bins))
323 for i, bin
in enumerate(xrange(ref.begin(), ref.end())):
324 (scale, ylow, yhigh) = ref.yvalues(bin)
325 for w, bins
in zip(wrappers, wrappers_bins):
328 w.divide(bins[i], scale)
337 if isinstance(obj, ROOT.TH1):
338 xaxis = obj.GetXaxis()
339 if limitToNonZeroContent:
340 for i
in xrange(1, obj.GetNbinsX()+1):
341 if obj.GetBinContent(i) != 0:
342 return xaxis.GetBinLowEdge(i)
346 return xaxis.GetBinLowEdge(xaxis.GetFirst())
347 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
348 m =
min([obj.GetX()[i]
for i
in xrange(0, obj.GetN())])
349 return m*0.9
if m > 0
else m*1.1
350 raise Exception(
"Unsupported type %s" % str(obj))
353 if isinstance(obj, ROOT.TH1):
354 xaxis = obj.GetXaxis()
355 if limitToNonZeroContent:
356 for i
in xrange(obj.GetNbinsX(), 0, -1):
357 if obj.GetBinContent(i) != 0:
358 return xaxis.GetBinUpEdge(i)
362 return xaxis.GetBinUpEdge(xaxis.GetLast())
363 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
364 m =
max([obj.GetX()[i]
for i
in xrange(0, obj.GetN())])
365 return m*1.1
if m > 0
else m*0.9
366 raise Exception(
"Unsupported type %s" % str(obj))
369 if isinstance(obj, ROOT.TH2):
370 yaxis = obj.GetYaxis()
371 return yaxis.GetBinLowEdge(yaxis.GetFirst())
372 elif isinstance(obj, ROOT.TH1):
373 return obj.GetMinimum()
374 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
375 m =
min([obj.GetY()[i]
for i
in xrange(0, obj.GetN())])
376 return m*0.9
if m > 0
else m*1.1
377 raise Exception(
"Unsupported type %s" % str(obj))
380 if isinstance(obj, ROOT.TH2):
381 yaxis = obj.GetYaxis()
382 return yaxis.GetBinUpEdge(yaxis.GetLast())
383 elif isinstance(obj, ROOT.TH1):
384 return obj.GetMaximum()
385 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
386 m =
max([obj.GetY()[i]
for i
in xrange(0, obj.GetN())])
387 return m*1.1
if m > 0
else m*0.9
388 raise Exception(
"Unsupported type %s" % str(obj))
391 return max([th1.GetBinContent(i)+th1.GetBinError(i)
for i
in xrange(1, th1.GetNbinsX()+1)])
394 yvals =
filter(
lambda n: n>0, [th1.GetBinContent(i)
for i
in xrange(1, th1.GetNbinsX()+1)])
397 return th1.GetMinimum()
402 ind_min = len(yvals)-1 - int(len(yvals)*0.95)
403 min_val = yvals[ind_min]
404 for i
in xrange(0, ind_min):
405 if yvals[i] > 0.1*min_val:
411 inRange =
lambda x:
True
412 inRange2 =
lambda xmin,xmax:
True
414 inRange =
lambda x: coverageRange[0] <= x <= coverageRange[1]
415 inRange2 =
lambda xmin,xmax: coverageRange[0] <= xmin
and xmax <= coverageRange[1]
417 if isinstance(obj, ROOT.TH1):
418 yvals = [obj.GetBinContent(i)
for i
in xrange(1, obj.GetNbinsX()+1)
if inRange2(obj.GetXaxis().GetBinLowEdge(i), obj.GetXaxis().GetBinUpEdge(i))]
419 yvals =
filter(
lambda x: x != 0, yvals)
420 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
421 yvals = [obj.GetY()[i]
for i
in xrange(0, obj.GetN())
if inRange(obj.GetX()[i])]
423 raise Exception(
"Unsupported type %s" % str(obj))
427 return (yvals[0], yvals[0])
429 return (yvals[0], yvals[1])
432 nvals = int(len(yvals)*coverage)
435 if len(yvals) % 2 == 0:
437 return ( yvals[half-1], yvals[half] )
439 middle = len(yvals)/2
440 return ( yvals[middle-1], yvals[middle+1] )
441 ind_min = (len(yvals)-nvals)/2
442 ind_max = len(yvals)-1 - ind_min
444 return (yvals[ind_min], yvals[ind_max])
446 def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None):
447 """Find x-y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
451 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
454 xmin -- Minimum x value; if None, take the minimum of TH1s
455 xmax -- Maximum x value; if None, take the maximum of TH1s
456 ymin -- Minimum y value; if None, take the minimum of TH1s
457 ymax -- Maximum y value; if None, take the maximum of TH1s
462 if xmin
is None or xmax
is None or isinstance(xmin, list)
or isinstance(max, list):
466 xmins.append(
_getXmin(th1, limitToNonZeroContent=isinstance(xmin, list)))
467 xmaxs.append(
_getXmax(th1, limitToNonZeroContent=isinstance(xmax, list)))
470 xmins =
filter(
lambda h: h
is not None, xmins)
471 xmaxs =
filter(
lambda h: h
is not None, xmaxs)
475 elif isinstance(xmin, list):
479 print "Histogram is zero, using the smallest given value for xmin from", str(xmin)
482 xmins_below =
filter(
lambda x: x<=xm, xmin)
483 if len(xmins_below) == 0:
487 print "Histogram minimum x %f is below all given xmin values %s, using the smallest one" % (xm, str(xmin))
489 xmin =
max(xmins_below)
493 elif isinstance(xmax, list):
497 print "Histogram is zero, using the smallest given value for xmax from", str(xmin)
500 xmaxs_above =
filter(
lambda x: x>xm, xmax)
501 if len(xmaxs_above) == 0:
505 print "Histogram maximum x %f is above all given xmax values %s, using the maximum one" % (xm, str(xmax))
507 xmax =
min(xmaxs_above)
510 th1.GetXaxis().SetRangeUser(xmin, xmax)
512 return (xmin, ymin, xmax, ymax)
514 def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None):
515 """Find y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
519 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
522 ymin -- Minimum y value; if None, take the minimum of TH1s
523 ymax -- Maximum y value; if None, take the maximum of TH1s
524 coverage -- If set, use only values within the 'coverage' part around the median are used for min/max (useful for ratio)
525 coverageRange -- If coverage and this are set, use only the x axis specified by an (xmin,xmax) pair for the coverage
527 if coverage
is not None:
531 y_scale_max =
lambda y: y
532 y_scale_min =
lambda y: y
535 y_scale_max =
lambda y: y*1.5
537 y_scale_max =
lambda y: y*1.05
538 y_scale_min =
lambda y: y*0.9
540 if ymin
is None or ymax
is None or isinstance(ymin, list)
or isinstance(ymax, list):
544 if coverage
is not None:
547 if ylog
and isinstance(ymin, list):
559 elif isinstance(ymin, list):
560 ym_unscaled =
min(ymins)
561 ym = y_scale_min(ym_unscaled)
562 ymins_below =
filter(
lambda y: y<=ym, ymin)
563 if len(ymins_below) == 0:
565 if ym_unscaled < ymin:
567 print "Histogram minimum y %f is below all given ymin values %s, using the smallest one" % (ym, str(ymin))
569 ymin =
max(ymins_below)
574 ymax = y_scale_max(
max(ymaxs+[ymin]))
575 elif isinstance(ymax, list):
576 ym_unscaled =
max(ymaxs)
577 ym = y_scale_max(ym_unscaled)
578 ymaxs_above =
filter(
lambda y: y>ym, ymax)
579 if len(ymaxs_above) == 0:
581 if ym_unscaled > ymax:
583 print "Histogram maximum y %f is above all given ymax values %s, using the maximum one" % (ym_unscaled, str(ymax))
585 ymax =
min(ymaxs_above)
588 th1.GetYaxis().SetRangeUser(ymin, ymax)
593 """Class for subtracting two histograms"""
598 name -- String for name of the resulting histogram (A-B)
599 nameA -- String for A histogram
600 nameB -- String for B histogram
603 title -- String for a title of the resulting histogram (default "")
605 Uncertainties are calculated with the assumption that B is a
606 subset of A, and the histograms contain event counts.
614 """String representation, returns the name"""
618 """Create and return the fake+duplicate histogram from a TDirectory"""
622 if not histoA
or not histoB:
625 ret = histoA.Clone(self.
_name)
628 for i
in xrange(0, histoA.GetNbinsX()+2):
629 val = histoA.GetBinContent(i)-histoB.GetBinContent(i)
630 ret.SetBinContent(i, val)
631 ret.SetBinError(i, math.sqrt(val))
636 """Class to transform bin contents in an arbitrary way."""
641 name -- String for name of the resulting histogram
642 histo -- String for a source histogram (needs to be cumulative)
643 func -- Function to operate on the bin content
651 """String representation, returns the name"""
655 """Create and return the transformed histogram from a TDirectory"""
660 ret = histo.Clone(self.
_name)
663 for i
in xrange(0, histo.GetNbinsX()+2):
664 ret.SetBinContent(i, self.
_func(histo.GetBinContent(i)))
668 """Class to calculate the fake+duplicate rate"""
669 def __init__(self, name, assoc, dup, reco, title=""):
673 name -- String for the name of the resulting efficiency histogram
674 assoc -- String for the name of the "associated" histogram
675 dup -- String for the name of the "duplicates" histogram
676 reco -- String for the name of the "reco" (denominator) histogram
679 title -- String for a title of the resulting histogram (default "")
681 The result is calculated as 1 - (assoc - dup) / reco
690 """String representation, returns the name"""
694 """Create and return the fake+duplicate histogram from a TDirectory"""
701 if not hassoc
or not hdup
or not hreco:
704 hfakedup = hreco.Clone(self.
_name)
705 hfakedup.SetTitle(self.
_title)
707 for i
in xrange(1, hassoc.GetNbinsX()+1):
708 numerVal = hassoc.GetBinContent(i) - hdup.GetBinContent(i)
709 denomVal = hreco.GetBinContent(i)
711 fakedupVal = (1 - numerVal / denomVal)
if denomVal != 0.0
else 0.0
712 errVal = math.sqrt(fakedupVal*(1-fakedupVal)/denomVal)
if (denomVal != 0.0
and fakedupVal <= 1)
else 0.0
714 hfakedup.SetBinContent(i, fakedupVal)
715 hfakedup.SetBinError(i, errVal)
720 """Class for making a cut efficiency histograms.
730 name -- String for name of the resulting histogram
731 histo -- String for a source histogram (needs to be cumulative)
738 """String representation, returns the name"""
742 """Create and return the cut efficiency histogram from a TDirectory"""
748 ascending = histo.GetBinContent(0) < histo.GetBinContent(histo.GetNbinsX())
750 n_tot = histo.GetBinContent(histo.GetNbinsX())
752 n_tot = histo.GetBinContent(0)
757 ret = histo.Clone(self.
_name)
761 for i
in xrange(1, histo.GetNbinsX()+1):
762 n = histo.GetBinContent(i)
764 errVal = math.sqrt(val*(1-val)/n_tot)
765 ret.SetBinContent(i, val)
766 ret.SetBinError(i, errVal)
770 """Class to create a histogram by aggregating bins of another histogram to a bin of the resulting histogram."""
771 def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False):
775 name -- String for the name of the resulting histogram
776 histoName -- String for the name of the source histogram
777 mapping -- Dictionary for mapping the bins (see below)
780 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.
781 scale -- Optional number for scaling the histogram (passed to ROOT.TH1.Scale())
782 renameBin -- Optional function (string -> string) to rename the bins of the input histogram
783 originalOrder -- Boolean for using the order of bins in the histogram (default False)
785 Mapping structure (mapping):
787 Dictionary (you probably want to use collections.OrderedDict)
788 should be a mapping from the destination bin label to a list
789 of source bin labels ("dst -> [src]").
802 """String representation, returns the name"""
806 """Create and return the histogram from a TDirectory"""
812 binValues = [
None]*len(self.
_mapping)
818 for i, (key, labels)
in enumerate(self._mapping.iteritems()):
824 sumTime += values[l][0]
825 sumErrorSq += values[l][1]**2
831 binValues[i] = (sumTime, math.sqrt(sumErrorSq))
834 ivalue = len(values)+1
840 ivalue = values.keys().
index(lab)
842 binIndexOrder.append( (ivalue, i) )
845 binIndexOrder.sort(key=
lambda t: t[0])
848 for i
in xrange(0, len(binValues)):
849 fromIndex = binIndexOrder[i][1]
850 tmpVal.append(binValues[fromIndex])
851 tmpLab.append(binLabels[fromIndex])
859 for i, val
in enumerate(binValues):
862 binValues =
filter(
lambda v: v
is not None, binValues)
863 binLabels =
filter(
lambda v: v
is not None, binLabels)
864 if len(binValues) == 0:
867 result = ROOT.TH1F(self.
_name, self.
_name, len(binValues), 0, len(binValues))
868 for i, (value, label)
in enumerate(
zip(binValues, binLabels)):
869 if value
is not None:
870 result.SetBinContent(i+1, value[0])
871 result.SetBinError(i+1, value[1])
872 result.GetXaxis().SetBinLabel(i+1, label)
877 print "Trying to normalize {name} to {binlabel}, which does not exist".
format(name=self.
_name, binlabel=self.
_normalizeTo)
879 value = th1.GetBinContent(bin)
881 result.Scale(1/value)
883 if self.
_scale is not None:
889 """Class to create a histogram by aggregaging integrals of another histoggrams."""
890 def __init__(self, name, mapping, normalizeTo=None):
894 name -- String for the name of the resulting histogram
895 mapping -- Dictionary for mapping the bin label to a histogram name
898 normalizeTo -- Optional string for a histogram. If given, all bins of the resulting histograqm are divided by the integral of this histogram.
905 """String representation, returns the name"""
909 """Create and return the histogram from a TDirectory"""
911 for key, histoName
in self._mapping.iteritems():
915 result.append( (key, th1.Integral(0, th1.GetNbinsX()+1)) )
919 res = ROOT.TH1F(self.
_name, self.
_name, len(result), 0, len(result))
921 for i, (name, count)
in enumerate(result):
922 res.SetBinContent(i+1, count)
923 res.GetXaxis().SetBinLabel(i+1, name)
929 scale = th1.Integral(0, th1.GetNbinsX()+1)
935 """Class to construct a ROC curve (e.g. efficiency vs. fake rate) from two histograms"""
936 def __init__(self, name, xhistoName, yhistoName, zaxis=False):
940 name -- String for the name of the resulting histogram
941 xhistoName -- String for the name of the x-axis histogram (or another "creator" object)
942 yhistoName -- String for the name of the y-axis histogram (or another "creator" object)
945 zaxis -- If set to True (default False), create a TGraph2D with z axis showing the cut value (recommended drawStyle 'pcolz')
953 """String representation, returns the name"""
957 """Create and return the histogram from a TDirectory"""
960 if xhisto
is None or yhisto
is None:
971 for i
in xrange(1, xhisto.GetNbinsX()+1):
972 x.append(xhisto.GetBinContent(i))
973 xerrup.append(xhisto.GetBinError(i))
974 xerrdown.append(xhisto.GetBinError(i))
976 y.append(yhisto.GetBinContent(i))
977 yerrup.append(yhisto.GetBinError(i))
978 yerrdown.append(yhisto.GetBinError(i))
980 z.append(xhisto.GetXaxis().GetBinUpEdge(i))
983 if x.count(0.0) == len(x)
or y.count(0.0) == len(y):
986 arr =
lambda v: array.array(
"d", v)
989 gr = ROOT.TGraph2D(len(x), arr(x), arr(y), arr(z))
991 gr = ROOT.TGraphAsymmErrors(len(x), arr(x), arr(y), arr(xerrdown), arr(xerrup), arr(yerrdown), arr(yerrup))
997 _plotStylesColor = [4, 2, ROOT.kBlack, ROOT.kOrange+7, ROOT.kMagenta-3]
998 _plotStylesMarker = [21, 20, 22, 34, 33]
1000 def _drawFrame(pad, bounds, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, suffix=""):
1001 """Function to draw a frame
1004 pad -- TPad to where the frame is drawn
1005 bounds -- List or 4-tuple for (xmin, ymin, xmax, ymax)
1008 xbinlabels -- Optional list of strings for x axis bin labels
1009 xbinlabelsize -- Optional number for the x axis bin label size
1010 xbinlabeloption -- Optional string for the x axis bin options (passed to ROOT.TH1.LabelsOption())
1011 suffix -- Optional string for a postfix of the frame name
1013 if xbinlabels
is None:
1014 frame = pad.DrawFrame(*bounds)
1017 nbins = len(xbinlabels)
1018 frame = ROOT.TH1F(
"hframe"+suffix,
"", nbins, bounds[0], bounds[2])
1019 frame.SetBit(ROOT.TH1.kNoStats)
1020 frame.SetBit(ROOT.kCanDelete)
1021 frame.SetMinimum(bounds[1])
1022 frame.SetMaximum(bounds[3])
1023 frame.GetYaxis().SetLimits(bounds[1], bounds[3])
1026 xaxis = frame.GetXaxis()
1027 for i
in xrange(nbins):
1028 xaxis.SetBinLabel(i+1, xbinlabels[i])
1029 if xbinlabelsize
is not None:
1030 xaxis.SetLabelSize(xbinlabelsize)
1031 if xbinlabeloption
is not None:
1032 frame.LabelsOption(xbinlabeloption)
1037 """Class for creating and managing a frame for a simple, one-pad plot"""
1038 def __init__(self, pad, bounds, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None):
1051 self._frame.GetYaxis().SetTitleOffset(self._frame.GetYaxis().GetTitleOffset()*yoffsetFactor)
1052 self._frame.GetXaxis().SetTitleOffset(self._frame.GetXaxis().GetTitleOffset()*xoffsetFactor)
1056 self._pad.SetLogx(log)
1059 self._pad.SetLogy(log)
1062 self._pad.SetGridx(grid)
1065 self._pad.SetGridy(grid)
1068 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1071 self._frame.Draw(
"")
1074 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1077 self._frame.Draw(
"")
1080 self._frame.SetTitle(title)
1083 self._frame.GetXaxis().SetTitle(title)
1086 self._frame.GetXaxis().SetTitleSize(size)
1089 self._frame.GetXaxis().SetTitleOffset(offset)
1092 self._frame.GetXaxis().SetLabelSize(size)
1095 self._frame.GetYaxis().SetTitle(title)
1098 self._frame.GetYaxis().SetTitleSize(size)
1101 self._frame.GetYaxis().SetTitleOffset(offset)
1104 self._pad.RedrawAxis()
1107 """Class for creating and managing a frame for a ratio plot with two subpads"""
1108 def __init__(self, pad, bounds, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None):
1111 if xbinlabels
is not None:
1118 self._frame.GetXaxis().SetLabelSize(0)
1119 self._frame.GetXaxis().SetTitleSize(0)
1121 yoffsetFactor = ratioFactor
1122 divisionPoint = 1-1/ratioFactor
1123 xoffsetFactor = 1/divisionPoint
1126 xoffsetFactor *= 0.6
1129 xoffsetFactor *= 1.5
1132 xoffsetFactor *= 2.3
1137 self._frame.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1138 self._frameRatio.GetYaxis().SetLabelSize(int(self._frameRatio.GetYaxis().GetLabelSize()*0.8))
1139 self._frameRatio.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1140 self._frameRatio.GetXaxis().SetTitleOffset(self._frameRatio.GetXaxis().GetTitleOffset()*xoffsetFactor)
1142 self._frameRatio.GetYaxis().SetNdivisions(4, 5, 0)
1144 self._frameRatio.GetYaxis().SetTitle(ratioYTitle)
1147 self._pad.SetLogx(log)
1148 self._padRatio.SetLogx(log)
1151 self._pad.SetLogy(log)
1154 self._pad.SetGridx(grid)
1155 self._padRatio.SetGridx(grid)
1158 self._pad.SetGridy(grid)
1159 self._padRatio.SetGridy(grid)
1162 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1163 self._padRatio.SetLeftMargin(self._padRatio.GetLeftMargin()+adjust)
1166 self._frame.Draw(
"")
1168 self._frameRatio.Draw(
"")
1171 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1172 self._padRatio.SetRightMargin(self._padRatio.GetRightMargin()+adjust)
1175 self._frame.Draw(
"")
1177 self._frameRatio.Draw(
"")
1180 self._frame.SetTitle(title)
1183 self._frameRatio.GetXaxis().SetTitle(title)
1186 self._frameRatio.GetXaxis().SetTitleSize(size)
1189 self._frameRatio.GetXaxis().SetTitleOffset(offset)
1192 self._frameRatio.GetXaxis().SetLabelSize(size)
1195 self._frame.GetYaxis().SetTitle(title)
1198 self._frameRatio.GetYaxis().SetTitle(title)
1201 self._frame.GetYaxis().SetTitleSize(size)
1202 self._frameRatio.GetYaxis().SetTitleSize(size)
1205 self._frame.GetYaxis().SetTitleOffset(offset)
1206 self._frameRatio.GetYaxis().SetTitleOffset(offset)
1209 self._padRatio.RedrawAxis()
1210 self._pad.RedrawAxis()
1212 self._parentPad.cd()
1219 self.
_coverPad = ROOT.TPad(
"coverpad",
"coverpad", xmin, ymin, xmax, ymax)
1220 self._coverPad.SetBorderMode(0)
1221 self._coverPad.Draw()
1227 """Class for creating and managing a frame for a plot from TGraph2D"""
1228 def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor):
1231 self.
_pad = pad.cd(1)
1235 (xlow, ylow, width, height) = (self._pad.GetXlowNDC(), self._pad.GetYlowNDC(), self._pad.GetWNDC(), self._pad.GetHNDC())
1239 bottomMargin = self._pad.GetBottomMargin()
1240 bottomMarginNew = ROOT.gStyle.GetPadBottomMargin()
1242 ylowNew = yup - (1-bottomMargin)/(1-bottomMarginNew) * (yup-ylow)
1243 topMarginNew = self._pad.GetTopMargin() * (yup-ylow)/(yup-ylowNew)
1245 self._pad.SetPad(xlow, ylowNew, xup, yup)
1246 self._pad.SetTopMargin(topMarginNew)
1247 self._pad.SetBottomMargin(bottomMarginNew)
1250 self._view.SetRange(bounds[0], bounds[1], 0, bounds[2], bounds[3], 20)
1252 self._view.ShowAxis()
1272 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1276 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1304 self._firstHisto.GetZaxis().SetTitle(title)
1307 self._firstHisto.GetZaxis().SetTitleOffset(offset)
1311 ROOT.TAxis3D.ToggleRulers()
1312 ROOT.TAxis3D.ToggleRulers()
1313 axis = ROOT.TAxis3D.GetPadAxis()
1314 axis.SetLabelColor(ROOT.kBlack);
1315 axis.SetAxisColor(ROOT.kBlack);
1320 if hasattr(self,
"_xtitle"):
1321 axis.GetXaxis().SetTitle(self.
_xtitle)
1322 if hasattr(self,
"_xtitlesize"):
1324 if hasattr(self,
"_xlabelsize"):
1325 axis.GetXaxis().SetLabelSize(self._labelsize)
1326 if hasattr(self,
"_ytitle"):
1327 axis.GetYaxis().SetTitle(self.
_ytitle)
1328 if hasattr(self,
"_ytitlesize"):
1330 if hasattr(self,
"_ytitleoffset"):
1334 """Abstraction on top of TLatex"""
1335 def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None):
1339 x -- X coordinate of the text (in NDC)
1340 y -- Y coordinate of the text (in NDC)
1341 text -- String to draw
1342 size -- Size of text (None for the default value, taken from gStyle)
1343 bold -- Should the text be bold?
1344 align -- Alignment of text (left, center, right)
1345 color -- Color of the text
1346 font -- Specify font explicitly
1355 self._l.SetTextFont(self._l.GetTextFont()-20)
1356 if font
is not None:
1357 self._l.SetTextFont(font)
1358 if size
is not None:
1359 self._l.SetTextSize(size)
1360 if isinstance(align, basestring):
1361 if align.lower() ==
"left":
1362 self._l.SetTextAlign(11)
1363 elif align.lower() ==
"center":
1364 self._l.SetTextAlign(21)
1365 elif align.lower() ==
"right":
1366 self._l.SetTextAlign(31)
1368 raise Exception(
"Error: Invalid option '%s' for text alignment! Options are: 'left', 'center', 'right'."%align)
1370 self._l.SetTextAlign(align)
1371 self._l.SetTextColor(color)
1374 """Draw the text to the current TPad.
1377 options -- For interface compatibility, ignored
1379 Provides interface compatible with ROOT's drawable objects.
1381 self._l.DrawLatex(self.
_x, self.
_y, self.
_text)
1385 """Class for drawing text and a background box."""
1386 def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, **kwargs):
1390 xmin -- X min coordinate of the box (NDC)
1391 ymin -- Y min coordinate of the box (NDC) (if None, deduced automatically)
1392 xmax -- X max coordinate of the box (NDC)
1393 ymax -- Y max coordinate of the box (NDC)
1394 lineheight -- Line height
1395 fillColor -- Fill color of the box
1396 transparent -- Should the box be transparent? (in practive the TPave is not created)
1398 Keyword arguments are forwarded to constructor of PlotText
1410 self._textArgs.update(kwargs)
1415 """Add text to current position"""
1422 def move(self, dx=0, dy=0, dw=0, dh=0):
1423 """Move the box and the contained text objects
1426 dx -- Movement in x (positive is to right)
1427 dy -- Movement in y (positive is to up)
1428 dw -- Increment of width (negative to decrease width)
1429 dh -- Increment of height (negative to decrease height)
1431 dx and dy affect to both box and text objects, dw and dh
1432 affect the box only.
1436 if self.
_ymin is not None:
1441 if self.
_ymin is not None:
1449 """Draw the box and the text to the current TPad.
1452 options -- Forwarded to ROOT.TPave.Draw(), and the Draw() of the contained objects
1457 ymin = self.currenty - 0.01
1458 self.
_pave = ROOT.TPave(self.xmin, self.ymin, self.xmax, self.ymax, 0,
"NDC")
1459 self._pave.SetFillColor(self.fillColor)
1460 self._pave.Draw(options)
1466 if hasattr(src,
"GetLineColor")
and hasattr(dst,
"SetLineColor"):
1467 properties.extend([
"LineColor",
"LineStyle",
"LineWidth"])
1468 if hasattr(src,
"GetFillColor")
and hasattr(dst,
"SetFillColor"):
1469 properties.extend([
"FillColor",
"FillStyle"])
1470 if hasattr(src,
"GetMarkerColor")
and hasattr(dst,
"SetMarkerColor"):
1471 properties.extend([
"MarkerColor",
"MarkerSize",
"MarkerStyle"])
1473 for prop
in properties:
1474 getattr(dst,
"Set"+prop)(getattr(src,
"Get"+prop)())
1477 """Represents one plot, comparing one or more histograms."""
1482 name -- String for name of the plot, or Efficiency object
1485 fallback -- Dictionary for specifying fallback (default None)
1486 outname -- String for an output name of the plot (default None for the same as 'name')
1487 title -- String for a title of the plot (default None)
1488 xtitle -- String for x axis title (default None)
1489 xtitlesize -- Float for x axis title size (default None)
1490 xtitleoffset -- Float for x axis title offset (default None)
1491 xlabelsize -- Float for x axis label size (default None)
1492 ytitle -- String for y axis title (default None)
1493 ytitlesize -- Float for y axis title size (default None)
1494 ytitleoffset -- Float for y axis title offset (default None)
1495 ztitle -- String for z axis title (default None)
1496 ztitleoffset -- Float for z axis title offset (default None)
1497 xmin -- Float for x axis minimum (default None, i.e. automatic)
1498 xmax -- Float for x axis maximum (default None, i.e. automatic)
1499 ymin -- Float for y axis minimum (default 0)
1500 ymax -- Float for y axis maximum (default None, i.e. automatic)
1501 xlog -- Bool for x axis log status (default False)
1502 ylog -- Bool for y axis log status (default False)
1503 xgrid -- Bool for x axis grid status (default True)
1504 ygrid -- Bool for y axis grid status (default True)
1505 stat -- Draw stat box? (default False)
1506 fit -- Do gaussian fit? (default False)
1507 statx -- Stat box x coordinate (default 0.65)
1508 staty -- Stat box y coordinate (default 0.8)
1509 statyadjust -- List of floats for stat box y coordinate adjustments (default None)
1510 normalizeToUnitArea -- Normalize histograms to unit area? (default False)
1511 normalizeToNumberOfEvents -- Normalize histograms to number of events? If yes, the PlotFolder needs 'numberOfEventsHistogram' set to a histogram filled once per event (default False)
1512 profileX -- Take histograms via ProfileX()? (default False)
1513 fitSlicesY -- Take histograms via FitSlicesY() (default False)
1514 rebinX -- rebin x axis (default None)
1515 scale -- Scale histograms by a number (default None)
1516 xbinlabels -- List of x axis bin labels (if given, default None)
1517 xbinlabelsize -- Size of x axis bin labels (default None)
1518 xbinlabeloption -- Option string for x axis bin labels (default None)
1519 removeEmptyBins -- Bool for removing empty bins, but only if histogram has bin labels (default False)
1520 printBins -- Bool for printing bin values, but only if histogram has bin labels (default False)
1521 drawStyle -- If "hist", draw as line instead of points (default None)
1522 drawCommand -- Deliver this to Draw() (default: None for same as drawStyle)
1523 lineWidth -- If drawStyle=="hist", the width of line (default 2)
1524 legendDx -- Float for moving TLegend in x direction for separate=True (default None)
1525 legendDy -- Float for moving TLegend in y direction for separate=True (default None)
1526 legendDw -- Float for changing TLegend width for separate=True (default None)
1527 legendDh -- Float for changing TLegend height for separate=True (default None)
1528 legend -- Bool to enable/disable legend (default True)
1529 adjustMarginRight -- Float for adjusting right margin (default None)
1530 ratio -- Possibility to disable ratio for this particular plot (default None)
1531 ratioYmin -- Float for y axis minimum in ratio pad (default: list of values)
1532 ratioYmax -- Float for y axis maximum in ratio pad (default: list of values)
1533 ratioFit -- Fit straight line in ratio? (default None)
1534 ratioUncertainty -- Plot uncertainties on ratio? (default True)
1535 ratioCoverageXrange -- Range of x axis values (xmin,xmax) to limit the automatic ratio y axis range calculation to (default None for disabled)
1536 histogramModifier -- Function to be called in create() to modify the histograms (default None)
1540 def _set(attr, default):
1541 setattr(self,
"_"+attr, kwargs.get(attr, default))
1543 _set(
"fallback",
None)
1544 _set(
"outname",
None)
1547 _set(
"xtitle",
None)
1548 _set(
"xtitlesize",
None)
1549 _set(
"xtitleoffset",
None)
1550 _set(
"xlabelsize",
None)
1551 _set(
"ytitle",
None)
1552 _set(
"ytitlesize",
None)
1553 _set(
"ytitleoffset",
None)
1554 _set(
"ztitle",
None)
1555 _set(
"ztitleoffset",
None)
1572 _set(
"statyadjust",
None)
1574 _set(
"normalizeToUnitArea",
False)
1575 _set(
"normalizeToNumberOfEvents",
False)
1576 _set(
"profileX",
False)
1577 _set(
"fitSlicesY",
False)
1578 _set(
"rebinX",
None)
1581 _set(
"xbinlabels",
None)
1582 _set(
"xbinlabelsize",
None)
1583 _set(
"xbinlabeloption",
None)
1584 _set(
"removeEmptyBins",
False)
1585 _set(
"printBins",
False)
1587 _set(
"drawStyle",
"EP")
1588 _set(
"drawCommand",
None)
1589 _set(
"lineWidth", 2)
1591 _set(
"legendDx",
None)
1592 _set(
"legendDy",
None)
1593 _set(
"legendDw",
None)
1594 _set(
"legendDh",
None)
1595 _set(
"legend",
True)
1597 _set(
"adjustMarginRight",
None)
1600 _set(
"ratioYmin", [0, 0.2, 0.5, 0.7, 0.8, 0.9, 0.95])
1601 _set(
"ratioYmax", [1.05, 1.1, 1.2, 1.3, 1.5, 1.8, 2, 2.5, 3, 4, 5])
1602 _set(
"ratioFit",
None)
1603 _set(
"ratioUncertainty",
True)
1604 _set(
"ratioCoverageXrange",
None)
1606 _set(
"histogramModifier",
None)
1611 for name, value
in kwargs.iteritems():
1612 if not hasattr(self,
"_"+name):
1613 raise Exception(
"No attribute '%s'" % name)
1614 setattr(self,
"_"+name, value)
1618 raise Exception(
"Plot can be cloned only before histograms have been created")
1619 cl = copy.copy(self)
1620 cl.setProperties(**kwargs)
1624 """Return number of existing histograms."""
1628 """Return true if there are no histograms created for the plot"""
1633 if isinstance(h, ROOT.TGraph2D):
1638 if self._ratio
is None:
1640 return ratio
and self._ratio
1643 if self._outname
is not None:
1644 return self._outname
1645 if isinstance(self.
_name, list):
1646 return str(self.
_name[0])
1648 return str(self.
_name)
1651 """Return true if the ratio uncertainty should be drawn"""
1652 return self._ratioUncertainty
1655 """Create one histogram from a TDirectory."""
1660 if isinstance(name, list):
1664 if h
is not None and self._normalizeToNumberOfEvents
and nevents
is not None and nevents != 0:
1665 h.Scale(1.0/nevents)
1668 def create(self, tdirNEvents, requireAllHistograms=False):
1669 """Create histograms from list of TDirectories"""
1670 self.
_histograms = [self.
_createOne(self.
_name, i, tdirNEvent[0], tdirNEvent[1])
for i, tdirNEvent
in enumerate(tdirNEvents)]
1672 if self._fallback
is not None:
1676 self.
_histograms[i] = self.
_createOne(self._fallback[
"name"], i, tdirNEvents[i][0], tdirNEvents[i][1])
1677 profileX[i] = self._fallback.get(
"profileX", self._profileX)
1679 if self._histogramModifier
is not None:
1683 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)))
1688 def _modifyHisto(th1, profileX):
1693 th1 = th1.ProfileX()
1695 if self._fitSlicesY:
1696 ROOT.TH1.AddDirectory(
True)
1698 th1 = ROOT.gDirectory.Get(th1.GetName()+
"_2")
1699 th1.SetDirectory(
None)
1701 ROOT.TH1.AddDirectory(
False)
1703 if self._title
is not None:
1704 th1.SetTitle(self._title)
1706 if self._scale
is not None:
1707 th1.Scale(self._scale)
1711 if self._fallback
is not None:
1715 if requireAllHistograms
and None in self.
_histograms:
1719 """Set stats box."""
1722 if h
is not None and hasattr(h,
"SetStats"):
1726 def _doStats(h, col, dy):
1731 if self._fit
and h.GetEntries() > 0.5:
1733 f = h.GetListOfFunctions().FindObject(
"gaus")
1741 st = h.GetListOfFunctions().FindObject(
"stats")
1745 st.SetX1NDC(startingX)
1746 st.SetX2NDC(startingX+0.3)
1747 st.SetY1NDC(startingY+dy)
1748 st.SetY2NDC(startingY+dy+0.15)
1749 st.SetTextColor(col)
1752 for i, h
in enumerate(histos):
1753 if self._statyadjust
is not None and i < len(self._statyadjust):
1754 dy += self._statyadjust[i]
1756 _doStats(h, _plotStylesColor[i], dy)
1760 """Normalise histograms to unit area"""
1768 if h.GetSumw2().fN <= 0:
1772 def draw(self, pad, ratio, ratioFactor, nrows):
1773 """Draw the histograms using values for a given algorithm."""
1784 if self._normalizeToUnitArea:
1787 if self._rebinX
is not None:
1789 h.Rebin(self._rebinX)
1791 def _styleMarker(h, msty, col):
1792 h.SetMarkerStyle(msty)
1793 h.SetMarkerColor(col)
1794 h.SetMarkerSize(0.7)
1798 def _styleHist(h, msty, col):
1799 _styleMarker(h, msty, col)
1801 h.SetLineWidth(self._lineWidth)
1804 style = _styleMarker
1805 if "hist" in self._drawStyle.lower():
1808 if "l" in self._drawStyle.lower():
1816 style(h, _plotStylesMarker[i], _plotStylesColor[i])
1818 if len(histos) == 0:
1820 print "No histograms for plot {name}".
format(name=self.
getName())
1825 histosHaveBinLabels = len(histos[0].GetXaxis().GetBinLabel(1)) > 0
1826 xbinlabels = self._xbinlabels
1827 if xbinlabels
is None:
1828 if histosHaveBinLabels:
1829 xbinlabels = [histos[0].GetXaxis().GetBinLabel(i)
for i
in xrange(1, histos[0].GetNbinsX()+1)]
1831 for h
in histos[1:]:
1832 labels = [h.GetXaxis().GetBinLabel(i)
for i
in xrange(1, h.GetNbinsX()+1)]
1833 diff = difflib.unified_diff(xbinlabels, labels, n=
max(len(xbinlabels), len(labels)))
1837 if item[:2] ==
"@@":
1840 operation.append(item[0])
1842 if lab
in xbinlabels:
1844 ind = xbinlabels.index(lab)
1845 if operation[ind] ==
"-" and operation[-1] ==
"+":
1846 xbinlabels.remove(lab)
1848 elif operation[ind] ==
"+" and operation[-1] ==
"-":
1852 raise Exception(
"This should never happen")
1853 xbinlabels.append(lab)
1856 if len(xbinlabels) == 0:
1861 h_new = h.Clone(h.GetName()+
"_xbinlabels")
1862 h_new.SetBins(len(xbinlabels), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(xbinlabels))
1863 for i, label
in enumerate(xbinlabels):
1864 bin = h.GetXaxis().FindFixBin(label)
1866 h_new.SetBinContent(i+1, h.GetBinContent(bin))
1867 h_new.SetBinError(i+1, h.GetBinError(bin))
1869 h_new.SetBinContent(i+1, 0)
1870 h_new.SetBinError(i+1, 0)
1871 histos_new.append(h_new)
1876 if self._removeEmptyBins
and histosHaveBinLabels:
1879 binsToRemove = set()
1880 for b
in xrange(1, histos[0].GetNbinsX()+1):
1883 if h.GetBinContent(b) > 0:
1889 if len(binsToRemove) > 0:
1892 for i
in xrange(len(xbinlabels)):
1893 if (i+1)
not in binsToRemove:
1894 xbinlab_new.append(xbinlabels[i])
1895 xbinlabels = xbinlab_new
1901 for b
in xrange(1, h.GetNbinsX()+1):
1902 if b
not in binsToRemove:
1903 values.append( (h.GetXaxis().GetBinLabel(b), h.GetBinContent(b), h.GetBinError(b)) )
1906 h_new = h.Clone(h.GetName()+
"_empty")
1907 h_new.SetBins(len(values), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(values))
1908 for b, (l, v, e)
in enumerate(values):
1909 h_new.GetXaxis().SetBinLabel(b+1, l)
1910 h_new.SetBinContent(b+1, v)
1911 h_new.SetBinError(b+1, e)
1913 histos_new.append(h_new)
1917 if len(histos) == 0:
1919 print "No histograms with non-empty bins for plot {name}".
format(name=self.
getName())
1922 if self._printBins
and histosHaveBinLabels:
1923 print "####################"
1925 width =
max([len(l)
for l
in xbinlabels])
1926 tmp =
"%%-%ds " % width
1927 for b
in xrange(1, histos[0].GetNbinsX()+1):
1928 s = tmp % xbinlabels[b-1]
1930 s +=
"%.3f " % h.GetBinContent(b)
1935 xmin=self._xmin, xmax=self._xmax,
1936 ymin=self._ymin, ymax=self._ymax)
1944 ratioHistos =
filter(
lambda h: h
is not None, [r.getRatio()
for r
in self.
_ratios[1:]])
1946 if len(ratioHistos) > 0:
1947 ratioBoundsY =
_findBoundsY(ratioHistos, ylog=
False, ymin=self._ratioYmin, ymax=self._ratioYmax, coverage=0.68, coverageRange=self._ratioCoverageXrange)
1949 ratioBoundsY = (0.9, 1,1)
1951 if self._ratioFit
is not None:
1952 for i, rh
in enumerate(ratioHistos):
1953 tf_line = ROOT.TF1(
"line%d"%i,
"[0]+x*[1]")
1954 tf_line.SetRange(self._ratioFit[
"rangemin"], self._ratioFit[
"rangemax"])
1955 fitres = rh.Fit(tf_line,
"RINSQ")
1956 tf_line.SetLineColor(rh.GetMarkerColor())
1957 tf_line.SetLineWidth(2)
1958 self._ratioAdditional.append(tf_line)
1959 box =
PlotTextBox(xmin=self._ratioFit.get(
"boxXmin", 0.14), ymin=
None,
1960 xmax=self._ratioFit.get(
"boxXmax", 0.35), ymax=self._ratioFit.get(
"boxYmax", 0.09),
1961 color=rh.GetMarkerColor(), font=43, size=11, lineheight=0.02)
1962 box.move(dx=(box.width()+0.01)*i)
1965 box.addText(
"Const: %.4f#pm%.4f" % (fitres.Parameter(0), fitres.ParError(0)))
1966 box.addText(
"Slope: %.4f#pm%.4f" % (fitres.Parameter(1), fitres.ParError(1)))
1967 self._mainAdditional.append(box)
1977 self.
_setStats(histos, self._statx, self._staty)
1981 frame =
FrameTGraph2D(pad, bounds, histos, ratioOrig, ratioFactor)
1984 ratioBounds = (bounds[0], ratioBoundsY[0], bounds[2], ratioBoundsY[1])
1985 frame =
FrameRatio(pad, bounds, ratioBounds, ratioFactor, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
1987 frame =
Frame(pad, bounds, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
1990 frame.setLogx(self._xlog)
1991 frame.setLogy(self._ylog)
1992 frame.setGridx(self._xgrid)
1993 frame.setGridy(self._ygrid)
1998 if self._drawStyle
is not None:
1999 ds = self._drawStyle
2000 if self._drawCommand
is not None:
2001 ds = self._drawCommand
2006 frame.setTitle(histos[0].GetTitle())
2007 if self._xtitle
is not None:
2008 frame.setXTitle(self._xtitle)
2009 if self._xtitlesize
is not None:
2010 frame.setXTitleSize(self._xtitlesize)
2011 if self._xtitleoffset
is not None:
2012 frame.setXTitleOffset(self._xtitleoffset)
2013 if self._xlabelsize
is not None:
2014 frame.setXLabelSize(self._xlabelsize)
2015 if self._ytitle
is not None:
2016 frame.setYTitle(self._ytitle)
2017 if self._ytitlesize
is not None:
2018 frame.setYTitleSize(self._ytitlesize)
2019 if self._ytitleoffset
is not None:
2020 frame.setYTitleOffset(self._ytitleoffset)
2021 if self._ztitle
is not None:
2022 frame.setZTitle(self._ztitle)
2023 if self._ztitleoffset
is not None:
2024 frame.setZTitleOffset(self._ztitleoffset)
2025 if self._adjustMarginRight
is not None:
2026 frame.adjustMarginRight(self._adjustMarginRight)
2028 frame.adjustMarginLeft(0.03)
2029 frame.adjustMarginRight(0.08)
2042 if ratio
and len(histos) > 0:
2043 frame._padRatio.cd()
2044 firstRatio = self.
_ratios[0].getRatio()
2045 if self._ratioUncertainty
and firstRatio
is not None:
2046 firstRatio.SetFillStyle(1001)
2047 firstRatio.SetFillColor(ROOT.kGray)
2048 firstRatio.SetLineColor(ROOT.kGray)
2049 firstRatio.SetMarkerColor(ROOT.kGray)
2050 firstRatio.SetMarkerSize(0)
2052 frame._padRatio.RedrawAxis(
"G")
2063 """Add histograms to a legend.
2067 legendLabels -- List of strings for the legend labels
2069 first = denomUncertainty
2076 self._forLegend.SetFillStyle(1001)
2077 self._forLegend.SetFillColor(ROOT.kGray)
2078 entry = legend.AddEntry(self.
_forLegend, label,
"lpf")
2081 legend.AddEntry(h, label,
"LP")
2084 """Group of plots, results a TCanvas"""
2089 name -- String for name of the TCanvas, used also as the basename of the picture files
2090 plots -- List of Plot objects
2093 ncols -- Number of columns (default 2)
2094 legendDx -- Float for moving TLegend in x direction (default None)
2095 legendDy -- Float for moving TLegend in y direction (default None)
2096 legendDw -- Float for changing TLegend width (default None)
2097 legendDh -- Float for changing TLegend height (default None)
2098 legend -- Bool for disabling legend (default True for legend being enabled)
2099 overrideLegendLabels -- List of strings for legend labels, if given, these are used instead of the ones coming from Plotter (default None)
2100 onlyForPileup -- Plots this group only for pileup samples
2105 def _set(attr, default):
2106 setattr(self,
"_"+attr, kwargs.get(attr, default))
2110 _set(
"legendDx",
None)
2111 _set(
"legendDy",
None)
2112 _set(
"legendDw",
None)
2113 _set(
"legendDh",
None)
2114 _set(
"legend",
True)
2116 _set(
"overrideLegendLabels",
None)
2118 _set(
"onlyForPileup",
False)
2123 for name, value
in kwargs.iteritems():
2124 if not hasattr(self,
"_"+name):
2125 raise Exception(
"No attribute '%s'" % name)
2126 setattr(self,
"_"+name, value)
2135 for i, plot
in enumerate(self.
_plots):
2136 if plot.getName() == name:
2139 raise Exception(
"Did not find Plot '%s' from PlotGroup '%s'" % (name, self.
_name))
2145 self._plots.append(plot)
2149 if plot.getName() == name:
2151 raise Exception(
"No Plot named '%s'" % name)
2154 """Return True if the PlotGroup is intended only for pileup samples"""
2155 return self._onlyForPileup
2157 def create(self, tdirectoryNEvents, requireAllHistograms=False):
2158 """Create histograms from a list of TDirectories.
2161 tdirectoryNEvents -- List of (TDirectory, nevents) pairs
2162 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2165 plot.create(tdirectoryNEvents, requireAllHistograms)
2167 def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True):
2168 """Draw the histograms using values for a given algorithm.
2171 legendLabels -- List of strings for legend labels (corresponding to the tdirectories in create())
2172 prefix -- Optional string for file name prefix (default None)
2173 separate -- Save the plots of a group to separate files instead of a file per group (default False)
2174 saveFormat -- String specifying the plot format (default '.pdf')
2175 ratio -- Add ratio to the plot (default True)
2178 if self._overrideLegendLabels
is not None:
2179 legendLabels = self._overrideLegendLabels
2182 onlyEmptyPlots =
True
2184 if not plot.isEmpty():
2185 onlyEmptyPlots =
False
2191 return self.
_drawSeparate(legendLabels, prefix, saveFormat, ratio)
2193 cwidth = 500*self._ncols
2194 nrows = int((len(self.
_plots)+self._ncols-1)/self._ncols)
2195 cheight = 500 * nrows
2202 canvas.Divide(self._ncols, nrows)
2204 for i, plot
in enumerate(self.
_plots):
2205 pad = canvas.cd(i+1)
2209 for i, plot
in enumerate(self.
_plots):
2210 pad = canvas.cd(i+1)
2211 if not plot.isEmpty():
2216 if len(self.
_plots) <= 4:
2226 if self._legendDx
is not None:
2227 lx1 += self._legendDx
2228 lx2 += self._legendDx
2229 if self._legendDy
is not None:
2230 ly1 += self._legendDy
2231 ly2 += self._legendDy
2232 if self._legendDw
is not None:
2233 lx2 += self._legendDw
2234 if self._legendDh
is not None:
2235 ly1 -= self._legendDh
2236 plot =
max(self.
_plots, key=
lambda p: p.getNumberOfHistograms())
2237 denomUnc = sum([p.drawRatioUncertainty()
for p
in self.
_plots]) > 0
2238 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2,
2239 denomUncertainty=(ratio
and denomUnc))
2241 return self.
_save(canvas, saveFormat, prefix=prefix)
2244 """Internal method to do the drawing to separate files per Plot instead of a file per PlotGroup"""
2252 for c
in [canvas, canvasRatio]:
2253 c.SetTopMargin(0.05)
2254 c.SetBottomMargin(0.13)
2255 c.SetLeftMargin(0.16)
2256 c.SetRightMargin(0.05)
2269 ratioForThisPlot = plot.isRatio(ratio)
2271 if ratioForThisPlot:
2287 if plot._legendDx
is not None:
2288 lx1 += plot._legendDx
2289 lx2 += plot._legendDx
2290 if plot._legendDy
is not None:
2291 ly1 += plot._legendDy
2292 ly2 += plot._legendDy
2293 if plot._legendDw
is not None:
2294 lx2 += plot._legendDw
2295 if plot._legendDh
is not None:
2296 ly1 -= plot._legendDh
2299 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.03,
2300 denomUncertainty=(ratioForThisPlot
and plot.drawRatioUncertainty))
2302 ret.extend(self.
_save(c, saveFormat, prefix=prefix, postfix=
"_"+plot.getName(), single=
True))
2306 """Internal method to set divide a pad to two for ratio plots"""
2309 def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True):
2310 if not self._legend:
2313 l = ROOT.TLegend(lx1, ly1, lx2, ly2)
2314 l.SetTextSize(textSize)
2321 plot.addToLegend(l, legendLabels, denomUncertainty)
2325 def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False):
2328 if prefix
is not None:
2330 if postfix
is not None:
2334 backup = ROOT.gErrorIgnoreLevel
2335 ROOT.gErrorIgnoreLevel = ROOT.kWarning
2336 canvas.SaveAs(name+saveFormat)
2338 ROOT.gErrorIgnoreLevel = backup
2342 canvas.SetLogx(
False)
2343 canvas.SetLogy(
False)
2347 return [name+saveFormat]
2350 """Represents a collection of PlotGroups, produced from a single folder in a DQM file"""
2355 plotGroups -- List of PlotGroup objects
2358 loopSubFolders -- Should the subfolders be looped over? (default: True)
2359 onlyForPileup -- Plots this folder only for pileup samples
2360 onlyForElectron -- Plots this folder only for electron samples
2361 onlyForConversion -- Plots this folder only for conversion samples
2362 purpose -- html.PlotPurpose member class for the purpose of the folder, used for grouping of the plots to the HTML pages
2363 page -- Optional string for the page in HTML generatin
2364 section -- Optional string for the section within a page in HTML generation
2365 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".
2377 raise Exception(
"Got unexpected keyword arguments: "+
",".
join(kwargs.keys()))
2380 """Return True if the PlotGroups of this folder should be applied to the all subfolders"""
2384 """Return True if the folder is intended only for pileup samples"""
2406 self._plotGroups.append(plotGroup)
2416 if pg.getName() == name:
2418 raise Exception(
"No PlotGroup named '%s'" % name)
2420 def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False):
2421 """Create histograms from a list of TFiles.
2424 dirsNEvents -- List of (TDirectory, nevents) pairs
2425 labels -- List of strings for legend labels corresponding the files
2426 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2427 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2430 if len(dirsNEvents) != len(labels):
2431 raise Exception(
"len(dirsNEvents) should be len(labels), now they are %d and %d" % (len(dirsNEvents), len(labels)))
2436 if pg.onlyForPileup()
and not isPileupSample:
2438 pg.create(dirsNEvents, requireAllHistograms)
2440 def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True):
2441 """Draw and save all plots using settings of a given algorithm.
2444 prefix -- Optional string for file name prefix (default None)
2445 separate -- Save the plots of a group to separate files instead of a file per group (default False)
2446 saveFormat -- String specifying the plot format (default '.pdf')
2447 ratio -- Add ratio to the plot (default True)
2452 ret.extend(pg.draw(self.
_labels, prefix=prefix, separate=separate, saveFormat=saveFormat, ratio=ratio))
2458 """Method called to (possibly) translate a subfolder name to more 'readable' form
2460 The implementation in this (base) class just returns the
2461 argument. The idea is that a deriving class might want to do
2462 something more complex (like trackingPlots.TrackingPlotFolder
2465 return dqmSubFolderName
2468 """Iterate over possible selections name (used in output directory name and legend) from the name of PlotterFolder, and a return value of translateSubFolder"""
2470 if plotFolderName !=
"":
2471 ret +=
"_"+plotFolderName
2472 if translatedDqmSubFolder
is not None:
2473 ret +=
"_"+translatedDqmSubFolder
2477 """Return True if this subfolder should be processed
2480 limitOnlyTo -- List/set/similar containing the translatedDqmSubFolder
2481 translatedDqmSubFolder -- Return value of translateSubFolder
2483 return translatedDqmSubFolder
in limitOnlyTo
2486 """Class to hold the original name and a 'translated' name of a subfolder in the DQM ROOT file"""
2492 """Equality is defined by the 'translated' name"""
2496 """Plotter for one DQM folder.
2498 This class is supposed to be instantiated by the Plotter class (or
2499 PlotterItem, to be more specific), and not used directly by the
2502 def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators):
2507 name -- Name of the folder (is used in the output directory naming)
2508 possibleDqmFolders -- List of strings for possible directories of histograms in TFiles
2509 dqmSubFolders -- List of lists of strings for list of subfolders per input file, or None if no subfolders
2510 plotFolder -- PlotFolder object
2511 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.
2512 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.
2513 tableCreators -- List of PlotterTableItem objects for tables to be created from this folder
2519 if dqmSubFolders
is None:
2525 for sf_list
in dqmSubFolders:
2527 sf_translated = self._plotFolder.translateSubFolder(sf)
2528 if sf_translated
is not None and not sf_translated
in subfolders:
2529 subfolders[sf_translated] =
DQMSubFolder(sf, sf_translated)
2532 self._dqmSubFolders.sort(key=
lambda sf: sf.subfolder)
2542 return self._plotFolder.getPurpose()
2545 return self._plotFolder.getPage()
2548 return self._plotFolder.getSection()
2551 return self._plotFolder.onlyForPileup()
2554 return self._plotFolder.onlyForElectron()
2557 return self._plotFolder.onlyForConversion()
2563 """Get list of subfolders, possibly limiting to some of them.
2566 limitOnlyTo -- Object depending on the PlotFolder type for limiting the set of subfolders to be processed
2572 if limitOnlyTo
is None:
2575 return filter(
lambda s: self._plotFolder.limitSubFolder(limitOnlyTo, s.translated), self.
_dqmSubFolders)
2581 """Get a generator for the 'selection name', looping over the name and fallbackNames"""
2583 for selname
in self._plotFolder.iterSelectionName(name, dqmSubFolder.translated
if dqmSubFolder
is not None else None):
2589 def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False):
2590 """Create histograms from a list of TFiles.
2592 files -- List of TFiles
2593 labels -- List of strings for legend labels corresponding the files
2594 dqmSubFolder -- DQMSubFolder object for a subfolder (or None for no subfolder)
2595 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2596 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2599 subfolder = dqmSubFolder.subfolder
if dqmSubFolder
is not None else None
2600 neventsHisto = self._plotFolder.getNumberOfEventsHistogram()
2608 fallback = fallbackFunc(subfolder)
2609 if fallback
is not None:
2613 d = GetDirectoryCode.codesToNone(ret)
2615 if neventsHisto
is not None and tfile
is not None:
2617 if hnev
is not None:
2618 nev = hnev.GetEntries()
2619 dirsNEvents.append( (d, nev) )
2621 self._plotFolder.create(dirsNEvents, labels, isPileupSample, requireAllHistograms)
2624 """Draw and save all plots using settings of a given algorithm."""
2625 return self._plotFolder.draw(*args, **kwargs)
2629 """Instance of plotter that knows the directory content, holds many folders."""
2636 if limitSubFoldersOnlyTo
is not None:
2637 limitOnlyTo = limitSubFoldersOnlyTo.get(plotterFolder.getName(),
None)
2639 for dqmSubFolder
in plotterFolder.getDQMSubFolders(limitOnlyTo=limitOnlyTo):
2640 yield plotterFolder, dqmSubFolder
2644 def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[]):
2648 name -- Name of the folder (is used in the output directory naming)
2649 possibleDirs -- List of strings for possible directories of histograms in TFiles
2650 plotFolder -- PlotFolder object
2653 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.
2654 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.
2670 self._tableCreators.append(tc)
2673 """Read available subfolders from the files
2676 files -- List of strings for paths to files, or list of TFiles
2678 For each file, loop over 'possibleDirs', and read the
2679 subfolders of first one that exists.
2681 Returns a PlotterFolder if at least one file for which one of
2682 'possibleDirs' exists. Otherwise, return None to signal that
2683 there is nothing available for this PlotFolder.
2686 if self._plotFolder.loopSubFolders():
2688 possibleDirFound =
False
2693 isOpenFile = isinstance(fname, ROOT.TFile)
2697 tfile = ROOT.TFile.Open(fname)
2701 possibleDirFound =
True
2702 if subFolders
is not None:
2704 for key
in d.GetListOfKeys():
2705 if isinstance(key.ReadObj(), ROOT.TDirectory):
2706 subf.append(key.GetName())
2707 subFolders.append(subf)
2713 if not possibleDirFound:
2723 def create(self, openFiles, legendLabels, dqmSubFolder):
2724 if isinstance(dqmSubFolder, list):
2725 if len(dqmSubFolder) != len(openFiles):
2726 raise Exception(
"When dqmSubFolder is a list, len(dqmSubFolder) should be len(openFiles), now they are %d and %d" % (len(dqmSubFolder), len(openFiles)))
2728 dqmSubFolder = [dqmSubFolder]*len(openFiles)
2729 dqmSubFolder = [sf.subfolder
if sf
is not None else None for sf
in dqmSubFolder]
2732 for f, sf
in zip(openFiles, dqmSubFolder):
2735 if tdir
is not None:
2736 data = self._tableCreator.create(tdir)
2751 for i
in xrange(len(tbl)):
2753 tbl[i] = [
None]*colLen
2755 return html.Table(columnHeaders=legendLabels, rowHeaders=self._tableCreator.headers(), table=tbl,
2756 purpose=self._tableCreator.getPurpose(), page=self._tableCreator.getPage(), section=self._tableCreator.getSection(dqmSubFolder[0]))
2759 """Contains PlotFolders, i.e. the information what plots to do, and creates a helper object to actually produce the plots."""
2763 ROOT.TH1.AddDirectory(
False)
2766 """Append a plot folder to the plotter.
2768 All arguments are forwarded to the constructor of PlotterItem.
2773 for plotterItem
in self.
_plots:
2774 if plotterItem.getName() == attachToFolder:
2777 raise Exception(
"Did not find plot folder '%s' when trying to attach a table creator to it" % attachToFolder)
2780 """Remove all plot folders and tables"""
2784 return [item.getName()
for item
in self.
_plots]
2787 return [item.getPlotFolder()
for item
in self.
_plots]
2791 if item.getName() == name:
2792 return item.getPlotFolder()
2793 raise Exception(
"No PlotFolder named '%s'" % name)
2796 """Returns PlotterInstance object, which knows how exactly to produce the plots for these files"""
def _getYminMaxAroundMedian
def getSelectionNameIterator
def getPossibleDQMFolders
def _getYminIgnoreOutlier
bool equal(const T &first, const T &second)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
Abs< T >::type abs(const T &t)
static std::string join(char **cmd)
def getNumberOfHistograms
def getNumberOfEventsHistogram
def _getDirectoryDetailed
auto wrap(F iFunc) -> decltype(iFunc())
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