6 ROOT.gROOT.SetBatch(
True)
7 ROOT.PyConfig.IgnoreCommandLineOptions =
True
10 obj = tdirectory.Get(name)
12 print "Did not find {obj} from {dir}".
format(obj=name, dir=tdirectory.GetPath())
17 if hasattr(nameOrCreator,
"create"):
18 return nameOrCreator.create(tdirectory)
22 if isinstance(obj, ROOT.TH1):
23 xaxis = obj.GetXaxis()
24 return xaxis.GetBinLowEdge(xaxis.GetFirst())
25 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
26 return min([obj.GetX()[i]
for i
in xrange(0, obj.GetN())])*0.9
27 raise Exception(
"Unsupported type %s" % str(obj))
30 if isinstance(obj, ROOT.TH1):
31 xaxis = obj.GetXaxis()
32 return xaxis.GetBinUpEdge(xaxis.GetLast())
33 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
34 return max([obj.GetX()[i]
for i
in xrange(0, obj.GetN())])*1.02
35 raise Exception(
"Unsupported type %s" % str(obj))
38 if isinstance(obj, ROOT.TH1):
39 return obj.GetMinimum()
40 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
41 return min([obj.GetY()[i]
for i
in xrange(0, obj.GetN())])
42 raise Exception(
"Unsupported type %s" % str(obj))
45 if isinstance(obj, ROOT.TH1):
46 return obj.GetMaximum()
47 elif isinstance(obj, ROOT.TGraph)
or isinstance(obj, ROOT.TGraph2D):
48 return max([obj.GetY()[i]
for i
in xrange(0, obj.GetN())])
49 raise Exception(
"Unsupported type %s" % str(obj))
52 return max([th1.GetBinContent(i)+th1.GetBinError(i)
for i
in xrange(1, th1.GetNbinsX()+1)])
55 yvals =
filter(
lambda n: n>0, [th1.GetBinContent(i)
for i
in xrange(1, th1.GetNbinsX()+1)])
58 return th1.GetMinimum()
63 ind_min = len(yvals)-1 - int(len(yvals)*0.95)
64 min_val = yvals[ind_min]
65 for i
in xrange(0, ind_min):
66 if yvals[i] > 0.1*min_val:
71 def _findBounds(th1s, ylog, xmin=None, xmax=None, ymin=None, ymax=None):
72 """Find x-y axis boundaries encompassing a list of TH1s if the bounds are not given in arguments.
76 ylog -- Boolean indicating if y axis is in log scale or not (affects the automatic ymax)
79 xmin -- Minimum x value; if None, take the minimum of TH1s
80 xmax -- Maximum x value; if None, take the maximum of TH1s
81 xmin -- Minimum y value; if None, take the minimum of TH1s
82 xmax -- Maximum y value; if None, take the maximum of TH1s
94 if xmin
is None or xmax
is None or ymin
is None or ymax
is None or isinstance(ymin, list)
or isinstance(ymax, list):
102 if ylog
and isinstance(ymin, list):
115 elif isinstance(ymin, list):
116 ym_unscaled =
min(ymins)
117 ym = y_scale_min(ym_unscaled)
118 ymins_below =
filter(
lambda y: y<=ym, ymin)
119 if len(ymins_below) == 0:
121 if ym_unscaled < ymin:
122 print "Histogram minimum y %f is below all given ymin values %s, using the smallest one" % (ym, str(ymin))
124 ymin =
max(ymins_below)
127 ymax = y_scale_max(
max(ymaxs))
128 elif isinstance(ymax, list):
129 ym_unscaled =
max(ymaxs)
130 ym = y_scale_max(ym_unscaled)
131 ymaxs_above =
filter(
lambda y: y>ym, ymax)
132 if len(ymaxs_above) == 0:
134 if ym_unscaled > ymax:
135 print "Histogram maximum y %f is above all given ymax values %s, using the maximum one" % (ym_unscaled, str(ymax))
137 ymax =
min(ymaxs_above)
141 th1.GetXaxis().SetRangeUser(xmin, xmax)
142 th1.GetYaxis().SetRangeUser(ymin, ymax)
144 return (xmin, ymin, xmax, ymax)
147 """Class for subtracting two histograms"""
152 name -- String for name of the resulting histogram (A-B)
153 nameA -- String for A histogram
154 nameB -- String for B histogram
157 title -- String for a title of the resulting histogram (default "")
159 Uncertainties are calculated with the assumption that B is a
160 subset of A, and the histograms contain event counts.
168 """String representation, returns the name"""
172 """Create and return the fake+duplicate histogram from a TDirectory"""
176 if not histoA
or not histoB:
179 ret = histoA.Clone(self.
_name)
182 for i
in xrange(1, histoA.GetNbinsX()+1):
183 val = histoA.GetBinContent(i)-histoB.GetBinContent(i)
184 ret.SetBinContent(i, val)
185 ret.SetBinError(i, math.sqrt(val))
190 """Class to calculate the fake+duplicate rate"""
191 def __init__(self, name, assoc, dup, reco, title=""):
195 name -- String for the name of the resulting efficiency histogram
196 assoc -- String for the name of the "associated" histogram
197 dup -- String for the name of the "duplicates" histogram
198 reco -- String for the name of the "reco" (denominator) histogram
201 title -- String for a title of the resulting histogram (default "")
203 The result is calculated as 1 - (assoc - dup) / reco
212 """String representation, returns the name"""
216 """Create and return the fake+duplicate histogram from a TDirectory"""
223 if not hassoc
or not hdup
or not hreco:
226 hfakedup = hreco.Clone(self.
_name)
227 hfakedup.SetTitle(self.
_title)
229 for i
in xrange(1, hassoc.GetNbinsX()+1):
230 numerVal = hassoc.GetBinContent(i) - hdup.GetBinContent(i)
231 denomVal = hreco.GetBinContent(i)
233 fakedupVal = (1 - numerVal / denomVal)
if denomVal != 0.0
else 0.0
234 errVal = math.sqrt(fakedupVal*(1-fakedupVal)/denomVal)
if (denomVal != 0.0
and fakedupVal <= 1)
else 0.0
236 hfakedup.SetBinContent(i, fakedupVal)
237 hfakedup.SetBinError(i, errVal)
242 """Class to create a histogram by aggregating bins of another histogram to a bin of the resulting histogram."""
243 def __init__(self, name, histoName, mapping, normalizeTo=None, scale=None, renameBin=None):
247 name -- String for the name of the resulting histogram
248 histoName -- String for the name of the source histogram
249 mapping -- Dictionary or list for mapping the bins (see below)
252 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.
253 scale -- Optional number for scaling the histogram (passed to ROOT.TH1.Scale())
254 renameBin -- Optional function (string -> string) to rename the bins of the input histogram
256 Mapping structure (mapping):
258 Dictionary (you probably want to use collections.OrderedDict)
259 should be a mapping from the destination bin label to a list
260 of source bin labels ("dst -> [src]").
262 If the mapping is a list, it should only contain the source
263 bin labels. In this case, the resulting histogram contains a
264 subset of the bins of the source histogram.
274 """String representation, returns the name"""
278 """Create and return the histogram from a TDirectory"""
287 for i
in xrange(1, th1.GetNbinsX()+1):
288 binLabel = th1.GetXaxis().GetBinLabel(i)
291 values[binLabel] = (th1.GetBinContent(i), th1.GetBinError(i))
294 for i, label
in enumerate(self.
_mapping):
296 result.SetBinContent(i+1, values[label][0])
297 result.SetBinError(i+1, values[label][1])
300 result.GetXaxis().SetBinLabel(i+1, label)
302 for i, (key, labels)
in enumerate(self._mapping.iteritems()):
307 sumTime += values[l][0]
308 sumErrorSq += values[l][1]**2
311 result.SetBinContent(i+1, sumTime)
312 result.SetBinError(i+1, math.sqrt(sumErrorSq))
313 result.GetXaxis().SetBinLabel(i+1, key)
318 print "Trying to normalize {name} to {binlabel}, which does not exist".
format(name=self.
_name, binlabel=self.
_normalizeTo)
320 value = th1.GetBinContent(bin)
322 result.Scale(1/value)
324 if self.
_scale is not None:
330 """Class to create a histogram by aggregaging integrals of another histoggrams."""
331 def __init__(self, name, mapping, normalizeTo=None):
335 name -- String for the name of the resulting histogram
336 mapping -- Dictionary for mapping the bin label to a histogram name
339 normalizeTo -- Optional string for a histogram. If given, all bins of the resulting histograqm are divided by the integral of this histogram.
346 """String representation, returns the name"""
350 """Create and return the histogram from a TDirectory"""
352 for key, histoName
in self._mapping.iteritems():
356 result.append( (key, th1.Integral(0, th1.GetNbinsX()+1)) )
360 res = ROOT.TH1F(self.
_name, self.
_name, len(result), 0, len(result))
362 for i, (name, count)
in enumerate(result):
363 res.SetBinContent(i+1, count)
364 res.GetXaxis().SetBinLabel(i+1, name)
370 scale = th1.Integral(0, th1.GetNbinsX()+1)
376 """Class to construct a ROC curve (e.g. efficiency vs. fake rate) from two histograms"""
377 def __init__(self, name, xhistoName, yhistoName, zaxis=False):
381 name -- String for the name of the resulting histogram
382 xhistoName -- String for the name of the x-axis histogram (or another "creator" object)
383 yhistoName -- String for the name of the y-axis histogram (or another "creator" object)
386 zaxis -- If set to True (default False), create a TGraph2D with z axis showing the cut value (recommended drawStyle 'pcolz')
394 """String representation, returns the name"""
398 """Create and return the histogram from a TDirectory"""
401 if xhisto
is None or yhisto
is None:
412 for i
in xrange(1, xhisto.GetNbinsX()+1):
413 x.append(xhisto.GetBinContent(i))
414 xerrup.append(xhisto.GetBinError(i))
415 xerrdown.append(xhisto.GetBinError(i))
417 y.append(yhisto.GetBinContent(i))
418 yerrup.append(yhisto.GetBinError(i))
419 yerrdown.append(yhisto.GetBinError(i))
421 z.append(xhisto.GetXaxis().GetBinUpEdge(i))
423 arr =
lambda v: array.array(
"d", v)
426 gr = ROOT.TGraph2D(len(x), arr(x), arr(y), arr(z))
428 gr = ROOT.TGraphAsymmErrors(len(x), arr(x), arr(y), arr(xerrdown), arr(xerrup), arr(yerrdown), arr(yerrup))
434 _plotStylesColor = [4, 2, ROOT.kBlack, ROOT.kOrange+7, ROOT.kMagenta-3]
435 _plotStylesMarker = [21, 20, 22, 34, 33]
437 def _drawFrame(pad, bounds, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None, suffix=""):
438 """Function to draw a frame
441 pad -- TPad to where the frame is drawn
442 bounds -- List or 4-tuple for (xmin, ymin, xmax, ymax)
445 xbinlabels -- Optional list of strings for x axis bin labels
446 xbinlabelsize -- Optional number for the x axis bin label size
447 xbinlabeloption -- Optional string for the x axis bin options (passed to ROOT.TH1.LabelsOption())
448 suffix -- Optional string for a postfix of the frame name
450 if xbinlabels
is None:
451 frame = pad.DrawFrame(*bounds)
454 nbins = len(xbinlabels)
455 frame = ROOT.TH1F(
"hframe"+suffix,
"", nbins, bounds[0], bounds[2])
456 frame.SetBit(ROOT.TH1.kNoStats)
457 frame.SetBit(ROOT.kCanDelete)
458 frame.SetMinimum(bounds[1])
459 frame.SetMaximum(bounds[3])
460 frame.GetYaxis().SetLimits(bounds[1], bounds[3])
463 xaxis = frame.GetXaxis()
464 for i
in xrange(nbins):
465 xaxis.SetBinLabel(i+1, xbinlabels[i])
466 if xbinlabelsize
is not None:
467 xaxis.SetLabelSize(xbinlabelsize)
468 if xbinlabeloption
is not None:
469 frame.LabelsOption(xbinlabeloption)
474 """Class for creating and managing a frame for a simple, one-pad plot"""
475 def __init__(self, pad, bounds, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None):
488 self._frame.GetYaxis().SetTitleOffset(self._frame.GetYaxis().GetTitleOffset()*yoffsetFactor)
489 self._frame.GetXaxis().SetTitleOffset(self._frame.GetXaxis().GetTitleOffset()*xoffsetFactor)
493 self._pad.SetLogx(log)
496 self._pad.SetLogy(log)
499 self._pad.SetGridx(grid)
502 self._pad.SetGridy(grid)
505 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
511 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
517 self._frame.SetTitle(title)
520 self._frame.GetXaxis().SetTitle(title)
523 self._frame.GetXaxis().SetTitleSize(size)
526 self._frame.GetXaxis().SetTitleOffset(offset)
529 self._frame.GetXaxis().SetLabelSize(size)
532 self._frame.GetYaxis().SetTitle(title)
535 self._frame.GetYaxis().SetTitleSize(size)
538 self._frame.GetYaxis().SetTitleOffset(offset)
541 self._pad.RedrawAxis()
544 """Class for creating and managing a frame for a ratio plot with two subpads"""
545 def __init__(self, pad, bounds, ratioBounds, ratioFactor, nrows, xbinlabels=None, xbinlabelsize=None, xbinlabeloption=None):
548 if xbinlabels
is not None:
555 self._frame.GetXaxis().SetLabelSize(0)
556 self._frame.GetXaxis().SetTitleSize(0)
558 yoffsetFactor = ratioFactor
559 divisionPoint = 1-1/ratioFactor
560 xoffsetFactor = 1/divisionPoint
574 self._frame.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
575 self._frameRatio.GetYaxis().SetLabelSize(int(self._frameRatio.GetYaxis().GetLabelSize()*0.8))
576 self._frameRatio.GetYaxis().SetTitleOffset(self._frameRatio.GetYaxis().GetTitleOffset()*yoffsetFactor)
577 self._frameRatio.GetXaxis().SetTitleOffset(self._frameRatio.GetXaxis().GetTitleOffset()*xoffsetFactor)
579 self._frameRatio.GetYaxis().SetNdivisions(4, 5, 0)
581 self._frameRatio.GetYaxis().SetTitle(
"Ratio")
584 self._pad.SetLogx(log)
585 self._padRatio.SetLogx(log)
588 self._pad.SetLogy(log)
591 self._pad.SetGridx(grid)
592 self._padRatio.SetGridx(grid)
595 self._pad.SetGridy(grid)
596 self._padRatio.SetGridy(grid)
599 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
600 self._padRatio.SetLeftMargin(self._padRatio.GetLeftMargin()+adjust)
605 self._frameRatio.Draw(
"")
608 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
609 self._padRatio.SetRightMargin(self._padRatio.GetRightMargin()+adjust)
614 self._frameRatio.Draw(
"")
617 self._frame.SetTitle(title)
620 self._frameRatio.GetXaxis().SetTitle(title)
623 self._frameRatio.GetXaxis().SetTitleSize(size)
626 self._frameRatio.GetXaxis().SetTitleOffset(offset)
629 self._frameRatio.GetXaxis().SetLabelSize(size)
632 self._frame.GetYaxis().SetTitle(title)
635 self._frameRatio.GetYaxis().SetTitle(title)
638 self._frame.GetYaxis().SetTitleSize(size)
639 self._frameRatio.GetYaxis().SetTitleSize(size)
642 self._frame.GetYaxis().SetTitleOffset(offset)
643 self._frameRatio.GetYaxis().SetTitleOffset(offset)
646 self._padRatio.RedrawAxis()
647 self._pad.RedrawAxis()
656 self.
_coverPad = ROOT.TPad(
"coverpad",
"coverpad", xmin, ymin, xmax, ymax)
657 self._coverPad.SetBorderMode(0)
658 self._coverPad.Draw()
664 """Class for creating and managing a frame for a plot from TGraph2D"""
665 def __init__(self, pad, bounds, histos, ratioOrig, ratioFactor):
668 self.
_pad = pad.cd(1)
672 (xlow, ylow, width, height) = (self._pad.GetXlowNDC(), self._pad.GetYlowNDC(), self._pad.GetWNDC(), self._pad.GetHNDC())
676 bottomMargin = self._pad.GetBottomMargin()
677 bottomMarginNew = ROOT.gStyle.GetPadBottomMargin()
679 ylowNew = yup - (1-bottomMargin)/(1-bottomMarginNew) * (yup-ylow)
680 topMarginNew = self._pad.GetTopMargin() * (yup-ylow)/(yup-ylowNew)
682 self._pad.SetPad(xlow, ylowNew, xup, yup)
683 self._pad.SetTopMargin(topMarginNew)
684 self._pad.SetBottomMargin(bottomMarginNew)
686 self.
_view = ROOT.TView.CreateView()
687 self._view.SetRange(bounds[0], bounds[1], 0, bounds[2], bounds[3], 20)
689 self._view.ShowAxis()
709 self._pad.SetLeftMargin(self._pad.GetLeftMargin()+adjust)
713 self._pad.SetRightMargin(self._pad.GetRightMargin()+adjust)
741 self._firstHisto.GetZaxis().SetTitle(title)
744 self._firstHisto.GetZaxis().SetTitleOffset(offset)
748 ROOT.TAxis3D.ToggleRulers()
749 ROOT.TAxis3D.ToggleRulers()
750 axis = ROOT.TAxis3D.GetPadAxis()
751 axis.SetLabelColor(ROOT.kBlack);
752 axis.SetAxisColor(ROOT.kBlack);
757 if hasattr(self,
"_xtitle"):
758 axis.GetXaxis().SetTitle(self.
_xtitle)
759 if hasattr(self,
"_xtitlesize"):
761 if hasattr(self,
"_xlabelsize"):
762 axis.GetXaxis().SetLabelSize(self._labelsize)
763 if hasattr(self,
"_ytitle"):
764 axis.GetYaxis().SetTitle(self.
_ytitle)
765 if hasattr(self,
"_ytitlesize"):
767 if hasattr(self,
"_ytitleoffset"):
772 if hasattr(src,
"GetLineColor")
and hasattr(dst,
"SetLineColor"):
773 properties.extend([
"LineColor",
"LineStyle",
"LineWidth"])
774 if hasattr(src,
"GetFillColor")
and hasattr(dst,
"SetFillColor"):
775 properties.extend([
"FillColor",
"FillStyle"])
776 if hasattr(src,
"GetMarkerColor")
and hasattr(dst,
"SetMarkerColor"):
777 properties.extend([
"MarkerColor",
"MarkerSize",
"MarkerStyle"])
779 for prop
in properties:
780 getattr(dst,
"Set"+prop)(getattr(src,
"Get"+prop)())
783 """Represents one plot, comparing one or more histograms."""
788 name -- String for name of the plot, or Efficiency object
791 title -- String for a title of the plot (default None)
792 xtitle -- String for x axis title (default None)
793 xtitlesize -- Float for x axis title size (default None)
794 xtitleoffset -- Float for x axis title offset (default None)
795 xlabelsize -- Float for x axis label size (default None)
796 ytitle -- String for y axis title (default None)
797 ytitlesize -- Float for y axis title size (default None)
798 ytitleoffset -- Float for y axis title offset (default None)
799 ztitle -- String for z axis title (default None)
800 ztitleoffset -- Float for z axis title offset (default None)
801 xmin -- Float for x axis minimum (default None, i.e. automatic)
802 xmax -- Float for x axis maximum (default None, i.e. automatic)
803 ymin -- Float for y axis minimum (default 0)
804 ymax -- Float for y axis maximum (default None, i.e. automatic)
805 xlog -- Bool for x axis log status (default False)
806 ylog -- Bool for y axis log status (default False)
807 xgrid -- Bool for x axis grid status (default True)
808 ygrid -- Bool for y axis grid status (default True)
809 stat -- Draw stat box? (default False)
810 fit -- Do gaussian fit? (default False)
811 statx -- Stat box x coordinate (default 0.65)
812 staty -- Stat box y coordinate (default 0.8)
813 statyadjust -- List of floats for stat box y coordinate adjustments (default None)
814 normalizeToUnitArea -- Normalize histograms to unit area? (default False)
815 profileX -- Take histograms via ProfileX()? (default False)
816 fitSlicesY -- Take histograms via FitSlicesY() (default False)
817 rebinX -- rebin x axis (default None)
818 scale -- Scale histograms by a number (default None)
819 xbinlabels -- List of x axis bin labels (if given, default None)
820 xbinlabelsize -- Size of x axis bin labels (default None)
821 xbinlabeloption -- Option string for x axis bin labels (default None)
822 drawStyle -- If "hist", draw as line instead of points (default None)
823 drawCommand -- Deliver this to Draw() (default: None for same as drawStyle)
824 lineWidth -- If drawStyle=="hist", the width of line (default 2)
825 legendDx -- Float for moving TLegend in x direction for separate=True (default None)
826 legendDy -- Float for moving TLegend in y direction for separate=True (default None)
827 legendDw -- Float for changing TLegend width for separate=True (default None)
828 legendDh -- Float for changing TLegend height for separate=True (default None)
829 adjustMarginRight -- Float for adjusting right margin (default None)
830 ratioYmin -- Float for y axis minimum in ratio pad (default 0.9)
831 ratioYmax -- Float for y axis maximum in ratio pad (default 1.1)
832 ratioUncertainty -- Plot uncertainties on ratio? (default True)
833 histogramModifier -- Function to be called in create() to modify the histograms (default None)
837 def _set(attr, default):
838 setattr(self,
"_"+attr, kwargs.get(attr, default))
842 _set(
"xtitlesize",
None)
843 _set(
"xtitleoffset",
None)
844 _set(
"xlabelsize",
None)
846 _set(
"ytitlesize",
None)
847 _set(
"ytitleoffset",
None)
849 _set(
"ztitleoffset",
None)
866 _set(
"statyadjust",
None)
868 _set(
"normalizeToUnitArea",
False)
869 _set(
"profileX",
False)
870 _set(
"fitSlicesY",
False)
874 _set(
"xbinlabels",
None)
875 _set(
"xbinlabelsize",
None)
876 _set(
"xbinlabeloption",
None)
878 _set(
"drawStyle",
None)
879 _set(
"drawCommand",
None)
882 _set(
"legendDx",
None)
883 _set(
"legendDy",
None)
884 _set(
"legendDw",
None)
885 _set(
"legendDh",
None)
887 _set(
"adjustMarginRight",
None)
889 _set(
"ratioYmin", 0.9)
890 _set(
"ratioYmax", 1.1)
891 _set(
"ratioUncertainty",
True)
893 _set(
"histogramModifier",
None)
898 """Return number of existing histograms."""
902 """Return true if there are no histograms created for the plot"""
907 if isinstance(h, ROOT.TGraph2D):
912 if isinstance(self.
_name, list):
913 return str(self.
_name[0])
915 return str(self.
_name)
918 """Return true if the ratio uncertainty should be drawn"""
919 return self._ratioUncertainty
922 """Create one histogram from a TDirectory."""
927 if isinstance(self.
_name, list):
928 name = self.
_name[index]
934 def create(self, tdirs, requireAllHistograms=False):
935 """Create histograms from list of TDirectories"""
938 if self._histogramModifier
is not None:
942 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)))
947 def _modifyHisto(th1):
955 ROOT.TH1.AddDirectory(
True)
957 th1 = ROOT.gDirectory.Get(th1.GetName()+
"_2")
958 th1.SetDirectory(
None)
960 ROOT.TH1.AddDirectory(
False)
962 if self._title
is not None:
963 th1.SetTitle(self._title)
965 if self._scale
is not None:
966 th1.Scale(self._scale)
971 if requireAllHistograms
and None in self.
_histograms:
978 if h
is not None and hasattr(h,
"SetStats"):
982 def _doStats(h, col, dy):
989 f = h.GetListOfFunctions().FindObject(
"gaus")
997 st = h.GetListOfFunctions().FindObject(
"stats")
1001 st.SetX1NDC(startingX)
1002 st.SetX2NDC(startingX+0.3)
1003 st.SetY1NDC(startingY+dy)
1004 st.SetY2NDC(startingY+dy+0.15)
1005 st.SetTextColor(col)
1009 if self._statyadjust
is not None and i < len(self._statyadjust):
1010 dy += self._statyadjust[i]
1012 _doStats(h, _plotStylesColor[i], dy)
1016 """Normalise histograms to unit area"""
1027 def draw(self, pad, ratio, ratioFactor, nrows):
1028 """Draw the histograms using values for a given algorithm."""
1039 if self._normalizeToUnitArea:
1042 if self._rebinX
is not None:
1044 h.Rebin(self._rebinX)
1046 def _styleMarker(h, msty, col):
1047 h.SetMarkerStyle(msty)
1048 h.SetMarkerColor(col)
1049 h.SetMarkerSize(0.7)
1053 def _styleHist(h, msty, col):
1054 _styleMarker(h, msty, col)
1056 h.SetLineWidth(self._lineWidth)
1059 style = _styleMarker
1060 if self._drawStyle
is not None:
1061 if "hist" in self._drawStyle.lower():
1064 if "l" in self._drawStyle.lower():
1072 style(h, _plotStylesMarker[i], _plotStylesColor[i])
1074 if len(histos) == 0:
1075 print "No histograms for plot {name}".
format(name=self.
getName())
1079 xmin=self._xmin, xmax=self._xmax,
1080 ymin=self._ymin, ymax=self._ymax)
1089 self.
_setStats(self._statx, self._staty)
1091 xbinlabels = self._xbinlabels
1092 if xbinlabels
is None:
1093 if len(histos[0].GetXaxis().GetBinLabel(1)) > 0:
1095 for i
in xrange(1, histos[0].GetNbinsX()+1):
1096 xbinlabels.append(histos[0].GetXaxis().GetBinLabel(i))
1100 frame =
FrameTGraph2D(pad, bounds, histos, ratioOrig, ratioFactor)
1103 ratioBounds = (bounds[0], self._ratioYmin, bounds[2], self._ratioYmax)
1104 frame =
FrameRatio(pad, bounds, ratioBounds, ratioFactor, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
1106 frame =
Frame(pad, bounds, nrows, xbinlabels, self._xbinlabelsize, self._xbinlabeloption)
1109 frame.setLogx(self._xlog)
1110 frame.setLogy(self._ylog)
1111 frame.setGridx(self._xgrid)
1112 frame.setGridy(self._ygrid)
1117 if self._drawStyle
is not None:
1118 ds = self._drawStyle
1119 if self._drawCommand
is not None:
1120 ds = self._drawCommand
1125 frame.setTitle(histos[0].GetTitle())
1126 if self._xtitle
is not None:
1127 frame.setXTitle(self._xtitle)
1128 if self._xtitlesize
is not None:
1129 frame.setXTitleSize(self._xtitlesize)
1130 if self._xtitleoffset
is not None:
1131 frame.setXTitleOffset(self._xtitleoffset)
1132 if self._xlabelsize
is not None:
1133 frame.setXLabelSize(self._xlabelsize)
1134 if self._ytitle
is not None:
1135 frame.setYTitle(self._ytitle)
1136 if self._ytitlesize
is not None:
1137 frame.setYTitleSize(self._ytitlesize)
1138 if self._ytitleoffset
is not None:
1139 frame.setYTitleOffset(self._ytitleoffset)
1140 if self._ztitle
is not None:
1141 frame.setZTitle(self._ztitle)
1142 if self._ztitleoffset
is not None:
1143 frame.setZTitleOffset(self._ztitleoffset)
1144 if self._adjustMarginRight
is not None:
1145 frame.adjustMarginRight(self._adjustMarginRight)
1147 frame.adjustMarginLeft(0.03)
1148 frame.adjustMarginRight(0.08)
1158 if ratio
and len(histos) > 0:
1159 frame._padRatio.cd()
1161 if self._ratioUncertainty
and self.
_ratios[0]._ratio
is not None:
1162 self.
_ratios[0]._ratio.SetFillStyle(1001)
1163 self.
_ratios[0]._ratio.SetFillColor(ROOT.kGray)
1164 self.
_ratios[0]._ratio.SetLineColor(ROOT.kGray)
1165 self.
_ratios[0]._ratio.SetMarkerColor(ROOT.kGray)
1166 self.
_ratios[0]._ratio.SetMarkerSize(0)
1168 frame._padRatio.RedrawAxis(
"G")
1176 """Add histograms to a legend.
1180 legendLabels -- List of strings for the legend labels
1182 first = denomUncertainty
1183 for h, label
in zip(self.
_histograms, legendLabels):
1189 self._forLegend.SetFillStyle(1001)
1190 self._forLegend.SetFillColor(ROOT.kGray)
1191 entry = legend.AddEntry(self.
_forLegend, label,
"lpf")
1194 legend.AddEntry(h, label,
"LP")
1197 """Calculate the ratios for a list of histograms"""
1199 def _divideOrZero(numerator, denominator):
1200 if denominator == 0:
1202 return numerator/denominator
1207 def __init__(self, th1, uncertainty):
1211 xaxis = th1.GetXaxis()
1212 xaxis_arr = xaxis.GetXbins()
1213 if xaxis_arr.GetSize() > 0:
1214 lst = [xaxis_arr[i]
for i
in xrange(0, xaxis_arr.GetSize())]
1215 arr = array.array(
"d", lst)
1216 self.
_ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), arr)
1218 self.
_ratio = ROOT.TH1F(
"foo",
"foo", xaxis.GetNbins(), xaxis.GetXmin(), xaxis.GetXmax())
1220 self._ratio.SetStats(0)
1221 self._ratio.SetLineColor(ROOT.kBlack)
1222 self._ratio.SetLineWidth(1)
1223 def draw(self, style=None):
1230 self._ratio.Draw(
"same "+st)
1234 return self._th1.GetNbinsX()+1
1235 def xvalues(self, bin):
1236 xval = self._th1.GetBinCenter(bin)
1237 xlow = xval-self._th1.GetXaxis().GetBinLowEdge(bin)
1238 xhigh = self._th1.GetXaxis().GetBinUpEdge(bin)-xval
1239 return (xval, xlow, xhigh)
1240 def yvalues(self, bin):
1241 yval = self._th1.GetBinContent(bin)
1242 yerr = self._th1.GetBinError(bin)
1243 return (yval, yerr, yerr)
1245 return self._th1.GetBinContent(bin)
1246 def divide(self, bin, scale, xcenter):
1247 self._ratio.SetBinContent(bin, _divideOrZero(self._th1.GetBinContent(bin), scale))
1248 self._ratio.SetBinError(bin, _divideOrZero(self._th1.GetBinError(bin), scale))
1249 def makeRatio(self):
1253 def __init__(self, gr, uncertainty):
1263 def draw(self, style=None):
1272 self._ratio.Draw(
"same "+st)
1276 return self._gr.GetN()
1277 def xvalues(self, bin):
1278 return (self._gr.GetX()[bin], self._gr.GetErrorXlow(bin), self._gr.GetErrorXhigh(bin))
1279 def yvalues(self, bin):
1280 return (self._gr.GetY()[bin], self._gr.GetErrorYlow(bin), self._gr.GetErrorYhigh(bin))
1282 return self._gr.GetY()[bin]
1283 def divide(self, bin, scale, xcenter):
1288 if bin >= self._gr.GetN():
1292 xvals = self.xvalues(trueBin)
1294 epsilon = 1e-3 * xval
1295 if xval+epsilon < xcenter:
1299 elif xval-epsilon > xcenter:
1303 self._xvalues.append(xval)
1304 self._xerrslow.append(xvals[1])
1305 self._xerrshigh.append(xvals[2])
1306 yvals = self.yvalues(trueBin)
1307 self._yvalues.append(yvals[0] / scale)
1309 self._yerrslow.append(yvals[1] / scale)
1310 self._yerrshigh.append(yvals[2] / scale)
1312 self._yerrslow.append(0)
1313 self._yerrshigh.append(0)
1314 def makeRatio(self):
1322 class WrapTGraph2D(WrapTGraph):
1323 def __init__(self, gr, uncertainty):
1324 WrapTGraph.__init__(self, gr, uncertainty)
1325 def xvalues(self, bin):
1326 return (self._gr.GetX()[bin], self._gr.GetErrorX(bin), self._gr.GetErrorX(bin))
1327 def yvalues(self, bin):
1328 return (self._gr.GetY()[bin], self._gr.GetErrorY(bin), self._gr.GetErrorY(bin))
1331 if isinstance(o, ROOT.TH1):
1332 return WrapTH1(o, self._ratioUncertainty)
1333 elif isinstance(o, ROOT.TGraph):
1334 return WrapTGraph(o, self._ratioUncertainty)
1335 elif isinstance(o, ROOT.TGraph2D):
1336 return WrapTGraph2D(o, self._ratioUncertainty)
1338 wrappers = [
wrap(h)
for h
in histos]
1341 for bin
in xrange(ref.begin(), ref.end()):
1342 (scale, ylow, yhigh) = ref.yvalues(bin)
1343 (xval, xlow, xhigh) = ref.xvalues(bin)
1345 w.divide(bin, scale, xval)
1353 """Group of plots, results a TCanvas"""
1358 name -- String for name of the TCanvas, used also as the basename of the picture files
1359 plots -- List of Plot objects
1362 ncols -- Number of columns (default 2)
1363 legendDx -- Float for moving TLegend in x direction (default None)
1364 legendDy -- Float for moving TLegend in y direction (default None)
1365 legendDw -- Float for changing TLegend width (default None)
1366 legendDh -- Float for changing TLegend height (default None)
1367 overrideLegendLabels -- List of strings for legend labels, if given, these are used instead of the ones coming from Plotter (default None)
1368 onlyForPileup -- Plots this group only for pileup samples
1373 def _set(attr, default):
1374 setattr(self,
"_"+attr, kwargs.get(attr, default))
1378 _set(
"legendDx",
None)
1379 _set(
"legendDy",
None)
1380 _set(
"legendDw",
None)
1381 _set(
"legendDh",
None)
1383 _set(
"overrideLegendLabels",
None)
1385 _set(
"onlyForPileup",
False)
1390 """Return True if the PlotGroup is intended only for pileup samples"""
1391 return self._onlyForPileup
1393 def create(self, tdirectories, requireAllHistograms=False):
1394 """Create histograms from a list of TDirectories.
1397 tdirectories -- List of TDirectory objects
1398 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
1401 plot.create(tdirectories, requireAllHistograms)
1403 def draw(self, legendLabels, prefix=None, separate=False, saveFormat=".pdf", ratio=False):
1404 """Draw the histograms using values for a given algorithm.
1407 legendLabels -- List of strings for legend labels (corresponding to the tdirectories in create())
1408 prefix -- Optional string for file name prefix (default None)
1409 separate -- Save the plots of a group to separate files instead of a file per group (default False)
1410 saveFormat -- String specifying the plot format (default '.pdf')
1411 ratio -- Add ratio to the plot (default False)
1414 if self._overrideLegendLabels
is not None:
1415 legendLabels = self._overrideLegendLabels
1418 onlyEmptyPlots =
True
1420 if not plot.isEmpty():
1421 onlyEmptyPlots =
False
1427 return self.
_drawSeparate(legendLabels, prefix, saveFormat, ratio)
1429 cwidth = 500*self._ncols
1430 nrows = int((len(self.
_plots)+1)/self._ncols)
1431 cheight = 500 * nrows
1436 canvas = ROOT.TCanvas(self.
_name, self.
_name, cwidth, cheight)
1438 canvas.Divide(self._ncols, nrows)
1440 for i
in xrange(0, len(self.
_plots)):
1441 pad = canvas.cd(i+1)
1445 for i, plot
in enumerate(self.
_plots):
1446 pad = canvas.cd(i+1)
1447 if not plot.isEmpty():
1452 if len(self.
_plots) <= 4:
1462 if self._legendDx
is not None:
1463 lx1 += self._legendDx
1464 lx2 += self._legendDx
1465 if self._legendDy
is not None:
1466 ly1 += self._legendDy
1467 ly2 += self._legendDy
1468 if self._legendDw
is not None:
1469 lx2 += self._legendDw
1470 if self._legendDh
is not None:
1471 ly1 -= self._legendDh
1472 plot =
max(self.
_plots, key=
lambda p: p.getNumberOfHistograms())
1473 denomUnc = sum([p.drawRatioUncertainty()
for p
in self.
_plots]) > 0
1474 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2,
1475 denomUncertainty=(ratio
and denomUnc))
1477 return self.
_save(canvas, saveFormat, prefix=prefix)
1480 """Internal method to do the drawing to separate files per Plot instead of a file per PlotGroup"""
1486 canvas = ROOT.TCanvas(self.
_name+
"Single", self.
_name, width, height)
1488 canvas.SetTopMargin(0.05)
1489 canvas.SetBottomMargin(0.13)
1490 canvas.SetLeftMargin(0.16)
1491 canvas.SetRightMargin(0.05)
1519 if plot._legendDx
is not None:
1520 lx1 += plot._legendDx
1521 lx2 += plot._legendDx
1522 if plot._legendDy
is not None:
1523 ly1 += plot._legendDy
1524 ly2 += plot._legendDy
1525 if plot._legendDw
is not None:
1526 lx2 += plot._legendDw
1527 if plot._legendDh
is not None:
1528 ly1 -= plot._legendDh
1531 legend = self.
_createLegend(plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.03,
1532 denomUncertainty=(ratio
and plot.drawRatioUncertainty))
1534 ret.extend(self.
_save(canvas, saveFormat, prefix=prefix, postfix=
"_"+plot.getName(), single=
True))
1538 """Internal method to set divide a pad to two for ratio plots"""
1543 topMargin = pad.GetTopMargin()
1544 bottomMargin = pad.GetBottomMargin()
1545 divisionPoint += (1-divisionPoint)*bottomMargin
1546 divisionPointForPad1 = 1-( (1-divisionPoint) / (1-0.02) )
1551 ylow = divisionPointForPad1
1554 pad1.SetPad(xlow, ylow, xup, yup)
1555 pad1.SetFillStyle(4000)
1556 pad1.SetBottomMargin(0.02)
1562 pad2.SetPad(xlow, ylow, xup, yup)
1563 pad2.SetFillStyle(4000)
1564 pad2.SetTopMargin(0.0)
1565 pad2.SetBottomMargin(bottomMargin/(self.
_ratioFactor*divisionPoint))
1567 def _createLegend(self, plot, legendLabels, lx1, ly1, lx2, ly2, textSize=0.016, denomUncertainty=True):
1568 l = ROOT.TLegend(lx1, ly1, lx2, ly2)
1569 l.SetTextSize(textSize)
1576 plot.addToLegend(l, legendLabels, denomUncertainty)
1580 def _save(self, canvas, saveFormat, prefix=None, postfix=None, single=False):
1583 if prefix
is not None:
1585 if postfix
is not None:
1587 canvas.SaveAs(name+saveFormat)
1590 canvas.SetLogx(
False)
1591 canvas.SetLogy(
False)
1595 return [name+saveFormat]
1598 """Represents a collection of PlotGroups, produced from a single folder in a DQM file"""
1603 plotGroups -- List of PlotGroup objects
1606 loopSubFolders -- Should the subfolders be looped over? (default: True)
1607 onlyForPileup -- Plots this folder only for pileup samples
1608 purpose -- html.PlotPurpose member class for the purpose of the folder, used for grouping of the plots to the HTML pages
1609 page -- Optional string for the page in HTML generatin
1610 section -- Optional string for the section within a page in HTML generation
1619 raise Exception(
"Got unexpected keyword arguments: "+
",".
join(kwargs.keys()))
1622 """Return True if the PlotGroups of this folder should be applied to the all subfolders"""
1626 """Return True if the folder is intended only for pileup samples"""
1639 self._plotGroups.append(plotGroup)
1644 def create(self, files, labels, possibleDqmFolders, dqmSubFolder=None, isPileupSample=True, requireAllHistograms=False):
1645 """Create histograms from a list of TFiles.
1648 files -- List of TFiles
1649 labels -- List of strings for legend labels corresponding the files
1650 possibleDqmFolders -- List of strings for possible directories of histograms in TFiles
1651 dqmSubFolder -- Optional string for subdirectory inside the dqmFolder; if list of strings, then each corresponds to a TFile
1652 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
1653 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
1656 if len(files) != len(labels):
1657 raise Exception(
"len(files) should be len(labels), now they are %d and %d" % (len(files), len(labels)))
1661 if isinstance(dqmSubFolder, list):
1662 if len(dqmSubFolder) != len(files):
1663 raise Exception(
"When dqmSubFolder is a list, len(dqmSubFolder) should be len(files), now they are %d and %d" % (len(dqmSubFolder), len(files)))
1665 dqmSubFolder = [dqmSubFolder]*len(files)
1667 for fil, sf
in zip(files, dqmSubFolder):
1668 dirs.append(self.
_getDir(fil, possibleDqmFolders, sf))
1672 if pg.onlyForPileup()
and not isPileupSample:
1674 pg.create(dirs, requireAllHistograms)
1676 def draw(self, prefix=None, separate=False, saveFormat=".pdf", ratio=False):
1677 """Draw and save all plots using settings of a given algorithm.
1680 prefix -- Optional string for file name prefix (default None)
1681 separate -- Save the plots of a group to separate files instead of a file per group (default False)
1682 saveFormat -- String specifying the plot format (default '.pdf')
1683 ratio -- Add ratio to the plot (default False)
1688 ret.extend(pg.draw(self.
_labels, prefix=prefix, separate=separate, saveFormat=saveFormat, ratio=ratio))
1692 def _getDir(self, tfile, possibleDqmFolders, dqmSubFolder):
1693 """Get TDirectory from TFile."""
1696 for pdf
in possibleDqmFolders:
1699 if dqmSubFolder
is not None:
1701 d = d.Get(dqmSubFolder)
1705 print "Did not find subdirectory '%s' from directory '%s' in file %s" % (dqmSubFolder, pdf, tfile.GetName())
1709 print "Did not find any of directories '%s' from file %s" % (
",".
join(possibleDqmFolders), tfile.GetName())
1714 """Method called to (possibly) translate a subfolder name to more 'readable' form
1716 The implementation in this (base) class just returns the
1717 argument. The idea is that a deriving class might want to do
1718 something more complex (like trackingPlots.TrackingPlotFolder
1721 return dqmSubFolderName
1724 """Get selection name (used in output directory name and legend) from the name of PlotterFolder, and a return value of translateSubFolder"""
1726 if plotFolderName !=
"":
1727 ret +=
"_"+plotFolderName
1728 if translatedDqmSubFolder
is not None:
1729 ret +=
"_"+translatedDqmSubFolder
1733 """Return True if this subfolder should be processed
1736 limitOnlyTo -- List/set/similar containing the translatedDqmSubFolder
1737 translatedDqmSubFolder -- Return value of translateSubFolder
1739 return translatedDqmSubFolder
in limitOnlyTo
1742 """Class to hold the original name and a 'translated' name of a subfolder in the DQM ROOT file"""
1748 """Equality is defined by the 'translated' name"""
1752 """Plotter for one DQM folder.
1754 This class is supposed to be instantiated by the Plotter class (or
1755 PlotterItem, to be more specific), and not used directly by the
1758 def __init__(self, name, possibleDqmFolders, dqmSubFolders, plotFolder, fallbackNames):
1763 name -- Name of the folder (is used in the output directory naming)
1764 possibleDqmFolders -- List of strings for possible directories of histograms in TFiles
1765 dqmSubFolders -- List of lists of strings for list of subfolders per input file, or None if no subfolders
1766 plotFolder -- PlotFolder object
1767 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.
1773 if dqmSubFolders
is None:
1787 return self._plotFolder.getPurpose()
1790 return self._plotFolder.getPage()
1793 return self._plotFolder.getSection()
1796 return self._plotFolder.onlyForPileup()
1802 """Get list of subfolders, possibly limiting to some of them.
1805 limitOnlyTo -- Object depending on the PlotFolder type for limiting the set of subfolders to be processed
1811 if limitOnlyTo
is None:
1814 return filter(
lambda s: self._plotFolder.limitSubFolder(limitOnlyTo, s.translated), self.
_dqmSubFolders)
1817 """Get a generator for the 'selection name', looping over the name and fallbackNames"""
1819 yield self._plotFolder.getSelectionName(name, dqmSubFolder.translated
if dqmSubFolder
is not None else None)
1824 def create(self, files, labels, dqmSubFolder, isPileupSample=True, requireAllHistograms=False):
1825 """Create histograms from a list of TFiles.
1827 files -- List of TFiles
1828 labels -- List of strings for legend labels corresponding the files
1829 dqmSubFolder -- DQMSubFolder object for a subfolder (or None for no subfolder)
1830 isPileupSample -- Is sample pileup (some PlotGroups may limit themselves to pileup)
1831 requireAllHistograms -- If True, a plot is produced if histograms from all files are present (default: False)
1835 self._plotFolder.create(files, labels, self.
_possibleDqmFolders, dqmSubFolder.subfolder
if dqmSubFolder
is not None else None, isPileupSample, requireAllHistograms)
1838 """Draw and save all plots using settings of a given algorithm."""
1839 return self._plotFolder.draw(*args, **kwargs)
1842 """Instance of plotter that knows the directory content, holds many folders."""
1849 if limitSubFoldersOnlyTo
is not None:
1850 limitOnlyTo = limitSubFoldersOnlyTo.get(plotterFolder.getName(),
None)
1852 for dqmSubFolder
in plotterFolder.getDQMSubFolders(limitOnlyTo=limitOnlyTo):
1853 yield plotterFolder, dqmSubFolder
1857 def __init__(self, name, possibleDirs, plotFolder, fallbackNames=[]):
1861 name -- Name of the folder (is used in the output directory naming)
1862 possibleDirs -- List of strings for possible directories of histograms in TFiles
1863 plotFolder -- PlotFolder object
1866 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.
1874 """Read available subfolders from the files
1877 files -- List of strings for paths to files, or list of TFiles
1879 For each file, loop over 'possibleDirs', and read the
1880 subfolders of first one that exists.
1882 Returns a PlotterFolder if at least one file for which one of
1883 'possibleDirs' exists. Otherwise, return None to signal that
1884 there is nothing available for this PlotFolder.
1887 if self._plotFolder.loopSubFolders():
1889 possibleDirFound =
False
1891 isOpenFile = isinstance(fname, ROOT.TFile)
1895 tfile = ROOT.TFile.Open(fname)
1899 possibleDirFound =
True
1900 if subFolders
is not None:
1902 for key
in d.GetListOfKeys():
1903 if isinstance(key.ReadObj(), ROOT.TDirectory):
1904 subf.append(key.GetName())
1905 subFolders.append(subf)
1911 if not possibleDirFound:
1917 """Contains PlotFolders, i.e. the information what plots to do, and creates a helper object to actually produce the plots."""
1921 _absoluteSize =
True
1933 ROOT.gROOT.SetStyle(
"Plain")
1934 ROOT.gStyle.SetPadRightMargin(0.07)
1935 ROOT.gStyle.SetPadLeftMargin(0.13)
1936 ROOT.gStyle.SetTitleFont(font,
"XYZ")
1937 ROOT.gStyle.SetTitleSize(titleSize,
"XYZ")
1938 ROOT.gStyle.SetTitleOffset(1.2,
"Y")
1940 ROOT.gStyle.SetLabelFont(font,
"XYZ")
1941 ROOT.gStyle.SetLabelSize(labelSize,
"XYZ")
1942 ROOT.gStyle.SetTextSize(labelSize)
1943 ROOT.gStyle.SetStatFont(font)
1944 ROOT.gStyle.SetStatFontSize(statSize)
1946 ROOT.TH1.AddDirectory(
False)
1949 """Append a plot folder to the plotter.
1951 All arguments are forwarded to the constructor of PlotterItem.
1956 """Returns PlotterInstance object, which knows how exactly to produce the plots for these files"""
def getSelectionNameIterator
def getPossibleDQMFolders
def _getYminIgnoreOutlier
static std::string join(char **cmd)
def getNumberOfHistograms
auto wrap(F iFunc) -> decltype(iFunc())