CMS 3D CMS Logo

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