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