CMS 3D CMS Logo

plotting.py
Go to the documentation of this file.
1 import os
2 import sys
3 import math
4 import copy
5 import array
6 import difflib
7 import collections
8 
9 import ROOT
10 ROOT.gROOT.SetBatch(True)
11 ROOT.PyConfig.IgnoreCommandLineOptions = True
12 
13 import html
14 
15 verbose=False
16 _ratioYTitle = "Ratio"
17 
18 def _setStyle():
19  _absoluteSize = True
20  if _absoluteSize:
21  font = 43
22  titleSize = 22
23  labelSize = 22
24  statSize = 14
25  else:
26  font = 42
27  titleSize = 0.05
28  labelSize = 0.05
29  statSize = 0.025
30 
31  ROOT.gROOT.SetStyle("Plain")
32  ROOT.gStyle.SetPadRightMargin(0.07)
33  ROOT.gStyle.SetPadLeftMargin(0.13)
34  ROOT.gStyle.SetTitleFont(font, "XYZ")
35  ROOT.gStyle.SetTitleSize(titleSize, "XYZ")
36  ROOT.gStyle.SetTitleOffset(1.2, "Y")
37  #ROOT.gStyle.SetTitleFontSize(0.05)
38  ROOT.gStyle.SetLabelFont(font, "XYZ")
39  ROOT.gStyle.SetLabelSize(labelSize, "XYZ")
40  ROOT.gStyle.SetTextSize(labelSize)
41  ROOT.gStyle.SetStatFont(font)
42  ROOT.gStyle.SetStatFontSize(statSize)
43 
44  ROOT.TGaxis.SetMaxDigits(4)
45 
46 def _getObject(tdirectory, name):
47  obj = tdirectory.Get(name)
48  if not obj:
49  if verbose:
50  print "Did not find {obj} from {dir}".format(obj=name, dir=tdirectory.GetPath())
51  return None
52  return obj
53 
54 def _getOrCreateObject(tdirectory, nameOrCreator):
55  if hasattr(nameOrCreator, "create"):
56  return nameOrCreator.create(tdirectory)
57  return _getObject(tdirectory, nameOrCreator)
58 
60  class FileNotExist: pass
62  class SubDirNotExist: pass
63 
64  @staticmethod
65  def codesToNone(code):
67  return None
68  return code
69 
70 def _getDirectoryDetailed(tfile, possibleDirs, subDir=None):
71  """Get TDirectory from TFile."""
72  if tfile is None:
74  for pdf in possibleDirs:
75  d = tfile.Get(pdf)
76  if d:
77  if subDir is not None:
78  # Pick associator if given
79  d = d.Get(subDir)
80  if d:
81  return d
82  else:
83  if verbose:
84  print "Did not find subdirectory '%s' from directory '%s' in file %s" % (subDir, pdf, tfile.GetName())
85 # if "Step" in subDir:
86 # raise Exception("Foo")
88  else:
89  return d
90  if verbose:
91  print "Did not find any of directories '%s' from file %s" % (",".join(possibleDirs), tfile.GetName())
93 
94 def _getDirectory(*args, **kwargs):
95  return GetDirectoryCode.codesToNone(_getDirectoryDetailed(*args, **kwargs))
96 
97 def _th1ToOrderedDict(th1, renameBin=None):
98  values = collections.OrderedDict()
99  for i in xrange(1, th1.GetNbinsX()+1):
100  binLabel = th1.GetXaxis().GetBinLabel(i)
101  if renameBin is not None:
102  binLabel = renameBin(binLabel)
103  values[binLabel] = (th1.GetBinContent(i), th1.GetBinError(i))
104  return values
105 
106 def _createCanvas(name, width, height):
107  # silence warning of deleting canvas with the same name
108  if not verbose:
109  backup = ROOT.gErrorIgnoreLevel
110  ROOT.gErrorIgnoreLevel = ROOT.kError
111  canvas = ROOT.TCanvas(name, name, width, height)
112  if not verbose:
113  ROOT.gErrorIgnoreLevel = backup
114  return canvas
115 
116 def _modifyPadForRatio(pad, ratioFactor):
117  pad.Divide(1, 2)
118 
119  divisionPoint = 1-1/ratioFactor
120 
121  topMargin = pad.GetTopMargin()
122  bottomMargin = pad.GetBottomMargin()
123  divisionPoint += (1-divisionPoint)*bottomMargin # correct for (almost-)zeroing bottom margin of pad1
124  divisionPointForPad1 = 1-( (1-divisionPoint) / (1-0.02) ) # then correct for the non-zero bottom margin, but for pad1 only
125 
126  # Set the lower point of the upper pad to divisionPoint
127  pad1 = pad.cd(1)
128  yup = 1.0
129  ylow = divisionPointForPad1
130  xup = 1.0
131  xlow = 0.0
132  pad1.SetPad(xlow, ylow, xup, yup)
133  pad1.SetFillStyle(4000) # transparent
134  pad1.SetBottomMargin(0.02) # need some bottom margin here for eps/pdf output (at least in ROOT 5.34)
135 
136  # Set the upper point of the lower pad to divisionPoint
137  pad2 = pad.cd(2)
138  yup = divisionPoint
139  ylow = 0.0
140  pad2.SetPad(xlow, ylow, xup, yup)
141  pad2.SetFillStyle(4000) # transparent
142  pad2.SetTopMargin(0.0)
143  pad2.SetBottomMargin(bottomMargin/(ratioFactor*divisionPoint))
144 
145 def _calculateRatios(histos, ratioUncertainty=False):
146  """Calculate the ratios for a list of histograms"""
147 
148  def _divideOrZero(numerator, denominator):
149  if denominator == 0:
150  return 0
151  return numerator/denominator
152 
153  def equal(a, b):
154  if a == 0. and b == 0.:
155  return True
156  return abs(a-b)/max(abs(a),abs(b)) < 1e-3
157 
158  def findBins(wrap, bins_xvalues):
159  ret = []
160  currBin = wrap.begin()
161  i = 0
162  while i < len(bins_xvalues) and currBin < wrap.end():
163  (xcenter, xlow, xhigh) = bins_xvalues[i]
164  xlowEdge = xcenter-xlow
165  xupEdge = xcenter+xhigh
166 
167  (curr_center, curr_low, curr_high) = wrap.xvalues(currBin)
168  curr_lowEdge = curr_center-curr_low
169  curr_upEdge = curr_center+curr_high
170 
171  if equal(xlowEdge, curr_lowEdge) and equal(xupEdge, curr_upEdge):
172  ret.append(currBin)
173  currBin += 1
174  i += 1
175  elif curr_upEdge <= xlowEdge:
176  currBin += 1
177  elif curr_lowEdge >= xupEdge:
178  ret.append(None)
179  i += 1
180  else:
181  ret.append(None)
182  currBin += 1
183  i += 1
184  if len(ret) != len(bins_xvalues):
185  ret.extend([None]*( len(bins_xvalues) - len(ret) ))
186  return ret
187 
188  # Define wrappers for TH1/TGraph/TGraph2D to have uniform interface
189  # TODO: having more global wrappers would make some things simpler also elsewhere in the code
190  class WrapTH1:
191  def __init__(self, th1, uncertainty):
192  self._th1 = th1
193  self._uncertainty = uncertainty
194 
195  xaxis = th1.GetXaxis()
196  xaxis_arr = xaxis.GetXbins()
197  if xaxis_arr.GetSize() > 0: # unequal binning
198  lst = [xaxis_arr[i] for i in xrange(0, xaxis_arr.GetSize())]
199  arr = array.array("d", lst)
200  self._ratio = ROOT.TH1F("foo", "foo", xaxis.GetNbins(), arr)
201  else:
202  self._ratio = ROOT.TH1F("foo", "foo", xaxis.GetNbins(), xaxis.GetXmin(), xaxis.GetXmax())
203  _copyStyle(th1, self._ratio)
204  self._ratio.SetStats(0)
205  self._ratio.SetLineColor(ROOT.kBlack)
206  self._ratio.SetLineWidth(1)
207  def draw(self, style=None):
208  st = style
209  if st is None:
210  if self._uncertainty:
211  st = "EP"
212  else:
213  st = "HIST P"
214  self._ratio.Draw("same "+st)
215  def begin(self):
216  return 1
217  def end(self):
218  return self._th1.GetNbinsX()+1
219  def xvalues(self, bin):
220  xval = self._th1.GetBinCenter(bin)
221  xlow = xval-self._th1.GetXaxis().GetBinLowEdge(bin)
222  xhigh = self._th1.GetXaxis().GetBinUpEdge(bin)-xval
223  return (xval, xlow, xhigh)
224  def yvalues(self, bin):
225  yval = self._th1.GetBinContent(bin)
226  yerr = self._th1.GetBinError(bin)
227  return (yval, yerr, yerr)
228  def y(self, bin):
229  return self._th1.GetBinContent(bin)
230  def divide(self, bin, scale):
231  self._ratio.SetBinContent(bin, _divideOrZero(self._th1.GetBinContent(bin), scale))
232  self._ratio.SetBinError(bin, _divideOrZero(self._th1.GetBinError(bin), scale))
233  def makeRatio(self):
234  pass
235  def getRatio(self):
236  return self._ratio
237 
238  class WrapTGraph:
239  def __init__(self, gr, uncertainty):
240  self._gr = gr
241  self._uncertainty = uncertainty
242  self._xvalues = []
243  self._xerrslow = []
244  self._xerrshigh = []
245  self._yvalues = []
246  self._yerrshigh = []
247  self._yerrslow = []
248  def draw(self, style=None):
249  if self._ratio is None:
250  return
251  st = style
252  if st is None:
253  if self._uncertainty:
254  st = "PZ"
255  else:
256  st = "PX"
257  self._ratio.Draw("same "+st)
258  def begin(self):
259  return 0
260  def end(self):
261  return self._gr.GetN()
262  def xvalues(self, bin):
263  return (self._gr.GetX()[bin], self._gr.GetErrorXlow(bin), self._gr.GetErrorXhigh(bin))
264  def yvalues(self, bin):
265  return (self._gr.GetY()[bin], self._gr.GetErrorYlow(bin), self._gr.GetErrorYhigh(bin))
266  def y(self, bin):
267  return self._gr.GetY()[bin]
268  def divide(self, bin, scale):
269  # Ignore bin if denominator is zero
270  if scale == 0:
271  return
272  # No more items in the numerator
273  if bin >= self._gr.GetN():
274  return
275  # denominator is missing an item
276  xvals = self.xvalues(bin)
277  xval = xvals[0]
278 
279  self._xvalues.append(xval)
280  self._xerrslow.append(xvals[1])
281  self._xerrshigh.append(xvals[2])
282  yvals = self.yvalues(bin)
283  self._yvalues.append(yvals[0] / scale)
284  if self._uncertainty:
285  self._yerrslow.append(yvals[1] / scale)
286  self._yerrshigh.append(yvals[2] / scale)
287  else:
288  self._yerrslow.append(0)
289  self._yerrshigh.append(0)
290  def makeRatio(self):
291  if len(self._xvalues) == 0:
292  self._ratio = None
293  return
294  self._ratio = ROOT.TGraphAsymmErrors(len(self._xvalues), array.array("d", self._xvalues), array.array("d", self._yvalues),
295  array.array("d", self._xerrslow), array.array("d", self._xerrshigh),
296  array.array("d", self._yerrslow), array.array("d", self._yerrshigh))
297  _copyStyle(self._gr, self._ratio)
298  def getRatio(self):
299  return self._ratio
300  class WrapTGraph2D(WrapTGraph):
301  def __init__(self, gr, uncertainty):
302  WrapTGraph.__init__(self, gr, uncertainty)
303  def xvalues(self, bin):
304  return (self._gr.GetX()[bin], self._gr.GetErrorX(bin), self._gr.GetErrorX(bin))
305  def yvalues(self, bin):
306  return (self._gr.GetY()[bin], self._gr.GetErrorY(bin), self._gr.GetErrorY(bin))
307 
308  def wrap(o):
309  if isinstance(o, ROOT.TH1):
310  return WrapTH1(o, ratioUncertainty)
311  elif isinstance(o, ROOT.TGraph):
312  return WrapTGraph(o, ratioUncertainty)
313  elif isinstance(o, ROOT.TGraph2D):
314  return WrapTGraph2D(o, ratioUncertainty)
315 
316  wrappers = [wrap(h) for h in histos]
317  ref = wrappers[0]
318 
319  wrappers_bins = []
320  ref_bins = [ref.xvalues(b) for b in xrange(ref.begin(), ref.end())]
321  for w in wrappers:
322  wrappers_bins.append(findBins(w, ref_bins))
323 
324  for i, bin in enumerate(xrange(ref.begin(), ref.end())):
325  (scale, ylow, yhigh) = ref.yvalues(bin)
326  for w, bins in zip(wrappers, wrappers_bins):
327  if bins[i] is None:
328  continue
329  w.divide(bins[i], scale)
330 
331  for w in wrappers:
332  w.makeRatio()
333 
334  return wrappers
335 
336 
337 def _getXmin(obj, limitToNonZeroContent=False):
338  if isinstance(obj, ROOT.TH1):
339  xaxis = obj.GetXaxis()
340  if limitToNonZeroContent:
341  for i in xrange(1, obj.GetNbinsX()+1):
342  if obj.GetBinContent(i) != 0:
343  return xaxis.GetBinLowEdge(i)
344  # None for all bins being zero
345  return None
346  else:
347  return xaxis.GetBinLowEdge(xaxis.GetFirst())
348  elif isinstance(obj, ROOT.TGraph) or isinstance(obj, ROOT.TGraph2D):
349  m = min([obj.GetX()[i] for i in xrange(0, obj.GetN())])
350  return m*0.9 if m > 0 else m*1.1
351  raise Exception("Unsupported type %s" % str(obj))
352 
353 def _getXmax(obj, limitToNonZeroContent=False):
354  if isinstance(obj, ROOT.TH1):
355  xaxis = obj.GetXaxis()
356  if limitToNonZeroContent:
357  for i in xrange(obj.GetNbinsX(), 0, -1):
358  if obj.GetBinContent(i) != 0:
359  return xaxis.GetBinUpEdge(i)
360  # None for all bins being zero
361  return None
362  else:
363  return xaxis.GetBinUpEdge(xaxis.GetLast())
364  elif isinstance(obj, ROOT.TGraph) or isinstance(obj, ROOT.TGraph2D):
365  m = max([obj.GetX()[i] for i in xrange(0, obj.GetN())])
366  return m*1.1 if m > 0 else m*0.9
367  raise Exception("Unsupported type %s" % str(obj))
368 
369 def _getYmin(obj, limitToNonZeroContent=False):
370  if isinstance(obj, ROOT.TH2):
371  yaxis = obj.GetYaxis()
372  return yaxis.GetBinLowEdge(yaxis.GetFirst())
373  elif isinstance(obj, ROOT.TH1):
374  if limitToNonZeroContent:
375  lst = [obj.GetBinContent(i) for i in xrange(1, obj.GetNbinsX()+1) if obj.GetBinContent(i) != 0 ]
376  return min(lst) if len(lst) != 0 else 0
377  else:
378  return obj.GetMinimum()
379  elif isinstance(obj, ROOT.TGraph) or isinstance(obj, ROOT.TGraph2D):
380  m = min([obj.GetY()[i] for i in xrange(0, obj.GetN())])
381  return m*0.9 if m > 0 else m*1.1
382  raise Exception("Unsupported type %s" % str(obj))
383 
384 def _getYmax(obj, limitToNonZeroContent=False):
385  if isinstance(obj, ROOT.TH2):
386  yaxis = obj.GetYaxis()
387  return yaxis.GetBinUpEdge(yaxis.GetLast())
388  elif isinstance(obj, ROOT.TH1):
389  if limitToNonZeroContent:
390  lst = [obj.GetBinContent(i) for i in xrange(1, obj.GetNbinsX()+1) if obj.GetBinContent(i) != 0 ]
391  return max(lst) if len(lst) != 0 else 0
392  else:
393  return obj.GetMaximum()
394  elif isinstance(obj, ROOT.TGraph) or isinstance(obj, ROOT.TGraph2D):
395  m = max([obj.GetY()[i] for i in xrange(0, obj.GetN())])
396  return m*1.1 if m > 0 else m*0.9
397  raise Exception("Unsupported type %s" % str(obj))
398 
400  return max([th1.GetBinContent(i)+th1.GetBinError(i) for i in xrange(1, th1.GetNbinsX()+1)])
401 
403  yvals = filter(lambda n: n>0, [th1.GetBinContent(i) for i in xrange(1, th1.GetNbinsX()+1)])
404  yvals.sort()
405  if len(yvals) == 0:
406  return th1.GetMinimum()
407  if len(yvals) == 1:
408  return yvals[0]
409 
410  # Define outlier as being x10 less than minimum of the 95 % of the non-zero largest values
411  ind_min = len(yvals)-1 - int(len(yvals)*0.95)
412  min_val = yvals[ind_min]
413  for i in xrange(0, ind_min):
414  if yvals[i] > 0.1*min_val:
415  return yvals[i]
416 
417  return min_val
418 
419 def _getYminMaxAroundMedian(obj, coverage, coverageRange=None):
420  inRange = lambda x: True
421  inRange2 = lambda xmin,xmax: True
422  if coverageRange:
423  inRange = lambda x: coverageRange[0] <= x <= coverageRange[1]
424  inRange2 = lambda xmin,xmax: coverageRange[0] <= xmin and xmax <= coverageRange[1]
425 
426  if isinstance(obj, ROOT.TH1):
427  yvals = [obj.GetBinContent(i) for i in xrange(1, obj.GetNbinsX()+1) if inRange2(obj.GetXaxis().GetBinLowEdge(i), obj.GetXaxis().GetBinUpEdge(i))]
428  yvals = filter(lambda x: x != 0, yvals)
429  elif isinstance(obj, ROOT.TGraph) or isinstance(obj, ROOT.TGraph2D):
430  yvals = [obj.GetY()[i] for i in xrange(0, obj.GetN()) if inRange(obj.GetX()[i])]
431  else:
432  raise Exception("Unsupported type %s" % str(obj))
433  if len(yvals) == 0:
434  return (0, 0)
435  if len(yvals) == 1:
436  return (yvals[0], yvals[0])
437  if len(yvals) == 2:
438  return (yvals[0], yvals[1])
439 
440  yvals.sort()
441  nvals = int(len(yvals)*coverage)
442  if nvals < 2:
443  # Take median and +- 1 values
444  if len(yvals) % 2 == 0:
445  half = len(yvals)/2
446  return ( yvals[half-1], yvals[half] )
447  else:
448  middle = len(yvals)/2
449  return ( yvals[middle-1], yvals[middle+1] )
450  ind_min = (len(yvals)-nvals)/2
451  ind_max = len(yvals)-1 - ind_min
452 
453  return (yvals[ind_min], yvals[ind_max])
454 
455 def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None):
456  """Find x-y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
457 
458  Arguments:
459  th1s -- List of TH1s
460  ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
461 
462  Keyword arguments:
463  xmin -- Minimum x value; if None, take the minimum of TH1s
464  xmax -- Maximum x value; if None, take the maximum of TH1s
465  ymin -- Minimum y value; if None, take the minimum of TH1s
466  ymax -- Maximum y value; if None, take the maximum of TH1s
467  """
468 
469  (ymin, ymax) = _findBoundsY(th1s, ylog, ymin, ymax)
470 
471  if xmin is None or xmax is None or isinstance(xmin, list) or isinstance(max, list):
472  xmins = []
473  xmaxs = []
474  for th1 in th1s:
475  xmins.append(_getXmin(th1, limitToNonZeroContent=isinstance(xmin, list)))
476  xmaxs.append(_getXmax(th1, limitToNonZeroContent=isinstance(xmax, list)))
477 
478  # Filter out cases where histograms have zero content
479  xmins = filter(lambda h: h is not None, xmins)
480  xmaxs = filter(lambda h: h is not None, xmaxs)
481 
482  if xmin is None:
483  xmin = min(xmins)
484  elif isinstance(xmin, list):
485  if len(xmins) == 0: # all histograms zero
486  xmin = min(xmin)
487  if verbose:
488  print "Histogram is zero, using the smallest given value for xmin from", str(xmin)
489  else:
490  xm = min(xmins)
491  xmins_below = filter(lambda x: x<=xm, xmin)
492  if len(xmins_below) == 0:
493  xmin = min(xmin)
494  if xm < xmin:
495  if verbose:
496  print "Histogram minimum x %f is below all given xmin values %s, using the smallest one" % (xm, str(xmin))
497  else:
498  xmin = max(xmins_below)
499 
500  if xmax is None:
501  xmax = max(xmaxs)
502  elif isinstance(xmax, list):
503  if len(xmaxs) == 0: # all histograms zero
504  xmax = max(xmax)
505  if verbose:
506  print "Histogram is zero, using the smallest given value for xmax from", str(xmin)
507  else:
508  xm = max(xmaxs)
509  xmaxs_above = filter(lambda x: x>xm, xmax)
510  if len(xmaxs_above) == 0:
511  xmax = max(xmax)
512  if xm > xmax:
513  if verbose:
514  print "Histogram maximum x %f is above all given xmax values %s, using the maximum one" % (xm, str(xmax))
515  else:
516  xmax = min(xmaxs_above)
517 
518  for th1 in th1s:
519  th1.GetXaxis().SetRangeUser(xmin, xmax)
520 
521  return (xmin, ymin, xmax, ymax)
522 
523 def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None):
524  """Find y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
525 
526  Arguments:
527  th1s -- List of TH1s
528  ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
529 
530  Keyword arguments:
531  ymin -- Minimum y value; if None, take the minimum of TH1s
532  ymax -- Maximum y value; if None, take the maximum of TH1s
533  coverage -- If set, use only values within the 'coverage' part around the median are used for min/max (useful for ratio)
534  coverageRange -- If coverage and this are set, use only the x axis specified by an (xmin,xmax) pair for the coverage
535  """
536  if coverage is not None or isinstance(th1s[0], ROOT.TH2):
537  # the only use case for coverage for now is ratio, for which
538  # the scalings are not needed (actually harmful), so let's
539  # just ignore them if 'coverage' is set
540  #
541  # Also for TH2 do not adjust automatic y bounds
542  y_scale_max = lambda y: y
543  y_scale_min = lambda y: y
544  else:
545  if ylog:
546  y_scale_max = lambda y: y*1.5
547  else:
548  y_scale_max = lambda y: y*1.05
549  y_scale_min = lambda y: y*0.9 # assuming log
550 
551  if ymin is None or ymax is None or isinstance(ymin, list) or isinstance(ymax, list):
552  ymins = []
553  ymaxs = []
554  for th1 in th1s:
555  if coverage is not None:
556  (_ymin, _ymax) = _getYminMaxAroundMedian(th1, coverage, coverageRange)
557  else:
558  if ylog and isinstance(ymin, list):
559  _ymin = _getYminIgnoreOutlier(th1)
560  else:
561  _ymin = _getYmin(th1, limitToNonZeroContent=isinstance(ymin, list))
562  _ymax = _getYmax(th1, limitToNonZeroContent=isinstance(ymax, list))
563 # _ymax = _getYmaxWithError(th1)
564 
565  ymins.append(_ymin)
566  ymaxs.append(_ymax)
567 
568  if ymin is None:
569  ymin = min(ymins)
570  elif isinstance(ymin, list):
571  ym_unscaled = min(ymins)
572  ym = y_scale_min(ym_unscaled)
573  ymins_below = filter(lambda y: y<=ym, ymin)
574  if len(ymins_below) == 0:
575  ymin = min(ymin)
576  if ym_unscaled < ymin:
577  if verbose:
578  print "Histogram minimum y %f is below all given ymin values %s, using the smallest one" % (ym, str(ymin))
579  else:
580  ymin = max(ymins_below)
581 
582  if ymax is None:
583  # in case ymax is automatic, ymin is set by list, and the
584  # histograms are zero, ensure here that ymax > ymin
585  ymax = y_scale_max(max(ymaxs+[ymin]))
586  elif isinstance(ymax, list):
587  ym_unscaled = max(ymaxs)
588  ym = y_scale_max(ym_unscaled)
589  ymaxs_above = filter(lambda y: y>ym, ymax)
590  if len(ymaxs_above) == 0:
591  ymax = max(ymax)
592  if ym_unscaled > ymax:
593  if verbose:
594  print "Histogram maximum y %f is above all given ymax values %s, using the maximum one" % (ym_unscaled, str(ymax))
595  else:
596  ymax = min(ymaxs_above)
597 
598  for th1 in th1s:
599  th1.GetYaxis().SetRangeUser(ymin, ymax)
600 
601  return (ymin, ymax)
602 
603 def _th1RemoveEmptyBins(histos, xbinlabels):
604  binsToRemove = set()
605  for b in xrange(1, histos[0].GetNbinsX()+1):
606  binEmpty = True
607  for h in histos:
608  if h.GetBinContent(b) > 0:
609  binEmpty = False
610  break
611  if binEmpty:
612  binsToRemove.add(b)
613 
614  if len(binsToRemove) > 0:
615  # filter xbinlabels
616  xbinlab_new = []
617  for i in xrange(len(xbinlabels)):
618  if (i+1) not in binsToRemove:
619  xbinlab_new.append(xbinlabels[i])
620  xbinlabels = xbinlab_new
621 
622  # filter histogram bins
623  histos_new = []
624  for h in histos:
625  values = []
626  for b in xrange(1, h.GetNbinsX()+1):
627  if b not in binsToRemove:
628  values.append( (h.GetXaxis().GetBinLabel(b), h.GetBinContent(b), h.GetBinError(b)) )
629 
630  if len(values) > 0:
631  h_new = h.Clone(h.GetName()+"_empty")
632  h_new.SetBins(len(values), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(values))
633  for b, (l, v, e) in enumerate(values):
634  h_new.GetXaxis().SetBinLabel(b+1, l)
635  h_new.SetBinContent(b+1, v)
636  h_new.SetBinError(b+1, e)
637 
638  histos_new.append(h_new)
639  histos = histos_new
640 
641  return (histos, xbinlabels)
642 
643 def _th2RemoveEmptyBins(histos, xbinlabels, ybinlabels):
644  xbinsToRemove = set()
645  ybinsToRemove = set()
646  for ih, h in enumerate(histos):
647  for bx in xrange(1, h.GetNbinsX()+1):
648  binEmpty = True
649  for by in xrange(1, h.GetNbinsY()+1):
650  if h.GetBinContent(bx, by) > 0:
651  binEmpty = False
652  break
653  if binEmpty:
654  xbinsToRemove.add(bx)
655  elif ih > 0:
656  xbinsToRemove.discard(bx)
657 
658  for by in xrange(1, h.GetNbinsY()+1):
659  binEmpty = True
660  for bx in xrange(1, h.GetNbinsX()+1):
661  if h.GetBinContent(bx, by) > 0:
662  binEmpty = False
663  break
664  if binEmpty:
665  ybinsToRemove.add(by)
666  elif ih > 0:
667  ybinsToRemove.discard(by)
668 
669  if len(xbinsToRemove) > 0 or len(ybinsToRemove) > 0:
670  xbinlabels_new = []
671  xbins = []
672  for b in xrange(1, len(xbinlabels)+1):
673  if b not in xbinsToRemove:
674  xbinlabels_new.append(histos[0].GetXaxis().GetBinLabel(b))
675  xbins.append(b)
676  xbinlabels = xbinlabels_new
677  ybinlabels_new = []
678  ybins = []
679  for b in xrange(1, len(ybinlabels)+1):
680  if b not in ybinsToRemove:
681  ybinlabels.append(histos[0].GetYaxis().GetBinLabel(b))
682  ybins.append(b)
683  ybinlabels = xbinlabels_new
684 
685  histos_new = []
686  if len(xbinlabels) == 0 or len(ybinlabels) == 0:
687  return (histos_new, xbinlabels, ybinlabels)
688  for h in histos:
689  h_new = ROOT.TH2F(h.GetName()+"_empty", h.GetTitle(), len(xbinlabels),0,len(xbinlabels), len(ybinlabels),0,len(ybinlabels))
690  for b, l in enumerate(xbinlabels):
691  h_new.GetXaxis().SetBinLabel(b+1, l)
692  for b, l in enumerate(ybinlabels):
693  h_new.GetYaxis().SetBinLabel(b+1, l)
694 
695  for ix, bx in enumerate(xbins):
696  for iy, by in enumerate(ybins):
697  h_new.SetBinContent(ix+1, iy+1, h.GetBinContent(bx, by))
698  h_new.SetBinError(ix+1, iy+1, h.GetBinError(bx, by))
699  histos_new.append(h_new)
700  histos = histos_new
701  return (histos, xbinlabels, ybinlabels)
702 
703 def _mergeBinLabelsX(histos):
704  return _mergeBinLabels([[h.GetXaxis().GetBinLabel(i) for i in xrange(1, h.GetNbinsX()+1)] for h in histos])
705 
706 def _mergeBinLabelsY(histos):
707  return _mergeBinLabels([[h.GetYaxis().GetBinLabel(i) for i in xrange(1, h.GetNbinsY()+1)] for h in histos])
708 
709 def _mergeBinLabels(labelsAll):
710  labels_merged = labelsAll[0]
711  for labels in labelsAll[1:]:
712  diff = difflib.unified_diff(labels_merged, labels, n=max(len(labels_merged), len(labels)))
713  labels_merged = []
714  operation = []
715  for item in diff: # skip the "header" lines
716  if item[:2] == "@@":
717  break
718  for item in diff:
719  operation.append(item[0])
720  lab = item[1:]
721  if lab in labels_merged:
722  # pick the last addition of the bin
723  ind = labels_merged.index(lab)
724  if operation[ind] == "-" and operation[-1] == "+":
725  labels_merged.remove(lab)
726  del operation[ind] # to keep xbinlabels and operation indices in sync
727  elif operation[ind] == "+" and operation[-1] == "-":
728  del operation[-1] # to keep xbinlabels and operation indices in sync
729  continue
730  else:
731  raise Exception("This should never happen")
732  labels_merged.append(lab)
733  # unified_diff returns empty diff if labels_merged and labels are equal
734  # so if labels_merged is empty here, it can be just set to labels
735  if len(labels_merged) == 0:
736  labels_merged = labels
737 
738  return labels_merged
739 
740 def _th1IncludeOnlyBins(histos, xbinlabels):
741  histos_new = []
742  for h in histos:
743  h_new = h.Clone(h.GetName()+"_xbinlabels")
744  h_new.SetBins(len(xbinlabels), h.GetBinLowEdge(1), h.GetBinLowEdge(1)+len(xbinlabels))
745  for i, label in enumerate(xbinlabels):
746  bin = h.GetXaxis().FindFixBin(label)
747  if bin >= 0:
748  h_new.SetBinContent(i+1, h.GetBinContent(bin))
749  h_new.SetBinError(i+1, h.GetBinError(bin))
750  else:
751  h_new.SetBinContent(i+1, 0)
752  h_new.SetBinError(i+1, 0)
753  histos_new.append(h_new)
754  return histos_new
755 
756 
757 class Subtract:
758  """Class for subtracting two histograms"""
759  def __init__(self, name, nameA, nameB, title=""):
760  """Constructor
761 
762  Arguments:
763  name -- String for name of the resulting histogram (A-B)
764  nameA -- String for A histogram
765  nameB -- String for B histogram
766 
767  Keyword arguments:
768  title -- String for a title of the resulting histogram (default "")
769 
770  Uncertainties are calculated with the assumption that B is a
771  subset of A, and the histograms contain event counts.
772  """
773  self._name = name
774  self._nameA = nameA
775  self._nameB = nameB
776  self._title = title
777 
778  def __str__(self):
779  """String representation, returns the name"""
780  return self._name
781 
782  def create(self, tdirectory):
783  """Create and return the fake+duplicate histogram from a TDirectory"""
784  histoA = _getObject(tdirectory, self._nameA)
785  histoB = _getObject(tdirectory, self._nameB)
786 
787  if not histoA or not histoB:
788  return None
789 
790  ret = histoA.Clone(self._name)
791  ret.SetTitle(self._title)
792 
793  # Disable canExtend if it is set, otherwise setting the
794  # overflow bin will extend instead, possibly causing weird
795  # effects downstream
796  ret.SetCanExtend(False)
797 
798  for i in xrange(0, histoA.GetNbinsX()+2): # include under- and overflow too
799  val = histoA.GetBinContent(i)-histoB.GetBinContent(i)
800  ret.SetBinContent(i, val)
801  ret.SetBinError(i, math.sqrt(val))
802 
803  return ret
804 
805 class Transform:
806  """Class to transform bin contents in an arbitrary way."""
807  def __init__(self, name, histo, func, title=""):
808  """Constructor.
809 
810  Argument:
811  name -- String for name of the resulting histogram
812  histo -- String for a source histogram (needs to be cumulative)
813  func -- Function to operate on the bin content
814  """
815  self._name = name
816  self._histo = histo
817  self._func = func
818  self._title = title
819 
820  def __str__(self):
821  """String representation, returns the name"""
822  return self._name
823 
824  def create(self, tdirectory):
825  """Create and return the transformed histogram from a TDirectory"""
826  histo = _getOrCreateObject(tdirectory, self._histo)
827  if not histo:
828  return None
829 
830  ret = histo.Clone(self._name)
831  ret.SetTitle(self._title)
832 
833  # Disable canExtend if it is set, otherwise setting the
834  # overflow bin will extend instead, possibly causing weird
835  # effects downstream
836  ret.SetCanExtend(False)
837 
838  for i in xrange(0, histo.GetNbinsX()+2):
839  ret.SetBinContent(i, self._func(histo.GetBinContent(i)))
840  return ret
841 
843  """Class to calculate the fake+duplicate rate"""
844  def __init__(self, name, assoc, dup, reco, title=""):
845  """Constructor.
846 
847  Arguments:
848  name -- String for the name of the resulting efficiency histogram
849  assoc -- String for the name of the "associated" histogram
850  dup -- String for the name of the "duplicates" histogram
851  reco -- String for the name of the "reco" (denominator) histogram
852 
853  Keyword arguments:
854  title -- String for a title of the resulting histogram (default "")
855 
856  The result is calculated as 1 - (assoc - dup) / reco
857  """
858  self._name = name
859  self._assoc = assoc
860  self._dup = dup
861  self._reco = reco
862  self._title = title
863 
864  def __str__(self):
865  """String representation, returns the name"""
866  return self._name
867 
868  def create(self, tdirectory):
869  """Create and return the fake+duplicate histogram from a TDirectory"""
870  # Get the numerator/denominator histograms
871  hassoc = _getObject(tdirectory, self._assoc)
872  hdup = _getObject(tdirectory, self._dup)
873  hreco = _getObject(tdirectory, self._reco)
874 
875  # Skip if any of them does not exist
876  if not hassoc or not hdup or not hreco:
877  return None
878 
879  hfakedup = hreco.Clone(self._name)
880  hfakedup.SetTitle(self._title)
881 
882  for i in xrange(1, hassoc.GetNbinsX()+1):
883  numerVal = hassoc.GetBinContent(i) - hdup.GetBinContent(i)
884  denomVal = hreco.GetBinContent(i)
885 
886  fakedupVal = (1 - numerVal / denomVal) if denomVal != 0.0 else 0.0
887  errVal = math.sqrt(fakedupVal*(1-fakedupVal)/denomVal) if (denomVal != 0.0 and fakedupVal <= 1) else 0.0
888 
889  hfakedup.SetBinContent(i, fakedupVal)
890  hfakedup.SetBinError(i, errVal)
891 
892  return hfakedup
893 
895  """Class for making a cut efficiency histograms.
896 
897  N after cut
898  eff = -----------
899  N total
900  """
901  def __init__(self, name, histo, title=""):
902  """Constructor
903 
904  Arguments:
905  name -- String for name of the resulting histogram
906  histo -- String for a source histogram (needs to be cumulative)
907  """
908  self._name = name
909  self._histo = histo
910  self._title = title
911 
912  def __str__(self):
913  """String representation, returns the name"""
914  return self._name
915 
916  def create(self, tdirectory):
917  """Create and return the cut efficiency histogram from a TDirectory"""
918  histo = _getOrCreateObject(tdirectory, self._histo)
919  if not histo:
920  return None
921 
922  # infer cumulative direction from the under/overflow bins
923  ascending = histo.GetBinContent(0) < histo.GetBinContent(histo.GetNbinsX())
924  if ascending:
925  n_tot = histo.GetBinContent(histo.GetNbinsX())
926  else:
927  n_tot = histo.GetBinContent(0)
928 
929  if n_tot == 0:
930  return histo
931 
932  ret = histo.Clone(self._name)
933  ret.SetTitle(self._title)
934 
935  # calculate efficiency
936  for i in xrange(1, histo.GetNbinsX()+1):
937  n = histo.GetBinContent(i)
938  val = n/n_tot
939  errVal = math.sqrt(val*(1-val)/n_tot)
940  ret.SetBinContent(i, val)
941  ret.SetBinError(i, errVal)
942  return ret
943 
945  """Class to create a histogram by aggregating bins of another histogram to a bin of the resulting histogram."""
946  def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False, reorder=None):
947  """Constructor.
948 
949  Arguments:
950  name -- String for the name of the resulting histogram
951  histoName -- String for the name of the source histogram
952  mapping -- Dictionary for mapping the bins (see below)
953 
954  Keyword arguments:
955  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.
956  scale -- Optional number for scaling the histogram (passed to ROOT.TH1.Scale())
957  renameBin -- Optional function (string -> string) to rename the bins of the input histogram
958  originalOrder -- Boolean for using the order of bins in the histogram (default False)
959  reorder -- Optional function to reorder the bins
960 
961  Mapping structure (mapping):
962 
963  Dictionary (you probably want to use collections.OrderedDict)
964  should be a mapping from the destination bin label to a list
965  of source bin labels ("dst -> [src]").
966  """
967  self._name = name
968  self._histoName = histoName
969  self._mapping = mapping
970  self._normalizeTo = normalizeTo
971  self._scale = scale
972  self._renameBin = renameBin
973  self._ignoreMissingBins = ignoreMissingBins
974  self._minExistingBins = minExistingBins
975  self._originalOrder = originalOrder
976  self._reorder = reorder
977  if self._originalOrder and self._reorder is not None:
978  raise Exception("reorder is not None and originalOrder is True, please set only one of them")
979 
980  def __str__(self):
981  """String representation, returns the name"""
982  return self._name
983 
984  def create(self, tdirectory):
985  """Create and return the histogram from a TDirectory"""
986  th1 = _getOrCreateObject(tdirectory, self._histoName)
987  if th1 is None:
988  return None
989 
990  binLabels = [""]*len(self._mapping)
991  binValues = [None]*len(self._mapping)
992 
993  # TH1 can't really be used as a map/dict, so convert it here:
994  values = _th1ToOrderedDict(th1, self._renameBin)
995 
996  binIndexOrder = [] # for reordering bins if self._originalOrder is True
997  for i, (key, labels) in enumerate(self._mapping.iteritems()):
998  sumTime = 0.
999  sumErrorSq = 0.
1000  nsum = 0
1001  for l in labels:
1002  try:
1003  sumTime += values[l][0]
1004  sumErrorSq += values[l][1]**2
1005  nsum += 1
1006  except KeyError:
1007  pass
1008 
1009  if nsum > 0:
1010  binValues[i] = (sumTime, math.sqrt(sumErrorSq))
1011  binLabels[i] = key
1012 
1013  ivalue = len(values)+1
1014  if len(labels) > 0:
1015  # first label doesn't necessarily exist (especially for
1016  # the iteration timing plots), so let's test them all
1017  for lab in labels:
1018  if lab in values:
1019  ivalue = values.keys().index(lab)
1020  break
1021  binIndexOrder.append( (ivalue, i) )
1022 
1023  if self._originalOrder:
1024  binIndexOrder.sort(key=lambda t: t[0])
1025  tmpVal = []
1026  tmpLab = []
1027  for i in xrange(0, len(binValues)):
1028  fromIndex = binIndexOrder[i][1]
1029  tmpVal.append(binValues[fromIndex])
1030  tmpLab.append(binLabels[fromIndex])
1031  binValues = tmpVal
1032  binLabels = tmpLab
1033  if self._reorder is not None:
1034  order = self._reorder(tdirectory, binLabels)
1035  binValues = [binValues[i] for i in order]
1036  binLabels = [binLabels[i] for i in order]
1037 
1038  if self._minExistingBins is not None and (len(binValues)-binValues.count(None)) < self._minExistingBins:
1039  return None
1040 
1041  if self._ignoreMissingBins:
1042  for i, val in enumerate(binValues):
1043  if val is None:
1044  binLabels[i] = None
1045  binValues = filter(lambda v: v is not None, binValues)
1046  binLabels = filter(lambda v: v is not None, binLabels)
1047  if len(binValues) == 0:
1048  return None
1049 
1050  result = ROOT.TH1F(self._name, self._name, len(binValues), 0, len(binValues))
1051  for i, (value, label) in enumerate(zip(binValues, binLabels)):
1052  if value is not None:
1053  result.SetBinContent(i+1, value[0])
1054  result.SetBinError(i+1, value[1])
1055  result.GetXaxis().SetBinLabel(i+1, label)
1056 
1057  if self._normalizeTo is not None:
1058  bin = th1.GetXaxis().FindBin(self._normalizeTo)
1059  if bin <= 0:
1060  print "Trying to normalize {name} to {binlabel}, which does not exist".format(name=self._name, binlabel=self._normalizeTo)
1061  sys.exit(1)
1062  value = th1.GetBinContent(bin)
1063  if value != 0:
1064  result.Scale(1/value)
1065 
1066  if self._scale is not None:
1067  result.Scale(self._scale)
1068 
1069  return result
1070 
1072  """Class to create a histogram by aggregaging integrals of another histoggrams."""
1073  def __init__(self, name, mapping, normalizeTo=None):
1074  """Constructor.
1075 
1076  Arguments:
1077  name -- String for the name of the resulting histogram
1078  mapping -- Dictionary for mapping the bin label to a histogram name
1079 
1080  Keyword arguments:
1081  normalizeTo -- Optional string for a histogram. If given, all bins of the resulting histograqm are divided by the integral of this histogram.
1082  """
1083  self._name = name
1084  self._mapping = mapping
1085  self._normalizeTo = normalizeTo
1086 
1087  def __str__(self):
1088  """String representation, returns the name"""
1089  return self._name
1090 
1091  def create(self, tdirectory):
1092  """Create and return the histogram from a TDirectory"""
1093  result = []
1094  for key, histoName in self._mapping.iteritems():
1095  th1 = _getObject(tdirectory, histoName)
1096  if th1 is None:
1097  continue
1098  result.append( (key, th1.Integral(0, th1.GetNbinsX()+1)) ) # include under- and overflow bins
1099  if len(result) == 0:
1100  return None
1101 
1102  res = ROOT.TH1F(self._name, self._name, len(result), 0, len(result))
1103 
1104  for i, (name, count) in enumerate(result):
1105  res.SetBinContent(i+1, count)
1106  res.GetXaxis().SetBinLabel(i+1, name)
1107 
1108  if self._normalizeTo is not None:
1109  th1 = _getObject(tdirectory, self._normalizeTo)
1110  if th1 is None:
1111  return None
1112  scale = th1.Integral(0, th1.GetNbinsX()+1)
1113  res.Scale(1/scale)
1114 
1115  return res
1116 
1117 class ROC:
1118  """Class to construct a ROC curve (e.g. efficiency vs. fake rate) from two histograms"""
1119  def __init__(self, name, xhistoName, yhistoName, zaxis=False):
1120  """Constructor.
1121 
1122  Arguments:
1123  name -- String for the name of the resulting histogram
1124  xhistoName -- String for the name of the x-axis histogram (or another "creator" object)
1125  yhistoName -- String for the name of the y-axis histogram (or another "creator" object)
1126 
1127  Keyword arguments:
1128  zaxis -- If set to True (default False), create a TGraph2D with z axis showing the cut value (recommended drawStyle 'pcolz')
1129  """
1130  self._name = name
1131  self._xhistoName = xhistoName
1132  self._yhistoName = yhistoName
1133  self._zaxis = zaxis
1134 
1135  def __str__(self):
1136  """String representation, returns the name"""
1137  return self._name
1138 
1139  def create(self, tdirectory):
1140  """Create and return the histogram from a TDirectory"""
1141  xhisto = _getOrCreateObject(tdirectory, self._xhistoName)
1142  yhisto = _getOrCreateObject(tdirectory, self._yhistoName);
1143  if xhisto is None or yhisto is None:
1144  return None
1145 
1146  x = []
1147  xerrup = []
1148  xerrdown = []
1149  y = []
1150  yerrup = []
1151  yerrdown = []
1152  z = []
1153 
1154  for i in xrange(1, xhisto.GetNbinsX()+1):
1155  x.append(xhisto.GetBinContent(i))
1156  xerrup.append(xhisto.GetBinError(i))
1157  xerrdown.append(xhisto.GetBinError(i))
1158 
1159  y.append(yhisto.GetBinContent(i))
1160  yerrup.append(yhisto.GetBinError(i))
1161  yerrdown.append(yhisto.GetBinError(i))
1162 
1163  z.append(xhisto.GetXaxis().GetBinUpEdge(i))
1164 
1165  # If either axis has only zeroes, no graph makes no point
1166  if x.count(0.0) == len(x) or y.count(0.0) == len(y):
1167  return None
1168 
1169  arr = lambda v: array.array("d", v)
1170  gr = None
1171  if self._zaxis:
1172  gr = ROOT.TGraph2D(len(x), arr(x), arr(y), arr(z))
1173  else:
1174  gr = ROOT.TGraphAsymmErrors(len(x), arr(x), arr(y), arr(xerrdown), arr(xerrup), arr(yerrdown), arr(yerrup))
1175  gr.SetTitle("")
1176  return gr
1177 
1178 
1179 # Plot styles
1180 _plotStylesColor = [4, 2, ROOT.kBlack, ROOT.kOrange+7, ROOT.kMagenta-3]
1181 _plotStylesMarker = [21, 20, 22, 34, 33]
1182 
1183 def _drawFrame(pad, bounds, zmax=None, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None, suffix=""):
1184  """Function to draw a frame
1185 
1186  Arguments:
1187  pad -- TPad to where the frame is drawn
1188  bounds -- List or 4-tuple for (xmin, ymin, xmax, ymax)
1189 
1190  Keyword arguments:
1191  zmax -- Maximum Z, needed for TH2 histograms
1192  xbinlabels -- Optional list of strings for x axis bin labels
1193  xbinlabelsize -- Optional number for the x axis bin label size
1194  xbinlabeloption -- Optional string for the x axis bin options (passed to ROOT.TH1.LabelsOption())
1195  suffix -- Optional string for a postfix of the frame name
1196  """
1197  if xbinlabels is None and ybinlabels is None:
1198  frame = pad.DrawFrame(*bounds)
1199  else:
1200  # Special form needed if want to set x axis bin labels
1201  nbins = len(xbinlabels)
1202  if ybinlabels is None:
1203  frame = ROOT.TH1F("hframe"+suffix, "", nbins, bounds[0], bounds[2])
1204  frame.SetMinimum(bounds[1])
1205  frame.SetMaximum(bounds[3])
1206  frame.GetYaxis().SetLimits(bounds[1], bounds[3])
1207  else:
1208  ybins = len(ybinlabels)
1209  frame = ROOT.TH2F("hframe"+suffix, "", nbins,bounds[0],bounds[2], ybins,bounds[1],bounds[3])
1210  frame.SetMaximum(zmax)
1211 
1212  frame.SetBit(ROOT.TH1.kNoStats)
1213  frame.SetBit(ROOT.kCanDelete)
1214  frame.Draw("")
1215 
1216  xaxis = frame.GetXaxis()
1217  for i in xrange(nbins):
1218  xaxis.SetBinLabel(i+1, xbinlabels[i])
1219  if xbinlabelsize is not None:
1220  xaxis.SetLabelSize(xbinlabelsize)
1221  if xbinlabeloption is not None:
1222  frame.LabelsOption(xbinlabeloption)
1223 
1224  if ybinlabels is not None:
1225  yaxis = frame.GetYaxis()
1226  for i, lab in enumerate(ybinlabels):
1227  yaxis.SetBinLabel(i+1, lab)
1228  if xbinlabelsize is not None:
1229  yaxis.SetLabelSize(xbinlabelsize)
1230  if xbinlabeloption is not None:
1231  frame.LabelsOption(xbinlabeloption, "Y")
1232 
1233  return frame
1234 
1235 class Frame:
1236  """Class for creating and managing a frame for a simple, one-pad plot"""
1237  def __init__(self, pad, bounds, zmax, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None):
1238  self._pad = pad
1239  self._frame = _drawFrame(pad, bounds, zmax, xbinlabels, xbinlabelsize, xbinlabeloption, ybinlabels)
1240 
1241  yoffsetFactor = 1
1242  xoffsetFactor = 1
1243  if nrows == 2:
1244  yoffsetFactor *= 2
1245  xoffsetFactor *= 2
1246  elif nrows >= 5:
1247  yoffsetFactor *= 1.5
1248  xoffsetFactor *= 1.5
1249  elif nrows >= 3:
1250  yoffsetFactor *= 4
1251  xoffsetFactor *= 3
1252 
1253  self._frame.GetYaxis().SetTitleOffset(self._frame.GetYaxis().GetTitleOffset()*yoffsetFactor)
1254  self._frame.GetXaxis().SetTitleOffset(self._frame.GetXaxis().GetTitleOffset()*xoffsetFactor)
1255 
1256 
1257  def setLogx(self, log):
1258  self._pad.SetLogx(log)
1259 
1260  def setLogy(self, log):
1261  self._pad.SetLogy(log)
1262 
1263  def setGridx(self, grid):
1264  self._pad.SetGridx(grid)
1265 
1266  def setGridy(self, grid):
1267  self._pad.SetGridy(grid)
1268 
1269  def adjustMarginLeft(self, adjust):
1270  self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1271  # Need to redraw frame after adjusting the margin
1272  self._pad.cd()
1273  self._frame.Draw("")
1274 
1275  def adjustMarginRight(self, adjust):
1276  self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1277  # Need to redraw frame after adjusting the margin
1278  self._pad.cd()
1279  self._frame.Draw("")
1280 
1281  def setTitle(self, title):
1282  self._frame.SetTitle(title)
1283 
1284  def setXTitle(self, title):
1285  self._frame.GetXaxis().SetTitle(title)
1286 
1287  def setXTitleSize(self, size):
1288  self._frame.GetXaxis().SetTitleSize(size)
1289 
1290  def setXTitleOffset(self, offset):
1291  self._frame.GetXaxis().SetTitleOffset(offset)
1292 
1293  def setXLabelSize(self, size):
1294  self._frame.GetXaxis().SetLabelSize(size)
1295 
1296  def setYTitle(self, title):
1297  self._frame.GetYaxis().SetTitle(title)
1298 
1299  def setYTitleSize(self, size):
1300  self._frame.GetYaxis().SetTitleSize(size)
1301 
1302  def setYTitleOffset(self, offset):
1303  self._frame.GetYaxis().SetTitleOffset(offset)
1304 
1305  def redrawAxis(self):
1306  self._pad.RedrawAxis()
1307 
1309  """Class for creating and managing a frame for a ratio plot with two subpads"""
1310  def __init__(self, pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ratioYTitle=_ratioYTitle):
1311  self._parentPad = pad
1312  self._pad = pad.cd(1)
1313  if xbinlabels is not None:
1314  self._frame = _drawFrame(self._pad, bounds, zmax, [""]*len(xbinlabels))
1315  else:
1316  self._frame = _drawFrame(self._pad, bounds, zmax)
1317  self._padRatio = pad.cd(2)
1318  self._frameRatio = _drawFrame(self._padRatio, ratioBounds, zmax, xbinlabels, xbinlabelsize, xbinlabeloption)
1319 
1320  self._frame.GetXaxis().SetLabelSize(0)
1321  self._frame.GetXaxis().SetTitleSize(0)
1322 
1323  yoffsetFactor = ratioFactor
1324  divisionPoint = 1-1/ratioFactor
1325  xoffsetFactor = 1/divisionPoint #* 0.6
1326 
1327  if nrows == 1:
1328  xoffsetFactor *= 0.6
1329  elif nrows == 2:
1330  yoffsetFactor *= 2
1331  xoffsetFactor *= 1.5
1332  elif nrows == 3:
1333  yoffsetFactor *= 4
1334  xoffsetFactor *= 2.3
1335  elif nrows >= 4:
1336  yoffsetFactor *= 5
1337  xoffsetFactor *= 3
1338 
1339  self._frame.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1340  self._frameRatio.GetYaxis().SetLabelSize(int(self._frameRatio.GetYaxis().GetLabelSize()*0.8))
1341  self._frameRatio.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
1342  self._frameRatio.GetXaxis().SetTitleOffset(self._frameRatio.GetXaxis().GetTitleOffset()*xoffsetFactor)
1343 
1344  self._frameRatio.GetYaxis().SetNdivisions(4, 5, 0)
1345 
1346  self._frameRatio.GetYaxis().SetTitle(ratioYTitle)
1347 
1348  def setLogx(self, log):
1349  self._pad.SetLogx(log)
1350  self._padRatio.SetLogx(log)
1351 
1352  def setLogy(self, log):
1353  self._pad.SetLogy(log)
1354 
1355  def setGridx(self, grid):
1356  self._pad.SetGridx(grid)
1357  self._padRatio.SetGridx(grid)
1358 
1359  def setGridy(self, grid):
1360  self._pad.SetGridy(grid)
1361  self._padRatio.SetGridy(grid)
1362 
1363  def adjustMarginLeft(self, adjust):
1364  self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1365  self._padRatio.SetLeftMargin(self._padRatio.GetLeftMargin()+adjust)
1366  # Need to redraw frame after adjusting the margin
1367  self._pad.cd()
1368  self._frame.Draw("")
1369  self._padRatio.cd()
1370  self._frameRatio.Draw("")
1371 
1372  def adjustMarginRight(self, adjust):
1373  self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1374  self._padRatio.SetRightMargin(self._padRatio.GetRightMargin()+adjust)
1375  # Need to redraw frames after adjusting the margin
1376  self._pad.cd()
1377  self._frame.Draw("")
1378  self._padRatio.cd()
1379  self._frameRatio.Draw("")
1380 
1381  def setTitle(self, title):
1382  self._frame.SetTitle(title)
1383 
1384  def setXTitle(self, title):
1385  self._frameRatio.GetXaxis().SetTitle(title)
1386 
1387  def setXTitleSize(self, size):
1388  self._frameRatio.GetXaxis().SetTitleSize(size)
1389 
1390  def setXTitleOffset(self, offset):
1391  self._frameRatio.GetXaxis().SetTitleOffset(offset)
1392 
1393  def setXLabelSize(self, size):
1394  self._frameRatio.GetXaxis().SetLabelSize(size)
1395 
1396  def setYTitle(self, title):
1397  self._frame.GetYaxis().SetTitle(title)
1398 
1399  def setYTitleRatio(self, title):
1400  self._frameRatio.GetYaxis().SetTitle(title)
1401 
1402  def setYTitleSize(self, size):
1403  self._frame.GetYaxis().SetTitleSize(size)
1404  self._frameRatio.GetYaxis().SetTitleSize(size)
1405 
1406  def setYTitleOffset(self, offset):
1407  self._frame.GetYaxis().SetTitleOffset(offset)
1408  self._frameRatio.GetYaxis().SetTitleOffset(offset)
1409 
1410  def redrawAxis(self):
1411  self._padRatio.RedrawAxis()
1412  self._pad.RedrawAxis()
1413 
1414  self._parentPad.cd()
1415 
1416  # pad to hide the lowest y axis label of the main pad
1417  xmin=0.065
1418  ymin=0.285
1419  xmax=0.128
1420  ymax=0.33
1421  self._coverPad = ROOT.TPad("coverpad", "coverpad", xmin, ymin, xmax, ymax)
1422  self._coverPad.SetBorderMode(0)
1423  self._coverPad.Draw()
1424 
1425  self._pad.cd()
1426  self._pad.Pop() # Move the first pad on top
1427 
1429  """Class for creating and managing a frame for a plot from TGraph2D"""
1430  def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor):
1431  self._pad = pad
1432  if ratioOrig:
1433  self._pad = pad.cd(1)
1434 
1435  # adjust margins because of not having the ratio, we want
1436  # the same bottom margin, so some algebra gives this
1437  (xlow, ylow, width, height) = (self._pad.GetXlowNDC(), self._pad.GetYlowNDC(), self._pad.GetWNDC(), self._pad.GetHNDC())
1438  xup = xlow+width
1439  yup = ylow+height
1440 
1441  bottomMargin = self._pad.GetBottomMargin()
1442  bottomMarginNew = ROOT.gStyle.GetPadBottomMargin()
1443 
1444  ylowNew = yup - (1-bottomMargin)/(1-bottomMarginNew) * (yup-ylow)
1445  topMarginNew = self._pad.GetTopMargin() * (yup-ylow)/(yup-ylowNew)
1446 
1447  self._pad.SetPad(xlow, ylowNew, xup, yup)
1448  self._pad.SetTopMargin(topMarginNew)
1449  self._pad.SetBottomMargin(bottomMarginNew)
1450 
1451  self._xtitleoffset = 1.8
1452  self._ytitleoffset = 2.3
1453 
1454  self._firstHisto = histos[0]
1455 
1456  def setLogx(self, log):
1457  pass
1458 
1459  def setLogy(self, log):
1460  pass
1461 
1462  def setGridx(self, grid):
1463  pass
1464 
1465  def setGridy(self, grid):
1466  pass
1467 
1468  def adjustMarginLeft(self, adjust):
1469  self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
1470  self._pad.cd()
1471 
1472  def adjustMarginRight(self, adjust):
1473  self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
1474  self._pad.cd()
1475 
1476  def setTitle(self, title):
1477  pass
1478 
1479  def setXTitle(self, title):
1480  self._xtitle = title
1481 
1482  def setXTitleSize(self, size):
1483  self._xtitlesize = size
1484 
1485  def setXTitleOffset(self, size):
1486  self._xtitleoffset = size
1487 
1488  def setXLabelSize(self, size):
1489  self._xlabelsize = size
1490 
1491  def setYTitle(self, title):
1492  self._ytitle = title
1493 
1494  def setYTitleSize(self, size):
1495  self._ytitlesize = size
1496 
1497  def setYTitleOffset(self, offset):
1498  self._ytitleoffset = offset
1499 
1500  def setZTitle(self, title):
1501  self._firstHisto.GetZaxis().SetTitle(title)
1502 
1503  def setZTitleOffset(self, offset):
1504  self._firstHisto.GetZaxis().SetTitleOffset(offset)
1505 
1506  def redrawAxis(self):
1507  # set top view
1508  epsilon = 1e-7
1509  self._pad.SetPhi(epsilon)
1510  self._pad.SetTheta(90+epsilon)
1511 
1512  self._firstHisto.GetXaxis().SetTitleOffset(self._xtitleoffset)
1513  self._firstHisto.GetYaxis().SetTitleOffset(self._ytitleoffset)
1514 
1515  if hasattr(self, "_xtitle"):
1516  self._firstHisto.GetXaxis().SetTitle(self._xtitle)
1517  if hasattr(self, "_xtitlesize"):
1518  self._firstHisto.GetXaxis().SetTitleSize(self._xtitlesize)
1519  if hasattr(self, "_xlabelsize"):
1520  self._firstHisto.GetXaxis().SetLabelSize(self._labelsize)
1521  if hasattr(self, "_ytitle"):
1522  self._firstHisto.GetYaxis().SetTitle(self._ytitle)
1523  if hasattr(self, "_ytitlesize"):
1524  self._firstHisto.GetYaxis().SetTitleSize(self._ytitlesize)
1525  if hasattr(self, "_ytitleoffset"):
1526  self._firstHisto.GetYaxis().SetTitleOffset(self._ytitleoffset)
1527 
1528 class PlotText:
1529  """Abstraction on top of TLatex"""
1530  def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None):
1531  """Constructor.
1532 
1533  Arguments:
1534  x -- X coordinate of the text (in NDC)
1535  y -- Y coordinate of the text (in NDC)
1536  text -- String to draw
1537  size -- Size of text (None for the default value, taken from gStyle)
1538  bold -- Should the text be bold?
1539  align -- Alignment of text (left, center, right)
1540  color -- Color of the text
1541  font -- Specify font explicitly
1542  """
1543  self._x = x
1544  self._y = y
1545  self._text = text
1546 
1547  self._l = ROOT.TLatex()
1548  self._l.SetNDC()
1549  if not bold:
1550  self._l.SetTextFont(self._l.GetTextFont()-20) # bold -> normal
1551  if font is not None:
1552  self._l.SetTextFont(font)
1553  if size is not None:
1554  self._l.SetTextSize(size)
1555  if isinstance(align, basestring):
1556  if align.lower() == "left":
1557  self._l.SetTextAlign(11)
1558  elif align.lower() == "center":
1559  self._l.SetTextAlign(21)
1560  elif align.lower() == "right":
1561  self._l.SetTextAlign(31)
1562  else:
1563  raise Exception("Error: Invalid option '%s' for text alignment! Options are: 'left', 'center', 'right'."%align)
1564  else:
1565  self._l.SetTextAlign(align)
1566  self._l.SetTextColor(color)
1567 
1568  def Draw(self, options=None):
1569  """Draw the text to the current TPad.
1570 
1571  Arguments:
1572  options -- For interface compatibility, ignored
1573 
1574  Provides interface compatible with ROOT's drawable objects.
1575  """
1576  self._l.DrawLatex(self._x, self._y, self._text)
1577 
1578 
1580  """Class for drawing text and a background box."""
1581  def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, **kwargs):
1582  """Constructor
1583 
1584  Arguments:
1585  xmin -- X min coordinate of the box (NDC)
1586  ymin -- Y min coordinate of the box (NDC) (if None, deduced automatically)
1587  xmax -- X max coordinate of the box (NDC)
1588  ymax -- Y max coordinate of the box (NDC)
1589  lineheight -- Line height
1590  fillColor -- Fill color of the box
1591  transparent -- Should the box be transparent? (in practive the TPave is not created)
1592 
1593  Keyword arguments are forwarded to constructor of PlotText
1594  """
1595  # ROOT.TPave Set/GetX1NDC() etc don't seem to work as expected.
1596  self._xmin = xmin
1597  self._xmax = xmax
1598  self._ymin = ymin
1599  self._ymax = ymax
1600  self._lineheight = lineheight
1601  self._fillColor = fillColor
1602  self._transparent = transparent
1603  self._texts = []
1604  self._textArgs = {}
1605  self._textArgs.update(kwargs)
1606 
1607  self._currenty = ymax
1608 
1609  def addText(self, text):
1610  """Add text to current position"""
1611  self._currenty -= self._lineheight
1612  self._texts.append(PlotText(self._xmin+0.01, self._currenty, text, **self._textArgs))
1613 
1614  def width(self):
1615  return self._xmax-self._xmin
1616 
1617  def move(self, dx=0, dy=0, dw=0, dh=0):
1618  """Move the box and the contained text objects
1619 
1620  Arguments:
1621  dx -- Movement in x (positive is to right)
1622  dy -- Movement in y (positive is to up)
1623  dw -- Increment of width (negative to decrease width)
1624  dh -- Increment of height (negative to decrease height)
1625 
1626  dx and dy affect to both box and text objects, dw and dh
1627  affect the box only.
1628  """
1629  self._xmin += dx
1630  self._xmax += dx
1631  if self._ymin is not None:
1632  self._ymin += dy
1633  self._ymax += dy
1634 
1635  self._xmax += dw
1636  if self._ymin is not None:
1637  self._ymin -= dh
1638 
1639  for t in self._texts:
1640  t._x += dx
1641  t._y += dy
1642 
1643  def Draw(self, options=""):
1644  """Draw the box and the text to the current TPad.
1645 
1646  Arguments:
1647  options -- Forwarded to ROOT.TPave.Draw(), and the Draw() of the contained objects
1648  """
1649  if not self._transparent:
1650  ymin = self.ymin
1651  if ymin is None:
1652  ymin = self.currenty - 0.01
1653  self._pave = ROOT.TPave(self.xmin, self.ymin, self.xmax, self.ymax, 0, "NDC")
1654  self._pave.SetFillColor(self.fillColor)
1655  self._pave.Draw(options)
1656  for t in self._texts:
1657  t.Draw(options)
1658 
1659 def _copyStyle(src, dst):
1660  properties = []
1661  if hasattr(src, "GetLineColor") and hasattr(dst, "SetLineColor"):
1662  properties.extend(["LineColor", "LineStyle", "LineWidth"])
1663  if hasattr(src, "GetFillColor") and hasattr(dst, "SetFillColor"):
1664  properties.extend(["FillColor", "FillStyle"])
1665  if hasattr(src, "GetMarkerColor") and hasattr(dst, "SetMarkerColor"):
1666  properties.extend(["MarkerColor", "MarkerSize", "MarkerStyle"])
1667 
1668  for prop in properties:
1669  getattr(dst, "Set"+prop)(getattr(src, "Get"+prop)())
1670 
1672  """Denotes an empty place in a group."""
1673  def __init__(self):
1674  pass
1675 
1676  def getName(self):
1677  return None
1678 
1680  return False
1681 
1682  def create(self, *args, **kwargs):
1683  pass
1684 
1685  def isEmpty(self):
1686  return True
1687 
1689  return 0
1690 
1691 class Plot:
1692  """Represents one plot, comparing one or more histograms."""
1693  def __init__(self, name, **kwargs):
1694  """ Constructor.
1695 
1696  Arguments:
1697  name -- String for name of the plot, or Efficiency object
1698 
1699  Keyword arguments:
1700  fallback -- Dictionary for specifying fallback (default None)
1701  outname -- String for an output name of the plot (default None for the same as 'name')
1702  title -- String for a title of the plot (default None)
1703  xtitle -- String for x axis title (default None)
1704  xtitlesize -- Float for x axis title size (default None)
1705  xtitleoffset -- Float for x axis title offset (default None)
1706  xlabelsize -- Float for x axis label size (default None)
1707  ytitle -- String for y axis title (default None)
1708  ytitlesize -- Float for y axis title size (default None)
1709  ytitleoffset -- Float for y axis title offset (default None)
1710  ztitle -- String for z axis title (default None)
1711  ztitleoffset -- Float for z axis title offset (default None)
1712  xmin -- Float for x axis minimum (default None, i.e. automatic)
1713  xmax -- Float for x axis maximum (default None, i.e. automatic)
1714  ymin -- Float for y axis minimum (default 0)
1715  ymax -- Float for y axis maximum (default None, i.e. automatic)
1716  xlog -- Bool for x axis log status (default False)
1717  ylog -- Bool for y axis log status (default False)
1718  xgrid -- Bool for x axis grid status (default True)
1719  ygrid -- Bool for y axis grid status (default True)
1720  stat -- Draw stat box? (default False)
1721  fit -- Do gaussian fit? (default False)
1722  statx -- Stat box x coordinate (default 0.65)
1723  staty -- Stat box y coordinate (default 0.8)
1724  statyadjust -- List of floats for stat box y coordinate adjustments (default None)
1725  normalizeToUnitArea -- Normalize histograms to unit area? (default False)
1726  normalizeToNumberOfEvents -- Normalize histograms to number of events? If yes, the PlotFolder needs 'numberOfEventsHistogram' set to a histogram filled once per event (default False)
1727  profileX -- Take histograms via ProfileX()? (default False)
1728  fitSlicesY -- Take histograms via FitSlicesY() (default False)
1729  rebinX -- rebin x axis (default None)
1730  scale -- Scale histograms by a number (default None)
1731  xbinlabels -- List of x axis bin labels (if given, default None)
1732  xbinlabelsize -- Size of x axis bin labels (default None)
1733  xbinlabeloption -- Option string for x axis bin labels (default None)
1734  removeEmptyBins -- Bool for removing empty bins, but only if histogram has bin labels (default False)
1735  printBins -- Bool for printing bin values, but only if histogram has bin labels (default False)
1736  drawStyle -- If "hist", draw as line instead of points (default None)
1737  drawCommand -- Deliver this to Draw() (default: None for same as drawStyle)
1738  lineWidth -- If drawStyle=="hist", the width of line (default 2)
1739  legendDx -- Float for moving TLegend in x direction for separate=True (default None)
1740  legendDy -- Float for moving TLegend in y direction for separate=True (default None)
1741  legendDw -- Float for changing TLegend width for separate=True (default None)
1742  legendDh -- Float for changing TLegend height for separate=True (default None)
1743  legend -- Bool to enable/disable legend (default True)
1744  adjustMarginLeft -- Float for adjusting left margin (default None)
1745  adjustMarginRight -- Float for adjusting right margin (default None)
1746  ratio -- Possibility to disable ratio for this particular plot (default None)
1747  ratioYmin -- Float for y axis minimum in ratio pad (default: list of values)
1748  ratioYmax -- Float for y axis maximum in ratio pad (default: list of values)
1749  ratioFit -- Fit straight line in ratio? (default None)
1750  ratioUncertainty -- Plot uncertainties on ratio? (default True)
1751  ratioCoverageXrange -- Range of x axis values (xmin,xmax) to limit the automatic ratio y axis range calculation to (default None for disabled)
1752  histogramModifier -- Function to be called in create() to modify the histograms (default None)
1753  """
1754  self._name = name
1755 
1756  def _set(attr, default):
1757  setattr(self, "_"+attr, kwargs.get(attr, default))
1758 
1759  _set("fallback", None)
1760  _set("outname", None)
1761 
1762  _set("title", None)
1763  _set("xtitle", None)
1764  _set("xtitlesize", None)
1765  _set("xtitleoffset", None)
1766  _set("xlabelsize", None)
1767  _set("ytitle", None)
1768  _set("ytitlesize", None)
1769  _set("ytitleoffset", None)
1770  _set("ztitle", None)
1771  _set("ztitleoffset", None)
1772 
1773  _set("xmin", None)
1774  _set("xmax", None)
1775  _set("ymin", 0.)
1776  _set("ymax", None)
1777 
1778  _set("xlog", False)
1779  _set("ylog", False)
1780  _set("xgrid", True)
1781  _set("ygrid", True)
1782 
1783  _set("stat", False)
1784  _set("fit", False)
1785 
1786  _set("statx", 0.65)
1787  _set("staty", 0.8)
1788  _set("statyadjust", None)
1789 
1790  _set("normalizeToUnitArea", False)
1791  _set("normalizeToNumberOfEvents", False)
1792  _set("profileX", False)
1793  _set("fitSlicesY", False)
1794  _set("rebinX", None)
1795 
1796  _set("scale", None)
1797  _set("xbinlabels", None)
1798  _set("xbinlabelsize", None)
1799  _set("xbinlabeloption", None)
1800  _set("removeEmptyBins", False)
1801  _set("printBins", False)
1802 
1803  _set("drawStyle", "EP")
1804  _set("drawCommand", None)
1805  _set("lineWidth", 2)
1806 
1807  _set("legendDx", None)
1808  _set("legendDy", None)
1809  _set("legendDw", None)
1810  _set("legendDh", None)
1811  _set("legend", True)
1812 
1813  _set("adjustMarginLeft", None)
1814  _set("adjustMarginRight", None)
1815 
1816  _set("ratio", None)
1817  _set("ratioYmin", [0, 0.2, 0.5, 0.7, 0.8, 0.9, 0.95])
1818  _set("ratioYmax", [1.05, 1.1, 1.2, 1.3, 1.5, 1.8, 2, 2.5, 3, 4, 5])
1819  _set("ratioFit", None)
1820  _set("ratioUncertainty", True)
1821  _set("ratioCoverageXrange", None)
1822 
1823  _set("histogramModifier", None)
1824 
1825  self._histograms = []
1826 
1827  def setProperties(self, **kwargs):
1828  for name, value in kwargs.iteritems():
1829  if not hasattr(self, "_"+name):
1830  raise Exception("No attribute '%s'" % name)
1831  setattr(self, "_"+name, value)
1832 
1833  def clone(self, **kwargs):
1834  if not self.isEmpty():
1835  raise Exception("Plot can be cloned only before histograms have been created")
1836  cl = copy.copy(self)
1837  cl.setProperties(**kwargs)
1838  return cl
1839 
1841  """Return number of existing histograms."""
1842  return len(filter(lambda h: h is not None, self._histograms))
1843 
1844  def isEmpty(self):
1845  """Return true if there are no histograms created for the plot"""
1846  return self.getNumberOfHistograms() == 0
1847 
1848  def isTGraph2D(self):
1849  for h in self._histograms:
1850  if isinstance(h, ROOT.TGraph2D):
1851  return True
1852  return False
1853 
1854  def isRatio(self, ratio):
1855  if self._ratio is None:
1856  return ratio
1857  return ratio and self._ratio
1858 
1859  def getName(self):
1860  if self._outname is not None:
1861  return self._outname
1862  if isinstance(self._name, list):
1863  return str(self._name[0])
1864  else:
1865  return str(self._name)
1866 
1868  """Return true if the ratio uncertainty should be drawn"""
1869  return self._ratioUncertainty
1870 
1871  def _createOne(self, name, index, tdir, nevents):
1872  """Create one histogram from a TDirectory."""
1873  if tdir == None:
1874  return None
1875 
1876  # If name is a list, pick the name by the index
1877  if isinstance(name, list):
1878  name = name[index]
1879 
1880  h = _getOrCreateObject(tdir, name)
1881  if h is not None and self._normalizeToNumberOfEvents and nevents is not None and nevents != 0:
1882  h.Scale(1.0/nevents)
1883  return h
1884 
1885  def create(self, tdirNEvents, requireAllHistograms=False):
1886  """Create histograms from list of TDirectories"""
1887  self._histograms = [self._createOne(self._name, i, tdirNEvent[0], tdirNEvent[1]) for i, tdirNEvent in enumerate(tdirNEvents)]
1888 
1889  if self._fallback is not None:
1890  profileX = [self._profileX]*len(self._histograms)
1891  for i in xrange(0, len(self._histograms)):
1892  if self._histograms[i] is None:
1893  self._histograms[i] = self._createOne(self._fallback["name"], i, tdirNEvents[i][0], tdirNEvents[i][1])
1894  profileX[i] = self._fallback.get("profileX", self._profileX)
1895 
1896  if self._histogramModifier is not None:
1897  self._histograms = self._histogramModifier(self._histograms)
1898 
1899  if len(self._histograms) > len(_plotStylesColor):
1900  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)))
1901 
1902  # Modify histograms here in case self._name returns numbers
1903  # and self._histogramModifier creates the histograms from
1904  # these numbers
1905  def _modifyHisto(th1, profileX):
1906  if th1 is None:
1907  return None
1908 
1909  if profileX:
1910  th1 = th1.ProfileX()
1911 
1912  if self._fitSlicesY:
1913  ROOT.TH1.AddDirectory(True)
1914  th1.FitSlicesY()
1915  th1 = ROOT.gDirectory.Get(th1.GetName()+"_2")
1916  th1.SetDirectory(None)
1917  #th1.SetName(th1.GetName()+"_ref")
1918  ROOT.TH1.AddDirectory(False)
1919 
1920  if self._title is not None:
1921  th1.SetTitle(self._title)
1922 
1923  if self._scale is not None:
1924  th1.Scale(self._scale)
1925 
1926  return th1
1927 
1928  if self._fallback is not None:
1929  self._histograms = map(_modifyHisto, self._histograms, profileX)
1930  else:
1931  self._histograms = map(lambda h: _modifyHisto(h, self._profileX), self._histograms)
1932  if requireAllHistograms and None in self._histograms:
1933  self._histograms = [None]*len(self._histograms)
1934 
1935  def _setStats(self, histos, startingX, startingY):
1936  """Set stats box."""
1937  if not self._stat:
1938  for h in histos:
1939  if h is not None and hasattr(h, "SetStats"):
1940  h.SetStats(0)
1941  return
1942 
1943  def _doStats(h, col, dy):
1944  if h is None:
1945  return
1946  h.SetStats(True)
1947 
1948  if self._fit and h.GetEntries() > 0.5:
1949  h.Fit("gaus", "Q")
1950  f = h.GetListOfFunctions().FindObject("gaus")
1951  if f == None:
1952  h.SetStats(0)
1953  return
1954  f.SetLineColor(col)
1955  f.SetLineWidth(1)
1956  h.Draw()
1957  ROOT.gPad.Update()
1958  st = h.GetListOfFunctions().FindObject("stats")
1959  if self._fit:
1960  st.SetOptFit(0o010)
1961  st.SetOptStat(1001)
1962  st.SetX1NDC(startingX)
1963  st.SetX2NDC(startingX+0.3)
1964  st.SetY1NDC(startingY+dy)
1965  st.SetY2NDC(startingY+dy+0.15)
1966  st.SetTextColor(col)
1967 
1968  dy = 0.0
1969  for i, h in enumerate(histos):
1970  if self._statyadjust is not None and i < len(self._statyadjust):
1971  dy += self._statyadjust[i]
1972 
1973  _doStats(h, _plotStylesColor[i], dy)
1974  dy -= 0.19
1975 
1976  def _normalize(self):
1977  """Normalise histograms to unit area"""
1978 
1979  for h in self._histograms:
1980  if h is None:
1981  continue
1982  i = h.Integral()
1983  if i == 0:
1984  continue
1985  if h.GetSumw2().fN <= 0: # to suppress warning
1986  h.Sumw2()
1987  h.Scale(1.0/i)
1988 
1989  def draw(self, pad, ratio, ratioFactor, nrows):
1990  """Draw the histograms using values for a given algorithm."""
1991 # if len(self._histograms) == 0:
1992 # print "No histograms for plot {name}".format(name=self._name)
1993 # return
1994 
1995  isTGraph2D = self.isTGraph2D()
1996  if isTGraph2D:
1997  # Ratios for the TGraph2Ds is not that interesting
1998  ratioOrig = ratio
1999  ratio = False
2000 
2001  if self._normalizeToUnitArea:
2002  self._normalize()
2003 
2004  if self._rebinX is not None:
2005  for h in self._histograms:
2006  h.Rebin(self._rebinX)
2007 
2008  def _styleMarker(h, msty, col):
2009  h.SetMarkerStyle(msty)
2010  h.SetMarkerColor(col)
2011  h.SetMarkerSize(0.7)
2012  h.SetLineColor(1)
2013  h.SetLineWidth(1)
2014 
2015  def _styleHist(h, msty, col):
2016  _styleMarker(h, msty, col)
2017  h.SetLineColor(col)
2018  h.SetLineWidth(self._lineWidth)
2019 
2020  # Use marker or hist style
2021  style = _styleMarker
2022  if "hist" in self._drawStyle.lower():
2023  style = _styleHist
2024  if len(self._histograms) > 0 and isinstance(self._histograms[0], ROOT.TGraph):
2025  if "l" in self._drawStyle.lower():
2026  style = _styleHist
2027 
2028  # Apply style to histograms, filter out Nones
2029  histos = []
2030  for i, h in enumerate(self._histograms):
2031  if h is None:
2032  continue
2033  style(h, _plotStylesMarker[i], _plotStylesColor[i])
2034  histos.append(h)
2035  if len(histos) == 0:
2036  if verbose:
2037  print "No histograms for plot {name}".format(name=self.getName())
2038  return
2039 
2040  # Extract x bin labels, make sure that only bins with same
2041  # label are compared with each other
2042  histosHaveBinLabels = len(histos[0].GetXaxis().GetBinLabel(1)) > 0
2043  xbinlabels = self._xbinlabels
2044  ybinlabels = None
2045  if xbinlabels is None:
2046  if histosHaveBinLabels:
2047  xbinlabels = _mergeBinLabelsX(histos)
2048  if isinstance(histos[0], ROOT.TH2):
2049  ybinlabels = _mergeBinLabelsY(histos)
2050 
2051  if len(histos) > 1: # don't bother if only one histogram
2052  # doing this for TH2 is pending for use case, for now there is only 1 histogram/plot for TH2
2053  histos = _th1IncludeOnlyBins(histos, xbinlabels)
2054  self._tmp_histos = histos # need to keep these in memory too ...
2055 
2056  # Remove empty bins, but only if histograms have bin labels
2057  if self._removeEmptyBins and histosHaveBinLabels:
2058  # at this point, all histograms have been "equalized" by their x binning and labels
2059  # therefore remove bins which are empty in all histograms
2060  if isinstance(histos[0], ROOT.TH2):
2061  (histos, xbinlabels, ybinlabels) = _th2RemoveEmptyBins(histos, xbinlabels, ybinlabels)
2062  else:
2063  (histos, xbinlabels) = _th1RemoveEmptyBins(histos, xbinlabels)
2064  self._tmp_histos = histos # need to keep these in memory too ...
2065  if len(histos) == 0:
2066  if verbose:
2067  print "No histograms with non-empty bins for plot {name}".format(name=self.getName())
2068  return
2069 
2070  if self._printBins and histosHaveBinLabels:
2071  print "####################"
2072  print self._name
2073  width = max([len(l) for l in xbinlabels])
2074  tmp = "%%-%ds " % width
2075  for b in xrange(1, histos[0].GetNbinsX()+1):
2076  s = tmp % xbinlabels[b-1]
2077  for h in histos:
2078  s += "%.3f " % h.GetBinContent(b)
2079  print s
2080  print
2081 
2082  bounds = _findBounds(histos, self._ylog,
2083  xmin=self._xmin, xmax=self._xmax,
2084  ymin=self._ymin, ymax=self._ymax)
2085  zmax = None
2086  if isinstance(histos[0], ROOT.TH2):
2087  zmax = max([h.GetMaximum() for h in histos])
2088 
2089  # need to keep these in memory
2092 
2093  if ratio:
2094  self._ratios = _calculateRatios(histos, self._ratioUncertainty) # need to keep these in memory too ...
2095  ratioHistos = filter(lambda h: h is not None, [r.getRatio() for r in self._ratios[1:]])
2096 
2097  if len(ratioHistos) > 0:
2098  ratioBoundsY = _findBoundsY(ratioHistos, ylog=False, ymin=self._ratioYmin, ymax=self._ratioYmax, coverage=0.68, coverageRange=self._ratioCoverageXrange)
2099  else:
2100  ratioBoundsY = (0.9, 1,1) # hardcoded default in absence of valid ratio calculations
2101 
2102  if self._ratioFit is not None:
2103  for i, rh in enumerate(ratioHistos):
2104  tf_line = ROOT.TF1("line%d"%i, "[0]+x*[1]")
2105  tf_line.SetRange(self._ratioFit["rangemin"], self._ratioFit["rangemax"])
2106  fitres = rh.Fit(tf_line, "RINSQ")
2107  tf_line.SetLineColor(rh.GetMarkerColor())
2108  tf_line.SetLineWidth(2)
2109  self._ratioAdditional.append(tf_line)
2110  box = PlotTextBox(xmin=self._ratioFit.get("boxXmin", 0.14), ymin=None, # None for automatix
2111  xmax=self._ratioFit.get("boxXmax", 0.35), ymax=self._ratioFit.get("boxYmax", 0.09),
2112  color=rh.GetMarkerColor(), font=43, size=11, lineheight=0.02)
2113  box.move(dx=(box.width()+0.01)*i)
2114  #box.addText("Const: %.4f" % fitres.Parameter(0))
2115  #box.addText("Slope: %.4f" % fitres.Parameter(1))
2116  box.addText("Const: %.4f#pm%.4f" % (fitres.Parameter(0), fitres.ParError(0)))
2117  box.addText("Slope: %.4f#pm%.4f" % (fitres.Parameter(1), fitres.ParError(1)))
2118  self._mainAdditional.append(box)
2119 
2120 
2121  # Create bounds before stats in order to have the
2122  # SetRangeUser() calls made before the fit
2123  #
2124  # stats is better to be called before frame, otherwise get
2125  # mess in the plot (that frame creation cleans up)
2126  if ratio:
2127  pad.cd(1)
2128  self._setStats(histos, self._statx, self._staty)
2129 
2130  # Create frame
2131  if isTGraph2D:
2132  frame = FrameTGraph2D(pad, bounds, histos, ratioOrig, ratioFactor)
2133  else:
2134  if ratio:
2135  ratioBounds = (bounds[0], ratioBoundsY[0], bounds[2], ratioBoundsY[1])
2136  frame = FrameRatio(pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
2137  else:
2138  frame = Frame(pad, bounds, zmax, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption, ybinlabels=ybinlabels)
2139 
2140  # Set log and grid
2141  frame.setLogx(self._xlog)
2142  frame.setLogy(self._ylog)
2143  frame.setGridx(self._xgrid)
2144  frame.setGridy(self._ygrid)
2145 
2146  # Construct draw option string
2147  opt = "sames" # s for statbox or something?
2148  ds = ""
2149  if self._drawStyle is not None:
2150  ds = self._drawStyle
2151  if self._drawCommand is not None:
2152  ds = self._drawCommand
2153  if len(ds) > 0:
2154  opt += " "+ds
2155 
2156  # Set properties of frame
2157  frame.setTitle(histos[0].GetTitle())
2158  if self._xtitle is not None:
2159  frame.setXTitle(self._xtitle)
2160  if self._xtitlesize is not None:
2161  frame.setXTitleSize(self._xtitlesize)
2162  if self._xtitleoffset is not None:
2163  frame.setXTitleOffset(self._xtitleoffset)
2164  if self._xlabelsize is not None:
2165  frame.setXLabelSize(self._xlabelsize)
2166  if self._ytitle is not None:
2167  frame.setYTitle(self._ytitle)
2168  if self._ytitlesize is not None:
2169  frame.setYTitleSize(self._ytitlesize)
2170  if self._ytitleoffset is not None:
2171  frame.setYTitleOffset(self._ytitleoffset)
2172  if self._ztitle is not None:
2173  frame.setZTitle(self._ztitle)
2174  if self._ztitleoffset is not None:
2175  frame.setZTitleOffset(self._ztitleoffset)
2176  if self._adjustMarginLeft is not None:
2177  frame.adjustMarginLeft(self._adjustMarginLeft)
2178  if self._adjustMarginRight is not None:
2179  frame.adjustMarginRight(self._adjustMarginRight)
2180  elif "z" in opt:
2181  frame.adjustMarginLeft(0.03)
2182  frame.adjustMarginRight(0.08)
2183 
2184  # Draw histograms
2185  if ratio:
2186  frame._pad.cd()
2187 
2188  for i, h in enumerate(histos):
2189  o = opt
2190  if isTGraph2D and i == 0:
2191  o = o.replace("sames", "")
2192  h.Draw(o)
2193 
2194  for addl in self._mainAdditional:
2195  addl.Draw("same")
2196 
2197  # Draw ratios
2198  if ratio and len(histos) > 0:
2199  frame._padRatio.cd()
2200  firstRatio = self._ratios[0].getRatio()
2201  if self._ratioUncertainty and firstRatio is not None:
2202  firstRatio.SetFillStyle(1001)
2203  firstRatio.SetFillColor(ROOT.kGray)
2204  firstRatio.SetLineColor(ROOT.kGray)
2205  firstRatio.SetMarkerColor(ROOT.kGray)
2206  firstRatio.SetMarkerSize(0)
2207  self._ratios[0].draw("E2")
2208  frame._padRatio.RedrawAxis("G") # redraw grid on top of the uncertainty of denominator
2209  for r in self._ratios[1:]:
2210  r.draw()
2211 
2212  for addl in self._ratioAdditional:
2213  addl.Draw("same")
2214 
2215  frame.redrawAxis()
2216  self._frame = frame # keep the frame in memory for sure
2217 
2218  def addToLegend(self, legend, legendLabels, denomUncertainty):
2219  """Add histograms to a legend.
2220 
2221  Arguments:
2222  legend -- TLegend
2223  legendLabels -- List of strings for the legend labels
2224  """
2225  first = denomUncertainty
2226  for h, label in zip(self._histograms, legendLabels):
2227  if h is None:
2228  first = False
2229  continue
2230  if first:
2231  self._forLegend = h.Clone()
2232  self._forLegend.SetFillStyle(1001)
2233  self._forLegend.SetFillColor(ROOT.kGray)
2234  entry = legend.AddEntry(self._forLegend, label, "lpf")
2235  first = False
2236  else:
2237  legend.AddEntry(h, label, "LP")
2238 
2240  """Group of plots, results a TCanvas"""
2241  def __init__(self, name, plots, **kwargs):
2242  """Constructor.
2243 
2244  Arguments:
2245  name -- String for name of the TCanvas, used also as the basename of the picture files
2246  plots -- List of Plot objects
2247 
2248  Keyword arguments:
2249  ncols -- Number of columns (default 2)
2250  legendDx -- Float for moving TLegend in x direction (default None)
2251  legendDy -- Float for moving TLegend in y direction (default None)
2252  legendDw -- Float for changing TLegend width (default None)
2253  legendDh -- Float for changing TLegend height (default None)
2254  legend -- Bool for disabling legend (default True for legend being enabled)
2255  overrideLegendLabels -- List of strings for legend labels, if given, these are used instead of the ones coming from Plotter (default None)
2256  onlyForPileup -- Plots this group only for pileup samples
2257  """
2258  super(PlotGroup, self).__init__()
2259 
2260  self._name = name
2261  self._plots = plots
2262 
2263  def _set(attr, default):
2264  setattr(self, "_"+attr, kwargs.get(attr, default))
2265 
2266  _set("ncols", 2)
2267 
2268  _set("legendDx", None)
2269  _set("legendDy", None)
2270  _set("legendDw", None)
2271  _set("legendDh", None)
2272  _set("legend", True)
2273 
2274  _set("overrideLegendLabels", None)
2275 
2276  _set("onlyForPileup", False)
2277 
2278  self._ratioFactor = 1.25
2279 
2280  def setProperties(self, **kwargs):
2281  for name, value in kwargs.iteritems():
2282  if not hasattr(self, "_"+name):
2283  raise Exception("No attribute '%s'" % name)
2284  setattr(self, "_"+name, value)
2285 
2286  def getName(self):
2287  return self._name
2288 
2289  def getPlots(self):
2290  return self._plots
2291 
2292  def remove(self, name):
2293  for i, plot in enumerate(self._plots):
2294  if plot.getName() == name:
2295  del self._plots[i]
2296  return
2297  raise Exception("Did not find Plot '%s' from PlotGroup '%s'" % (name, self._name))
2298 
2299  def clear(self):
2300  self._plots = []
2301 
2302  def append(self, plot):
2303  self._plots.append(plot)
2304 
2305  def getPlot(self, name):
2306  for plot in self._plots:
2307  if plot.getName() == name:
2308  return plot
2309  raise Exception("No Plot named '%s'" % name)
2310 
2311  def onlyForPileup(self):
2312  """Return True if the PlotGroup is intended only for pileup samples"""
2313  return self._onlyForPileup
2314 
2315  def create(self, tdirectoryNEvents, requireAllHistograms=False):
2316  """Create histograms from a list of TDirectories.
2317 
2318  Arguments:
2319  tdirectoryNEvents -- List of (TDirectory, nevents) pairs
2320  requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2321  """
2322  for plot in self._plots:
2323  plot.create(tdirectoryNEvents, requireAllHistograms)
2324 
2325  def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2326  """Draw the histograms using values for a given algorithm.
2327 
2328  Arguments:
2329  legendLabels -- List of strings for legend labels (corresponding to the tdirectories in create())
2330  prefix -- Optional string for file name prefix (default None)
2331  separate -- Save the plots of a group to separate files instead of a file per group (default False)
2332  saveFormat -- String specifying the plot format (default '.pdf')
2333  ratio -- Add ratio to the plot (default True)
2334  directory -- Directory where to save the file (default "")
2335  """
2336 
2337  if self._overrideLegendLabels is not None:
2338  legendLabels = self._overrideLegendLabels
2339 
2340  # Do not draw the group if it would be empty
2341  onlyEmptyPlots = True
2342  for plot in self._plots:
2343  if not plot.isEmpty():
2344  onlyEmptyPlots = False
2345  break
2346  if onlyEmptyPlots:
2347  return []
2348 
2349  if separate:
2350  return self._drawSeparate(legendLabels, prefix, saveFormat, ratio, directory)
2351 
2352  cwidth = 500*self._ncols
2353  nrows = int((len(self._plots)+self._ncols-1)/self._ncols) # this should work also for odd n
2354  cheight = 500 * nrows
2355 
2356  if ratio:
2357  cheight = int(cheight*self._ratioFactor)
2358 
2359  canvas = _createCanvas(self._name, cwidth, cheight)
2360 
2361  canvas.Divide(self._ncols, nrows)
2362  if ratio:
2363  for i, plot in enumerate(self._plots):
2364  pad = canvas.cd(i+1)
2365  self._modifyPadForRatio(pad)
2366 
2367  # Draw plots to canvas
2368  for i, plot in enumerate(self._plots):
2369  pad = canvas.cd(i+1)
2370  if not plot.isEmpty():
2371  plot.draw(pad, ratio, self._ratioFactor, nrows)
2372 
2373  # Setup legend
2374  canvas.cd()
2375  if len(self._plots) <= 4:
2376  lx1 = 0.2
2377  lx2 = 0.9
2378  ly1 = 0.48
2379  ly2 = 0.53
2380  else:
2381  lx1 = 0.1
2382  lx2 = 0.9
2383  ly1 = 0.64
2384  ly2 = 0.67
2385  if self._legendDx is not None:
2386  lx1 += self._legendDx
2387  lx2 += self._legendDx
2388  if self._legendDy is not None:
2389  ly1 += self._legendDy
2390  ly2 += self._legendDy
2391  if self._legendDw is not None:
2392  lx2 += self._legendDw
2393  if self._legendDh is not None:
2394  ly1 -= self._legendDh
2395  plot = max(self._plots, key=lambda p: p.getNumberOfHistograms())
2396  denomUnc = sum([p.drawRatioUncertainty() for p in self._plots]) > 0
2397  legend = self._createLegend(plot, legendLabels, lx1, ly1, lx2, ly2,
2398  denomUncertainty=(ratio and denomUnc))
2399 
2400  return self._save(canvas, saveFormat, prefix=prefix, directory=directory)
2401 
2402  def _drawSeparate(self, legendLabels, prefix, saveFormat, ratio, directory):
2403  """Internal method to do the drawing to separate files per Plot instead of a file per PlotGroup"""
2404  width = 500
2405  height = 500
2406 
2407  canvas = _createCanvas(self._name+"Single", width, height)
2408  canvasRatio = _createCanvas(self._name+"SingleRatio", width, int(height*self._ratioFactor))
2409 
2410  # from TDRStyle
2411  for c in [canvas, canvasRatio]:
2412  c.SetTopMargin(0.05)
2413  c.SetBottomMargin(0.13)
2414  c.SetLeftMargin(0.16)
2415  c.SetRightMargin(0.05)
2416 
2417  lx1def = 0.6
2418  lx2def = 0.95
2419  ly1def = 0.85
2420  ly2def = 0.95
2421 
2422  ret = []
2423 
2424  for plot in self._plots:
2425  if plot.isEmpty():
2426  continue
2427 
2428  ratioForThisPlot = plot.isRatio(ratio)
2429  c = canvas
2430  if ratioForThisPlot:
2431  c = canvasRatio
2432  c.cd()
2433  self._modifyPadForRatio(c)
2434 
2435  # Draw plot to canvas
2436  c.cd()
2437  plot.draw(c, ratioForThisPlot, self._ratioFactor, 1)
2438 
2439  if plot._legend:
2440  # Setup legend
2441  lx1 = lx1def
2442  lx2 = lx2def
2443  ly1 = ly1def
2444  ly2 = ly2def
2445 
2446  if plot._legendDx is not None:
2447  lx1 += plot._legendDx
2448  lx2 += plot._legendDx
2449  if plot._legendDy is not None:
2450  ly1 += plot._legendDy
2451  ly2 += plot._legendDy
2452  if plot._legendDw is not None:
2453  lx2 += plot._legendDw
2454  if plot._legendDh is not None:
2455  ly1 -= plot._legendDh
2456 
2457  c.cd()
2458  legend = self._createLegend(plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.03,
2459  denomUncertainty=(ratioForThisPlot and plot.drawRatioUncertainty))
2460 
2461  ret.extend(self._save(c, saveFormat, prefix=prefix, postfix="_"+plot.getName(), single=True, directory=directory))
2462  return ret
2463 
2464  def _modifyPadForRatio(self, pad):
2465  """Internal method to set divide a pad to two for ratio plots"""
2466  _modifyPadForRatio(pad, self._ratioFactor)
2467 
2468  def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True):
2469  if not self._legend:
2470  return None
2471 
2472  l = ROOT.TLegend(lx1, ly1, lx2, ly2)
2473  l.SetTextSize(textSize)
2474  l.SetLineColor(1)
2475  l.SetLineWidth(1)
2476  l.SetLineStyle(1)
2477  l.SetFillColor(0)
2478  l.SetMargin(0.07)
2479 
2480  plot.addToLegend(l, legendLabels, denomUncertainty)
2481  l.Draw()
2482  return l
2483 
2484  def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False, directory=""):
2485  # Save the canvas to file and clear
2486  name = self._name
2487  if prefix is not None:
2488  name = prefix+name
2489  if postfix is not None:
2490  name = name+postfix
2491  name = os.path.join(directory, name)
2492 
2493  if not verbose: # silence saved file printout
2494  backup = ROOT.gErrorIgnoreLevel
2495  ROOT.gErrorIgnoreLevel = ROOT.kWarning
2496  canvas.SaveAs(name+saveFormat)
2497  if not verbose:
2498  ROOT.gErrorIgnoreLevel = backup
2499 
2500  if single:
2501  canvas.Clear()
2502  canvas.SetLogx(False)
2503  canvas.SetLogy(False)
2504  else:
2505  canvas.Clear("D") # keep subpads
2506 
2507  return [name+saveFormat]
2508 
2510  """Resembles DQM GUI's "On side" layout.
2511 
2512  Like PlotGroup, but has only a description of a single plot. The
2513  plot is drawn separately for each file. Useful for 2D histograms."""
2514 
2515  def __init__(self, name, plot, ncols=2, onlyForPileup=False):
2516  super(PlotOnSideGroup, self).__init__(name, [], ncols=ncols, legend=False, onlyForPileup=onlyForPileup)
2517  self._plot = plot
2518  self._plot.setProperties(ratio=False)
2519 
2520  def append(self, *args, **kwargs):
2521  raise Exception("PlotOnSideGroup.append() is not implemented")
2522 
2523  def create(self, tdirectoryNEvents, requireAllHistograms=False):
2524  self._plots = []
2525  for element in tdirectoryNEvents:
2526  pl = self._plot.clone()
2527  pl.create([element], requireAllHistograms)
2528  self._plots.append(pl)
2529 
2530  def draw(self, *args, **kwargs):
2531  kargs = copy.copy(kwargs)
2532  kargs["ratio"] = False
2533  return super(PlotOnSideGroup, self).draw(*args, **kargs)
2534 
2536 
2537  """Represents a collection of PlotGroups, produced from a single folder in a DQM file"""
2538  def __init__(self, *plotGroups, **kwargs):
2539  """Constructor.
2540 
2541  Arguments:
2542  plotGroups -- List of PlotGroup objects
2543 
2544  Keyword arguments
2545  loopSubFolders -- Should the subfolders be looped over? (default: True)
2546  onlyForPileup -- Plots this folder only for pileup samples
2547  onlyForElectron -- Plots this folder only for electron samples
2548  onlyForConversion -- Plots this folder only for conversion samples
2549  onlyForBHadron -- Plots this folder only for B-hadron samples
2550  purpose -- html.PlotPurpose member class for the purpose of the folder, used for grouping of the plots to the HTML pages
2551  page -- Optional string for the page in HTML generatin
2552  section -- Optional string for the section within a page in HTML generation
2553  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".
2554  """
2555  self._plotGroups = list(plotGroups)
2556  self._loopSubFolders = kwargs.pop("loopSubFolders", True)
2557  self._onlyForPileup = kwargs.pop("onlyForPileup", False)
2558  self._onlyForElectron = kwargs.pop("onlyForElectron", False)
2559  self._onlyForConversion = kwargs.pop("onlyForConversion", False)
2560  self._onlyForBHadron = kwargs.pop("onlyForBHadron", False)
2561  self._purpose = kwargs.pop("purpose", None)
2562  self._page = kwargs.pop("page", None)
2563  self._section = kwargs.pop("section", None)
2564  self._numberOfEventsHistogram = kwargs.pop("numberOfEventsHistogram", None)
2565  if len(kwargs) > 0:
2566  raise Exception("Got unexpected keyword arguments: "+ ",".join(kwargs.keys()))
2567 
2568  def loopSubFolders(self):
2569  """Return True if the PlotGroups of this folder should be applied to the all subfolders"""
2570  return self._loopSubFolders
2571 
2572  def onlyForPileup(self):
2573  """Return True if the folder is intended only for pileup samples"""
2574  return self._onlyForPileup
2575 
2576  def onlyForElectron(self):
2577  return self._onlyForElectron
2578 
2580  return self._onlyForConversion
2581 
2582  def onlyForBHadron(self):
2583  return self._onlyForBHadron
2584 
2585  def getPurpose(self):
2586  return self._purpose
2587 
2588  def getPage(self):
2589  return self._page
2590 
2591  def getSection(self):
2592  return self._section
2593 
2595  return self._numberOfEventsHistogram
2596 
2597  def append(self, plotGroup):
2598  self._plotGroups.append(plotGroup)
2599 
2600  def set(self, plotGroups):
2601  self._plotGroups = plotGroups
2602 
2603  def getPlotGroups(self):
2604  return self._plotGroups
2605 
2606  def getPlotGroup(self, name):
2607  for pg in self._plotGroups:
2608  if pg.getName() == name:
2609  return pg
2610  raise Exception("No PlotGroup named '%s'" % name)
2611 
2612  def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False):
2613  """Create histograms from a list of TFiles.
2614 
2615  Arguments:
2616  dirsNEvents -- List of (TDirectory, nevents) pairs
2617  labels -- List of strings for legend labels corresponding the files
2618  isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2619  requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2620  """
2621 
2622  if len(dirsNEvents) != len(labels):
2623  raise Exception("len(dirsNEvents) should be len(labels), now they are %d and %d" % (len(dirsNEvents), len(labels)))
2624 
2625  self._labels = labels
2626 
2627  for pg in self._plotGroups:
2628  if pg.onlyForPileup() and not isPileupSample:
2629  continue
2630  pg.create(dirsNEvents, requireAllHistograms)
2631 
2632  def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory=""):
2633  """Draw and save all plots using settings of a given algorithm.
2634 
2635  Arguments:
2636  prefix -- Optional string for file name prefix (default None)
2637  separate -- Save the plots of a group to separate files instead of a file per group (default False)
2638  saveFormat -- String specifying the plot format (default '.pdf')
2639  ratio -- Add ratio to the plot (default True)
2640  directory -- Directory where to save the file (default "")
2641  """
2642  ret = []
2643 
2644  for pg in self._plotGroups:
2645  ret.extend(pg.draw(self._labels, prefix=prefix, separate=separate, saveFormat=saveFormat, ratio=ratio, directory=directory))
2646  return ret
2647 
2648 
2649  # These are to be overridden by derived classes for customisation
2650  def translateSubFolder(self, dqmSubFolderName):
2651  """Method called to (possibly) translate a subfolder name to more 'readable' form
2652 
2653  The implementation in this (base) class just returns the
2654  argument. The idea is that a deriving class might want to do
2655  something more complex (like trackingPlots.TrackingPlotFolder
2656  does)
2657  """
2658  return dqmSubFolderName
2659 
2660  def iterSelectionName(self, plotFolderName, translatedDqmSubFolder):
2661  """Iterate over possible selections name (used in output directory name and legend) from the name of PlotterFolder, and a return value of translateSubFolder"""
2662  ret = ""
2663  if plotFolderName != "":
2664  ret += "_"+plotFolderName
2665  if translatedDqmSubFolder is not None:
2666  ret += "_"+translatedDqmSubFolder
2667  yield ret
2668 
2669  def limitSubFolder(self, limitOnlyTo, translatedDqmSubFolder):
2670  """Return True if this subfolder should be processed
2671 
2672  Arguments:
2673  limitOnlyTo -- List/set/similar containing the translatedDqmSubFolder
2674  translatedDqmSubFolder -- Return value of translateSubFolder
2675  """
2676  return translatedDqmSubFolder in limitOnlyTo
2677 
2679  """Class to hold the original name and a 'translated' name of a subfolder in the DQM ROOT file"""
2680  def __init__(self, subfolder, translated):
2681  self.subfolder = subfolder
2682  self.translated = translated
2683 
2684  def equalTo(self, other):
2685  """Equality is defined by the 'translated' name"""
2686  return self.translated == other.translated
2687 
2689  """Plotter for one DQM folder.
2690 
2691  This class is supposed to be instantiated by the Plotter class (or
2692  PlotterItem, to be more specific), and not used directly by the
2693  user.
2694  """
2695  def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators):
2696  """
2697  Constructor
2698 
2699  Arguments:
2700  name -- Name of the folder (is used in the output directory naming)
2701  possibleDqmFolders -- List of strings for possible directories of histograms in TFiles
2702  dqmSubFolders -- List of lists of strings for list of subfolders per input file, or None if no subfolders
2703  plotFolder -- PlotFolder object
2704  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.
2705  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.
2706  tableCreators -- List of PlotterTableItem objects for tables to be created from this folder
2707  """
2708  self._name = name
2709  self._possibleDqmFolders = possibleDqmFolders
2710  self._plotFolder = plotFolder
2711  #self._dqmSubFolders = [map(lambda sf: DQMSubFolder(sf, self._plotFolder.translateSubFolder(sf)), lst) for lst in dqmSubFolders]
2712  if dqmSubFolders is None:
2713  self._dqmSubFolders = None
2714  else:
2715  # Match the subfolders between files in case the lists differ
2716  # equality is by the 'translated' name
2717  subfolders = {}
2718  for sf_list in dqmSubFolders:
2719  for sf in sf_list:
2720  sf_translated = self._plotFolder.translateSubFolder(sf)
2721  if sf_translated is not None and not sf_translated in subfolders:
2722  subfolders[sf_translated] = DQMSubFolder(sf, sf_translated)
2723 
2724  self._dqmSubFolders = subfolders.values()
2725  self._dqmSubFolders.sort(key=lambda sf: sf.subfolder)
2726 
2727  self._fallbackNames = fallbackNames
2728  self._fallbackDqmSubFolders = fallbackDqmSubFolders
2729  self._tableCreators = tableCreators
2730 
2731  def getName(self):
2732  return self._name
2733 
2734  def getPurpose(self):
2735  return self._plotFolder.getPurpose()
2736 
2737  def getPage(self):
2738  return self._plotFolder.getPage()
2739 
2740  def getSection(self):
2741  return self._plotFolder.getSection()
2742 
2743  def onlyForPileup(self):
2744  return self._plotFolder.onlyForPileup()
2745 
2746  def onlyForElectron(self):
2747  return self._plotFolder.onlyForElectron()
2748 
2750  return self._plotFolder.onlyForConversion()
2751 
2752  def onlyForBHadron(self):
2753  return self._plotFolder.onlyForBHadron()
2754 
2756  return self._possibleDqmFolders
2757 
2758  def getDQMSubFolders(self, limitOnlyTo=None):
2759  """Get list of subfolders, possibly limiting to some of them.
2760 
2761  Keyword arguments:
2762  limitOnlyTo -- Object depending on the PlotFolder type for limiting the set of subfolders to be processed
2763  """
2764 
2765  if self._dqmSubFolders is None:
2766  return [None]
2767 
2768  if limitOnlyTo is None:
2769  return self._dqmSubFolders
2770 
2771  return filter(lambda s: self._plotFolder.limitSubFolder(limitOnlyTo, s.translated), self._dqmSubFolders)
2772 
2773  def getTableCreators(self):
2774  return self._tableCreators
2775 
2776  def getSelectionNameIterator(self, dqmSubFolder):
2777  """Get a generator for the 'selection name', looping over the name and fallbackNames"""
2778  for name in [self._name]+self._fallbackNames:
2779  for selname in self._plotFolder.iterSelectionName(name, dqmSubFolder.translated if dqmSubFolder is not None else None):
2780  yield selname
2781 
2782  def getSelectionName(self, dqmSubFolder):
2783  return next(self.getSelectionNameIterator(dqmSubFolder))
2784 
2785  def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False):
2786  """Create histograms from a list of TFiles.
2787  Arguments:
2788  files -- List of TFiles
2789  labels -- List of strings for legend labels corresponding the files
2790  dqmSubFolder -- DQMSubFolder object for a subfolder (or None for no subfolder)
2791  isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
2792  requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
2793  """
2794 
2795  subfolder = dqmSubFolder.subfolder if dqmSubFolder is not None else None
2796  neventsHisto = self._plotFolder.getNumberOfEventsHistogram()
2797  dirsNEvents = []
2798 
2799  for tfile in files:
2800  ret = _getDirectoryDetailed(tfile, self._possibleDqmFolders, subfolder)
2801  # If file and any of possibleDqmFolders exist but subfolder does not, try the fallbacks
2803  for fallbackFunc in self._fallbackDqmSubFolders:
2804  fallback = fallbackFunc(subfolder)
2805  if fallback is not None:
2806  ret = _getDirectoryDetailed(tfile, self._possibleDqmFolders, fallback)
2807  if ret is not GetDirectoryCode.SubDirNotExist:
2808  break
2809  d = GetDirectoryCode.codesToNone(ret)
2810  nev = None
2811  if neventsHisto is not None and tfile is not None:
2812  hnev = _getObject(tfile, neventsHisto)
2813  if hnev is not None:
2814  nev = hnev.GetEntries()
2815  dirsNEvents.append( (d, nev) )
2816 
2817  self._plotFolder.create(dirsNEvents, labels, isPileupSample, requireAllHistograms)
2818 
2819  def draw(self, *args, **kwargs):
2820  """Draw and save all plots using settings of a given algorithm."""
2821  return self._plotFolder.draw(*args, **kwargs)
2822 
2823 
2825  """Instance of plotter that knows the directory content, holds many folders."""
2826  def __init__(self, folders):
2827  self._plotterFolders = filter(lambda f: f is not None, folders)
2828 
2829  def iterFolders(self, limitSubFoldersOnlyTo=None):
2830  for plotterFolder in self._plotterFolders:
2831  limitOnlyTo = None
2832  if limitSubFoldersOnlyTo is not None:
2833  limitOnlyTo = limitSubFoldersOnlyTo.get(plotterFolder.getName(), None)
2834 
2835  for dqmSubFolder in plotterFolder.getDQMSubFolders(limitOnlyTo=limitOnlyTo):
2836  yield plotterFolder, dqmSubFolder
2837 
2838 # Helper for Plotter
2840  def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[]):
2841  """ Constructor
2842 
2843  Arguments:
2844  name -- Name of the folder (is used in the output directory naming)
2845  possibleDirs -- List of strings for possible directories of histograms in TFiles
2846  plotFolder -- PlotFolder object
2847 
2848  Keyword arguments
2849  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.
2850  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.
2851  """
2852  self._name = name
2853  self._possibleDirs = possibleDirs
2854  self._plotFolder = plotFolder
2855  self._fallbackNames = fallbackNames
2856  self._fallbackDqmSubFolders = fallbackDqmSubFolders
2857  self._tableCreators = []
2858 
2859  def getName(self):
2860  return self._name
2861 
2862  def getPlotFolder(self):
2863  return self._plotFolder
2864 
2865  def appendTableCreator(self, tc):
2866  self._tableCreators.append(tc)
2867 
2868  def readDirs(self, files):
2869  """Read available subfolders from the files
2870 
2871  Arguments:
2872  files -- List of strings for paths to files, or list of TFiles
2873 
2874  For each file, loop over 'possibleDirs', and read the
2875  subfolders of first one that exists.
2876 
2877  Returns a PlotterFolder if at least one file for which one of
2878  'possibleDirs' exists. Otherwise, return None to signal that
2879  there is nothing available for this PlotFolder.
2880  """
2881  subFolders = None
2882  if self._plotFolder.loopSubFolders():
2883  subFolders = []
2884  possibleDirFound = False
2885  for fname in files:
2886  if fname is None:
2887  continue
2888 
2889  isOpenFile = isinstance(fname, ROOT.TFile)
2890  if isOpenFile:
2891  tfile = fname
2892  else:
2893  tfile = ROOT.TFile.Open(fname)
2894  for pd in self._possibleDirs:
2895  d = tfile.Get(pd)
2896  if d:
2897  possibleDirFound = True
2898  if subFolders is not None:
2899  subf = []
2900  for key in d.GetListOfKeys():
2901  if isinstance(key.ReadObj(), ROOT.TDirectory):
2902  subf.append(key.GetName())
2903  subFolders.append(subf)
2904  break
2905 
2906  if not isOpenFile:
2907  tfile.Close()
2908 
2909  if not possibleDirFound:
2910  return None
2911 
2912  return PlotterFolder(self._name, self._possibleDirs, subFolders, self._plotFolder, self._fallbackNames, self._fallbackDqmSubFolders, self._tableCreators)
2913 
2915  def __init__(self, possibleDirs, tableCreator):
2916  self._possibleDirs = possibleDirs
2917  self._tableCreator = tableCreator
2918 
2919  def create(self, openFiles, legendLabels, dqmSubFolder):
2920  if isinstance(dqmSubFolder, list):
2921  if len(dqmSubFolder) != len(openFiles):
2922  raise Exception("When dqmSubFolder is a list, len(dqmSubFolder) should be len(openFiles), now they are %d and %d" % (len(dqmSubFolder), len(openFiles)))
2923  else:
2924  dqmSubFolder = [dqmSubFolder]*len(openFiles)
2925  dqmSubFolder = [sf.subfolder if sf is not None else None for sf in dqmSubFolder]
2926 
2927  tbl = []
2928  for f, sf in zip(openFiles, dqmSubFolder):
2929  data = None
2930  tdir = _getDirectory(f, self._possibleDirs, sf)
2931  if tdir is not None:
2932  data = self._tableCreator.create(tdir)
2933  tbl.append(data)
2934 
2935  # Check if we have any content
2936  allNones = True
2937  colLen = 0
2938  for col in tbl:
2939  if col is not None:
2940  allNones = False
2941  colLen = len(col)
2942  break
2943  if allNones:
2944  return None
2945 
2946  # Replace all None columns with lists of column length
2947  for i in xrange(len(tbl)):
2948  if tbl[i] is None:
2949  tbl[i] = [None]*colLen
2950 
2951  return html.Table(columnHeaders=legendLabels, rowHeaders=self._tableCreator.headers(), table=tbl,
2952  purpose=self._tableCreator.getPurpose(), page=self._tableCreator.getPage(), section=self._tableCreator.getSection(dqmSubFolder[0]))
2953 
2954 class Plotter:
2955  """Contains PlotFolders, i.e. the information what plots to do, and creates a helper object to actually produce the plots."""
2956  def __init__(self):
2957  self._plots = []
2958  _setStyle()
2959  ROOT.TH1.AddDirectory(False)
2960 
2961  def append(self, *args, **kwargs):
2962  """Append a plot folder to the plotter.
2963 
2964  All arguments are forwarded to the constructor of PlotterItem.
2965  """
2966  self._plots.append(PlotterItem(*args, **kwargs))
2967 
2968  def appendTable(self, attachToFolder, *args, **kwargs):
2969  for plotterItem in self._plots:
2970  if plotterItem.getName() == attachToFolder:
2971  plotterItem.appendTableCreator(PlotterTableItem(*args, **kwargs))
2972  return
2973  raise Exception("Did not find plot folder '%s' when trying to attach a table creator to it" % attachToFolder)
2974 
2975  def clear(self):
2976  """Remove all plot folders and tables"""
2977  self._plots = []
2978 
2980  return [item.getName() for item in self._plots]
2981 
2982  def getPlotFolders(self):
2983  return [item.getPlotFolder() for item in self._plots]
2984 
2985  def getPlotFolder(self, name):
2986  for item in self._plots:
2987  if item.getName() == name:
2988  return item.getPlotFolder()
2989  raise Exception("No PlotFolder named '%s'" % name)
2990 
2991  def readDirs(self, *files):
2992  """Returns PlotterInstance object, which knows how exactly to produce the plots for these files"""
2993  return PlotterInstance([plotterItem.readDirs(files) for plotterItem in self._plots])
def remove(self, name)
Definition: plotting.py:2292
def create(self, tdirectory)
Definition: plotting.py:868
def iterSelectionName(self, plotFolderName, translatedDqmSubFolder)
Definition: plotting.py:2660
def __init__(self, x, y, text, size=None, bold=True, align="left", color=ROOT.kBlack, font=None)
Definition: plotting.py:1530
def getName(self)
Definition: plotting.py:1676
def onlyForPileup(self)
Definition: plotting.py:2572
def __init__(self)
Definition: plotting.py:1673
def append(self, plotGroup)
Definition: plotting.py:2597
def getName(self)
Definition: plotting.py:2286
def _setStyle()
Definition: plotting.py:18
def _getYmax(obj, limitToNonZeroContent=False)
Definition: plotting.py:384
def loopSubFolders(self)
Definition: plotting.py:2568
def _copyStyle(src, dst)
Definition: plotting.py:1659
def __init__(self, name, plots, kwargs)
Definition: plotting.py:2241
def __init__(self, name, nameA, nameB, title="")
Definition: plotting.py:759
def getPossibleDQMFolders(self)
Definition: plotting.py:2755
def adjustMarginLeft(self, adjust)
Definition: plotting.py:1468
def _findBoundsY(th1s, ylog, ymin=None, ymax=None, coverage=None, coverageRange=None)
Definition: plotting.py:523
def set(self, plotGroups)
Definition: plotting.py:2600
def setXTitle(self, title)
Definition: plotting.py:1384
def setLogx(self, log)
Definition: plotting.py:1348
def readDirs(self, files)
Definition: plotting.py:2991
def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory="")
Definition: plotting.py:2325
def setXLabelSize(self, size)
Definition: plotting.py:1488
def _drawFrame(pad, bounds, zmax=None, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None, suffix="")
Definition: plotting.py:1183
def __init__(self, name, histo, func, title="")
Definition: plotting.py:807
def setGridx(self, grid)
Definition: plotting.py:1462
def _getOrCreateObject(tdirectory, nameOrCreator)
Definition: plotting.py:54
def iterFolders(self, limitSubFoldersOnlyTo=None)
Definition: plotting.py:2829
def redrawAxis(self)
Definition: plotting.py:1410
def setYTitleSize(self, size)
Definition: plotting.py:1494
def setXLabelSize(self, size)
Definition: plotting.py:1393
def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None)
Definition: plotting.py:455
def _modifyPadForRatio(self, pad)
Definition: plotting.py:2464
def Draw(self, options="")
Definition: plotting.py:1643
def equalTo(self, other)
Definition: plotting.py:2684
def __init__(self, name, xhistoName, yhistoName, zaxis=False)
Definition: plotting.py:1119
def adjustMarginRight(self, adjust)
Definition: plotting.py:1275
def addText(self, text)
Definition: plotting.py:1609
def __init__(self, possibleDirs, tableCreator)
Definition: plotting.py:2915
def getTableCreators(self)
Definition: plotting.py:2773
def __init__(self, xmin, ymin, xmax, ymax, lineheight=0.04, fillColor=ROOT.kWhite, transparent=True, kwargs)
Definition: plotting.py:1581
def _getYmin(obj, limitToNonZeroContent=False)
Definition: plotting.py:369
def create(self, args, kwargs)
Definition: plotting.py:1682
def setXTitleSize(self, size)
Definition: plotting.py:1287
def create(self, tdirectory)
Definition: plotting.py:1139
def adjustMarginLeft(self, adjust)
Definition: plotting.py:1363
def clear(self)
Definition: plotting.py:2299
def setXLabelSize(self, size)
Definition: plotting.py:1293
def _getYminIgnoreOutlier(th1)
Definition: plotting.py:402
def append(self, args, kwargs)
Definition: plotting.py:2961
def getDQMSubFolders(self, limitOnlyTo=None)
Definition: plotting.py:2758
def _getXmax(obj, limitToNonZeroContent=False)
Definition: plotting.py:353
def append(self, plot)
Definition: plotting.py:2302
def setGridx(self, grid)
Definition: plotting.py:1355
def setLogy(self, log)
Definition: plotting.py:1459
def setGridy(self, grid)
Definition: plotting.py:1266
def __init__(self, pad, bounds, zmax, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ybinlabels=None)
Definition: plotting.py:1237
def drawRatioUncertainty(self)
Definition: plotting.py:1679
def setZTitle(self, title)
Definition: plotting.py:1500
def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor)
Definition: plotting.py:1430
def setZTitleOffset(self, offset)
Definition: plotting.py:1503
def setXTitleSize(self, size)
Definition: plotting.py:1482
def __init__(self, pad, bounds, zmax, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, ratioYTitle=_ratioYTitle)
Definition: plotting.py:1310
def getNumberOfHistograms(self)
Definition: plotting.py:1840
def create(self, tdirectoryNEvents, requireAllHistograms=False)
Definition: plotting.py:2315
bool equal(const T &first, const T &second)
Definition: Equal.h:34
def move(self, dx=0, dy=0, dw=0, dh=0)
Definition: plotting.py:1617
def __init__(self, name, kwargs)
Definition: plotting.py:1693
def getPlotGroups(self)
Definition: plotting.py:2603
def create(self, openFiles, legendLabels, dqmSubFolder)
Definition: plotting.py:2919
def _calculateRatios(histos, ratioUncertainty=False)
Definition: plotting.py:145
def appendTable(self, attachToFolder, args, kwargs)
Definition: plotting.py:2968
def setYTitleOffset(self, offset)
Definition: plotting.py:1302
def setYTitle(self, title)
Definition: plotting.py:1396
def clear(self)
Definition: plotting.py:2975
def create(self, tdirectory)
Definition: plotting.py:824
def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False, directory="")
Definition: plotting.py:2484
def setYTitleSize(self, size)
Definition: plotting.py:1299
def onlyForElectron(self)
Definition: plotting.py:2576
def draw(name, histos, styles=_defaultStyles, legendLabels=[], kwargs)
def _setStats(self, histos, startingX, startingY)
Definition: plotting.py:1935
def setLogy(self, log)
Definition: plotting.py:1260
def getSelectionName(self, dqmSubFolder)
Definition: plotting.py:2782
def _getDirectory(args, kwargs)
Definition: plotting.py:94
def setYTitle(self, title)
Definition: plotting.py:1296
def Draw(self, options=None)
Definition: plotting.py:1568
def draw(self, pad, ratio, ratioFactor, nrows)
Definition: plotting.py:1989
Definition: style.py:1
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def setTitle(self, title)
Definition: plotting.py:1381
def setXTitle(self, title)
Definition: plotting.py:1284
def getPlots(self)
Definition: plotting.py:2289
def create(self, tdirectory)
Definition: plotting.py:782
def isRatio(self, ratio)
Definition: plotting.py:1854
def setGridx(self, grid)
Definition: plotting.py:1263
def readDirs(self, files)
Definition: plotting.py:2868
def _mergeBinLabelsY(histos)
Definition: plotting.py:706
def getPlotFolder(self)
Definition: plotting.py:2862
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def adjustMarginRight(self, adjust)
Definition: plotting.py:1472
def adjustMarginLeft(self, adjust)
Definition: plotting.py:1269
def onlyForPileup(self)
Definition: plotting.py:2311
#define end
Definition: vmac.h:39
def getPlot(self, name)
Definition: plotting.py:2305
def setProperties(self, kwargs)
Definition: plotting.py:2280
def __str__(self)
Definition: plotting.py:778
T min(T a, T b)
Definition: MathUtil.h:58
def addToLegend(self, legend, legendLabels, denomUncertainty)
Definition: plotting.py:2218
def _getYmaxWithError(th1)
Definition: plotting.py:399
def setXTitleOffset(self, offset)
Definition: plotting.py:1290
def _getDirectoryDetailed(tfile, possibleDirs, subDir=None)
Definition: plotting.py:70
def setXTitleOffset(self, size)
Definition: plotting.py:1485
def draw(self, args, kwargs)
Definition: plotting.py:2530
def create(self, tdirectory)
Definition: plotting.py:984
def onlyForConversion(self)
Definition: plotting.py:2579
def _getYminMaxAroundMedian(obj, coverage, coverageRange=None)
Definition: plotting.py:419
def getPlotFolder(self, name)
Definition: plotting.py:2985
def setYTitleOffset(self, offset)
Definition: plotting.py:1497
def setLogy(self, log)
Definition: plotting.py:1352
def __init__(self, name, histo, title="")
Definition: plotting.py:901
def __str__(self)
Definition: plotting.py:820
def onlyForConversion(self)
Definition: plotting.py:2749
def limitSubFolder(self, limitOnlyTo, translatedDqmSubFolder)
Definition: plotting.py:2669
def setXTitleSize(self, size)
Definition: plotting.py:1387
def setYTitleRatio(self, title)
Definition: plotting.py:1399
def drawRatioUncertainty(self)
Definition: plotting.py:1867
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def create(self, dirsNEvents, labels, isPileupSample=True, requireAllHistograms=False)
Definition: plotting.py:2612
def setGridy(self, grid)
Definition: plotting.py:1465
def getNumberOfHistograms(self)
Definition: plotting.py:1688
def __init__(self, name, assoc, dup, reco, title="")
Definition: plotting.py:844
def onlyForBHadron(self)
Definition: plotting.py:2752
void divide(MonitorElement *eff, const MonitorElement *numerator, const MonitorElement *denominator)
Function to fill an efficiency histograms with binomial errors.
Definition: Histograms.h:23
def adjustMarginRight(self, adjust)
Definition: plotting.py:1372
def getPlotFolders(self)
Definition: plotting.py:2982
def __init__(self, subfolder, translated)
Definition: plotting.py:2680
def create(self, tdirectory)
Definition: plotting.py:916
def setXTitleOffset(self, offset)
Definition: plotting.py:1390
def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None, ignoreMissingBins=False, minExistingBins=None, originalOrder=False, reorder=None)
Definition: plotting.py:946
def __init__(self, name, mapping, normalizeTo=None)
Definition: plotting.py:1073
def _normalize(self)
Definition: plotting.py:1976
def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True)
Definition: plotting.py:2468
def translateSubFolder(self, dqmSubFolderName)
Definition: plotting.py:2650
def __str__(self)
Definition: plotting.py:1135
def _drawSeparate(self, legendLabels, prefix, saveFormat, ratio, directory)
Definition: plotting.py:2402
def __init__(self, folders)
Definition: plotting.py:2826
def setTitle(self, title)
Definition: plotting.py:1281
def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames, fallbackDqmSubFolders, tableCreators)
Definition: plotting.py:2695
def setXTitle(self, title)
Definition: plotting.py:1479
def setGridy(self, grid)
Definition: plotting.py:1359
#define begin
Definition: vmac.h:32
def setYTitleSize(self, size)
Definition: plotting.py:1402
def setYTitle(self, title)
Definition: plotting.py:1491
def _th1RemoveEmptyBins(histos, xbinlabels)
Definition: plotting.py:603
def onlyForBHadron(self)
Definition: plotting.py:2582
def __init__(self)
Definition: plotting.py:2956
def isEmpty(self)
Definition: plotting.py:1844
def _getXmin(obj, limitToNonZeroContent=False)
Definition: plotting.py:337
def onlyForElectron(self)
Definition: plotting.py:2746
def append(self, args, kwargs)
Definition: plotting.py:2520
def create(self, tdirNEvents, requireAllHistograms=False)
Definition: plotting.py:1885
def getPlotGroup(self, name)
Definition: plotting.py:2606
def getPage(self)
Definition: plotting.py:2588
def appendTableCreator(self, tc)
Definition: plotting.py:2865
def isTGraph2D(self)
Definition: plotting.py:1848
def _modifyPadForRatio(pad, ratioFactor)
Definition: plotting.py:116
def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False)
Definition: plotting.py:2785
def create(self, tdirectoryNEvents, requireAllHistograms=False)
Definition: plotting.py:2523
def _createCanvas(name, width, height)
Definition: plotting.py:106
def setYTitleOffset(self, offset)
Definition: plotting.py:1406
def redrawAxis(self)
Definition: plotting.py:1305
auto wrap(F iFunc) -> decltype(iFunc())
def setLogx(self, log)
Definition: plotting.py:1257
def onlyForPileup(self)
Definition: plotting.py:2743
def clone(self, kwargs)
Definition: plotting.py:1833
def __init__(self, name, plot, ncols=2, onlyForPileup=False)
Definition: plotting.py:2515
def getPlotFolderNames(self)
Definition: plotting.py:2979
def getNumberOfEventsHistogram(self)
Definition: plotting.py:2594
def _mergeBinLabels(labelsAll)
Definition: plotting.py:709
def getPurpose(self)
Definition: plotting.py:2585
def __init__(self, plotGroups, kwargs)
Definition: plotting.py:2538
def setLogx(self, log)
Definition: plotting.py:1456
def draw(self, args, kwargs)
Definition: plotting.py:2819
def _th1ToOrderedDict(th1, renameBin=None)
Definition: plotting.py:97
def isEmpty(self)
Definition: plotting.py:1685
def _th2RemoveEmptyBins(histos, xbinlabels, ybinlabels)
Definition: plotting.py:643
def getSelectionNameIterator(self, dqmSubFolder)
Definition: plotting.py:2776
def getName(self)
Definition: plotting.py:1859
def create(self, tdirectory)
Definition: plotting.py:1091
def setProperties(self, kwargs)
Definition: plotting.py:1827
def _mergeBinLabelsX(histos)
Definition: plotting.py:703
def _createOne(self, name, index, tdir, nevents)
Definition: plotting.py:1871
def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=True, directory="")
Definition: plotting.py:2632
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run
def setTitle(self, title)
Definition: plotting.py:1476
def _th1IncludeOnlyBins(histos, xbinlabels)
Definition: plotting.py:740
def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[], fallbackDqmSubFolders=[])
Definition: plotting.py:2840
def _getObject(tdirectory, name)
Definition: plotting.py:46
def getSection(self)
Definition: plotting.py:2591