示例#1
0
class DataMCPlot(object):
    '''Handles a Data vs MC plot.

    Features a list of histograms (some of them being stacked),
    and several Drawing functions.
    '''
    def __init__(self, name):
        self.histosDict = {}
        self.histos = []
        self.supportHist = None
        self.name = name
        self.stack = None
        self.legendOn = True
        self.legend = None
        self.legendBorders = 0.17, 0.46, 0.44, 0.89
        # self.lastDraw = None
        # self.lastDrawArgs = None
        self.stack = None
        self.nostack = None
        self.blindminx = None
        self.blindmaxx = None
        self.groups = {}
        self.axisWasSet = False

    def Blind(self, minx, maxx, blindStack):
        self.blindminx = minx
        self.blindmaxx = maxx
        if self.stack and blindStack:
            self.stack.Blind(minx, maxx)
        if self.nostack:
            for hist in self.nostack:
                hist.Blind(minx, maxx)

    def AddHistogram(self, name, histo, layer=0, legendLine=None):
        '''Add a ROOT histogram, with a given name.

        Histograms will be drawn by increasing layer.'''
        tmp = Histogram(name, histo, layer, legendLine)
        self.histos.append(tmp)
        self.histosDict[name] = tmp
        # tmp.AddEntry( self.legend, legendLine)

    def Group(self, groupName, namesToGroup, layer=None, style=None):
        '''Group all histos with names in namesToGroup into a single
        histo with name groupName. All histogram properties are taken
        from the first histogram in namesToGroup.
        See UnGroup as well
        '''
        groupHist = None
        realNames = []
        actualNamesInGroup = []
        for name in namesToGroup:
            hist = self.histosDict.get(name, None)
            if hist is None:
                print 'warning, no histo with name', name
                continue
            if groupHist is None:
                groupHist = hist.Clone(groupName)
                self.histos.append(groupHist)
                self.histosDict[groupName] = groupHist
            else:
                groupHist.Add(hist)
            actualNamesInGroup.append(name)
            realNames.append(hist.realName)
            hist.on = False
        if groupHist:
            self.groups[groupName] = actualNamesInGroup
            groupHist.realName = ','.join(realNames)

    def UnGroup(self, groupName):
        '''Ungroup groupName, recover the histograms in the group'''
        group = self.groups.get(groupName, None)
        if group is None:
            print groupName, 'is not a group in this plot.'
            return
        for name in group:
            self.histosDict[name].on = True
        self.histosDict[groupName].on = False

    def Replace(self, name, pyhist):
        '''Not very elegant... should have a clone function in Histogram...'''
        oldh = self.histosDict.get(name, None)
        pythist = copy.deepcopy(pyhist)
        pyhist.layer = oldh.layer
        pyhist.stack = oldh.stack
        pyhist.name = oldh.name
        pyhist.legendLine = oldh.legendLine
        pyhist.SetStyle(oldh.style)
        pyhist.weighted.SetFillStyle(oldh.weighted.GetFillStyle())
        if oldh is None:
            print 'histogram', name, 'does not exist, cannot replace it.'
            return
        else:
            index = self.histos.index(oldh)
            self.histosDict[name] = pyhist
            self.histos[index] = pyhist

    def _SortedHistograms(self, reverse=False):
        '''Returns the histogram dictionary, sorted by increasing layer,
        excluding histograms which are not "on".

        This function is used in all the Draw functions.'''
        byLayer = sorted(self.histos, key=attrgetter('layer'))
        byLayerOn = [hist for hist in byLayer if (hist.on is True)]
        if reverse:
            byLayerOn.reverse()
        return byLayerOn

    def Hist(self, histName):
        '''Returns an histogram.

        Print the DataMCPlot object to see which histograms are available.'''
        return self.histosDict[histName]

    def DrawNormalized(self, opt=''):
        '''All histograms are drawn as PDFs, even the stacked ones'''
        same = ''
        for hist in self._SortedHistograms():
            hist.obj.DrawNormalized(same + opt)
            if same == '':
                same = 'same'
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalized'
        # self.lastDrawArgs = [ opt ]

    def Draw(self, opt=''):
        '''All histograms are drawn.'''
        same = ''
        self.supportHist = None
        for hist in self._SortedHistograms():
            if self.supportHist is None:
                self.supportHist = hist
            hist.Draw(same + opt)
            if same == '':
                same = 'same'
        yaxis = self.supportHist.GetYaxis()
        yaxis.SetRangeUser(0.01, ymax(self._SortedHistograms()))
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'Draw'
        # self.lastDrawArgs = [ opt ]

    def CreateLegend(self, ratio=False):
        if self.legend is None:
            self.legend = TLegend(*self.legendBorders)
            self.legend.SetFillColor(0)
            self.legend.SetFillStyle(0)
            self.legend.SetLineColor(0)
        else:
            self.legend.Clear()
        hists = self._SortedHistograms(reverse=True)
        if ratio:
            hists = hists[:-1]  # removing the last histo.
        for index, hist in enumerate(hists):
            hist.AddEntry(self.legend)

    def DrawLegend(self, ratio=False):
        '''Draw the legend.'''
        if self.legendOn:
            self.CreateLegend(ratio)
            self.legend.Draw('same')

    def DrawRatio(self, opt=''):
        '''Draw ratios : h_i / h_0.

        h_0 is the histogram with the smallest layer, and h_i, i>0 are the other histograms.
        if the DataMCPlot object contains N histograms, N-1 ratio plots will be drawn.
        To take another histogram as the denominator, change the layer of this histogram by doing:
        dataMCPlot.Hist("histName").layer = -99 '''
        same = ''
        denom = None
        self.ratios = []
        for hist in self._SortedHistograms():
            if denom == None:
                denom = hist
                continue
            ratio = copy.deepcopy(hist)
            ratio.obj.Divide(denom.obj)
            ratio.obj.Draw(same)
            self.ratios.append(ratio)
            if same == '':
                same = 'same'
        self.DrawLegend(ratio=True)
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawRatio'
        # self.lastDrawArgs = [ opt ]

    def DrawDataOverMCMinus1(self, ymin=-0.5, ymax=0.5):
        stackedHists = []
        dataHist = None
        for hist in self._SortedHistograms():
            if hist.stack is False:
                dataHist = hist
                continue
            stackedHists.append(hist)
        self._BuildStack(stackedHists, ytitle='Data/MC')
        mcHist = self.stack.totalHist
        self.dataOverMCHist = copy.deepcopy(dataHist)
        self.dataOverMCHist.Add(mcHist, -1)
        self.dataOverMCHist.Divide(mcHist)
        self.dataOverMCHist.Draw()
        yaxis = self.dataOverMCHist.GetYaxis()
        yaxis.SetRangeUser(ymin, ymax)
        yaxis.SetTitle('Data/MC - 1')
        yaxis.SetNdivisions(5)
        fraclines = 0.2
        if ymax <= 0.2 or ymin >= -0.2:
            fraclines = 0.1
        self.DrawRatioLines(self.dataOverMCHist, fraclines, 0.)
        if TPad.Pad():
            TPad.Pad().Update()

    def DrawRatioStack(self,
                       opt='',
                       xmin=None,
                       xmax=None,
                       ymin=None,
                       ymax=None):
        '''Draw ratios.

        The stack is considered as a single histogram.'''
        denom = None
        # import pdb; pdb.set_trace()
        histForRatios = []
        denom = None
        for hist in self._SortedHistograms():
            if hist.stack is False:
                # if several unstacked histograms, the highest layer is used
                denom = hist
                continue
            histForRatios.append(hist)
        self._BuildStack(histForRatios, ytitle='MC/Data')
        self.stack.Divide(denom.obj)
        if self.blindminx and self.blindmaxx:
            self.stack.Blind(self.blindminx, self.blindmaxx)
        self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        self.ratios = []
        for hist in self.nostack:
            if hist is denom: continue
            ratio = copy.deepcopy(hist)
            ratio.obj.Divide(denom.obj)
            ratio.obj.Draw('same')
            self.ratios.append(ratio)
        self.DrawLegend(ratio=True)
        self.DrawRatioLines(denom, 0.2, 1)
        if TPad.Pad():
            TPad.Pad().Update()

    def DrawNormalizedRatioStack(self,
                                 opt='',
                                 xmin=None,
                                 xmax=None,
                                 ymin=None,
                                 ymax=None):
        '''Draw ratios.

        The stack is considered as a single histogram.
        All histograms are normalized before computing the ratio'''
        denom = None
        histForRatios = []
        for hist in self._SortedHistograms():
            # taking the first histogram (lowest layer)
            # as the denominator histogram.
            if denom == None:
                denom = copy.deepcopy(hist)
                continue
            # other histograms will be divided by the denominator
            histForRatios.append(hist)
        self._BuildStack(histForRatios, ytitle='MC p.d.f. / Data p.d.f.')
        self.stack.Normalize()
        denom.Normalize()
        self.stack.Divide(denom.weighted)
        self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        self.ratios = []
        for hist in self.nostack:
            # print 'nostack ', hist
            ratio = copy.deepcopy(hist)
            ratio.Normalize()
            ratio.obj.Divide(denom.weighted)
            ratio.obj.Draw('same')
            self.ratios.append(ratio)
        self.DrawLegend(ratio=True)
        self.DrawRatioLines(denom, 0.2, 1)
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalizedRatioStack'
        # self.lastDrawArgs = [ opt ]

    def DrawRatioLines(self, hist, frac=0.2, y0=1.):
        '''Draw a line at y = 1, at 1+frac, and at 1-frac.

        hist is used to get the x axis range.'''
        xmin = hist.obj.GetXaxis().GetXmin()
        xmax = hist.obj.GetXaxis().GetXmax()
        line = TLine()
        line.DrawLine(xmin, y0, xmax, y0)
        line.DrawLine(xmin, y0 + frac, xmax, y0 + frac)
        line.DrawLine(xmin, y0 - frac, xmax, y0 - frac)

    def DrawStack(self, opt='', xmin=None, xmax=None, ymin=None, ymax=None):
        '''Draw all histograms, some of them in a stack.

        if Histogram.stack is True, the histogram is put in the stack.'''
        self._BuildStack(self._SortedHistograms(), ytitle='Events')
        same = 'same'
        if len(self.nostack) == 0:
            same = ''
        self.supportHist = None
        for hist in self.nostack:
            hist.Draw()
            if not self.supportHist:
                self.supportHist = hist
        self.stack.Draw(opt + same, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        if self.supportHist is None:
            self.supportHist = self.stack.totalHist
        if not self.axisWasSet:
            mxsup = self.supportHist.weighted.GetBinContent(
                self.supportHist.weighted.GetMaximumBin())
            mxstack = self.stack.totalHist.weighted.GetBinContent(
                self.stack.totalHist.weighted.GetMaximumBin())
            mx = max(mxsup, mxstack)
            if ymin is None: ymin = 0.01
            if ymax is None: ymax = mx * 1.3
            self.supportHist.GetYaxis().SetRangeUser(ymin, ymax)
            self.axisWasSet = True
        for hist in self.nostack:
            if self.blindminx:
                hist.Blind(self.blindminx, self.blindmaxx)
            hist.Draw('same')
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawStack'
        # self.lastDrawArgs = [ opt ]

    def DrawNormalizedStack(self,
                            opt='',
                            xmin=None,
                            xmax=None,
                            ymin=0.001,
                            ymax=None):
        '''Draw all histograms, some of them in a stack.

        if Histogram.stack is True, the histogram is put in the stack.
        all histograms out of the stack, and the stack itself, are shown as PDFs.'''
        self._BuildStack(self._SortedHistograms(), ytitle='p.d.f.')
        self.stack.DrawNormalized(opt,
                                  xmin=xmin,
                                  xmax=xmax,
                                  ymin=ymin,
                                  ymax=ymax)
        for hist in self.nostack:
            hist.obj.DrawNormalized('same')
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalizedStack'
        # self.lastDrawArgs = [ opt ]

    def Rebin(self, factor):
        '''Rebin, and redraw.'''
        # the dispatching technique is not too pretty,
        # but keeping a self.lastDraw function initialized to one of the Draw functions
        # when calling it creates a problem in deepcopy.
        for hist in self.histos:
            hist.Rebin(factor)
        self.axisWasSet = False

    def NormalizeToBinWidth(self):
        '''Normalize each Histograms bin to the bin width.'''
        for hist in self.histos:
            hist.NormalizeToBinWidth()

    def _BuildStack(self, hists, ytitle=None):
        '''build a stack from a list of Histograms.

        The histograms for which Histogram.stack is False are put in self.nostack'''
        self.stack = None
        self.stack = Stack(self.name + '_stack', ytitle=ytitle)
        self.nostack = []
        for hist in hists:
            if hist.stack:
                self.stack.Add(hist)
            else:
                self.nostack.append(hist)

    def __str__(self):
        if self.stack is None:
            self._BuildStack(self._SortedHistograms(), ytitle='Events')
        tmp = [' '.join(['DataMCPlot: ', self.name])]
        tmp.append('Histograms:')
        for hist in self._SortedHistograms(reverse=True):
            tmp.append(' '.join(['\t', str(hist)]))
        tmp.append(
            'Stack yield = {integ:7.1f}'.format(integ=self.stack.integral))
        return '\n'.join(tmp)
示例#2
0
class DataMCPlot(object):
    '''Handles a Data vs MC plot.

    Features a list of histograms (some of them being stacked),
    and several Drawing functions.
    '''
    _f_keeper = {}
    _t_keeper = {}

    def __init__(self, name):
        self.histosDict = {}
        self.histos = []
        self.supportHist = None
        self.name = name
        self.stack = None
        self.legendOn = True
        self.legend = None
        #        self.legendBorders = 0.20, 0.46, 0.44, 0.89
        #        self.legendPos = 'left'
        self.legendBorders = 0.20, 0.78, 0.80, 0.88
        self.legendPos = 'top'
        # self.lastDraw = None
        # self.lastDrawArgs = None
        self.nostack = None
        self.blindminx = None
        self.blindmaxx = None
        self.groups = {}
        self.axisWasSet = False
        self.histPref = histPref

    def __contains__(self, name):
        return name in self.histosDict

    def __getitem__(self, name):
        return self.histosDict[name]

    def readTree(self,
                 file_name,
                 tree_name='tree',
                 verbose=False,
                 friend_func=None):
        '''Cache files/trees'''
        if file_name in self.__class__._t_keeper:
            ttree = self.__class__._t_keeper[file_name]
            if verbose:
                print 'got cached tree', ttree
        else:
            tfile = self.__class__._f_keeper[file_name] = TFile.Open(file_name)
            ttree = self.__class__._t_keeper[file_name] = tfile.Get(tree_name)
            if verbose:
                print 'read tree', ttree, 'from file', file_name

        if friend_func:
            file_name = friend_func(file_name)
            friend_tree = self.readTree(file_name, tree_name, verbose)
            ttree.AddFriend(friend_tree)

        gROOT.cd()

        return ttree

    def Blind(self, minx, maxx, blindStack):
        self.blindminx = minx
        self.blindmaxx = maxx
        if self.stack and blindStack:
            self.stack.Blind(minx, maxx)
        if self.nostack:
            for hist in self.nostack:
                if hist.style.drawAsData:
                    hist.Blind(minx, maxx)

    def AddHistogram(self, name, histo, layer=0, legendLine=None, stack=True):
        '''Add a ROOT histogram, with a given name.

        Histograms will be drawn by increasing layer.'''
        tmp = Histogram(name, histo, layer, legendLine, stack=stack)
        self.histos.append(tmp)
        self.histosDict[name] = tmp
        return tmp

    def Group(self,
              groupName,
              namesToGroup,
              layer=None,
              style=None,
              silent=False):
        '''Group all histos with names in namesToGroup into a single
        histo with name groupName. All histogram properties are taken
        from the first histogram in namesToGroup.
        See UnGroup as well
        '''
        groupHist = None
        realNames = []
        actualNamesInGroup = []
        for name in namesToGroup:
            hist = self.histosDict.get(name, None)
            if hist is None:
                if not silent:
                    print 'warning, no histo with name', name
                continue
            if groupHist is None:
                groupHist = hist.Clone(groupName)
                self.histos.append(groupHist)
                self.histosDict[groupName] = groupHist
            else:
                groupHist.Add(hist)
            actualNamesInGroup.append(name)
            realNames.append(hist.realName)
            hist.on = False
        if groupHist:
            self.groups[groupName] = actualNamesInGroup
            groupHist.realName = ','.join(realNames)
            if style is not None:
                groupHist.SetStyle(style)
            self._ApplyPrefs()

    def UnGroup(self, groupName):
        '''Ungroup groupName, recover the histograms in the group'''
        group = self.groups.get(groupName, None)
        if group is None:
            print groupName, 'is not a group in this plot.'
            return
        for name in group:
            self.histosDict[name].on = True
        self.histosDict[groupName].on = False

    def Replace(self, name, pyhist):
        '''Not very elegant... should have a clone function in Histogram...'''
        oldh = self.histosDict.get(name, None)
        if oldh is None:
            print 'histogram', name, 'does not exist, cannot replace it.'
            return

        pythist = copy.deepcopy(pyhist)
        pythist.layer = oldh.layer
        pythist.stack = oldh.stack
        pythist.name = oldh.name
        pythist.legendLine = oldh.legendLine
        pythist.SetStyle(oldh.style)
        pythist.weighted.SetFillStyle(oldh.weighted.GetFillStyle())

        index = self.histos.index(oldh)
        self.histosDict[name] = pythist
        self.histos[index] = pythist

    def _SortedHistograms(self, reverse=False):
        '''Returns the histogram dictionary, sorted by increasing layer,
        excluding histograms which are not "on".

        This function is used in all the Draw functions.'''
        byLayer = sorted(self.histos, key=attrgetter('layer'))
        byLayerOn = [hist for hist in byLayer if (hist.on is True)]
        if reverse:
            byLayerOn.reverse()
        return byLayerOn

    def Hist(self, histName):
        '''Returns a histogram.

        Print the DataMCPlot object to see which histograms are available.'''
        return self.histosDict[histName]

    def DrawNormalized(self, opt=''):
        '''All histograms are drawn as PDFs, even the stacked ones'''
        same = ''
        for hist in self._SortedHistograms():
            hist.obj.DrawNormalized(same + opt)
            if same == '':
                same = 'same'
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalized'
        # self.lastDrawArgs = [ opt ]

    def Draw(self, opt=''):
        '''All histograms are drawn.'''
        same = ''
        self.supportHist = None
        for hist in self._SortedHistograms():
            if self.supportHist is None:
                self.supportHist = hist
            hist.Draw(same + opt)
            if same == '':
                same = 'same'
#        set_trace()
        yaxis = self.supportHist.GetYaxis()
        yaxis.SetRangeUser(0.01, 1.5 * ymax(self._SortedHistograms()))
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'Draw'
        # self.lastDrawArgs = [ opt ]

    def CreateLegend(self, ratio=False, print_norm=False):
        if self.legend is None:
            self.legend = TLegend(*self.legendBorders)
            self.legend.SetFillColor(0)
            self.legend.SetFillStyle(0)
            self.legend.SetLineColor(0)
            self.legend.SetLineWidth(1)
            self.legend.SetNColumns(5)  # number of comps / 2 (or 3) + 1
            self.legend.SetEntrySeparation(0.2)
            self.legend.SetColumnSeparation(0.2)
            self.legend.SetBorderSize(0)
            self.legend.SetMargin(0.25)
        else:
            self.legend.Clear()
        hists = self._SortedHistograms(reverse=True)
        if ratio:
            hists = hists[:-1]  # removing the last histo.
        for index, hist in enumerate(hists):
            if print_norm:
                if not hist.legendLine:
                    hist.legendLine = hist.name
                hist.legendLine += ' ({norm:.1f})'.format(norm=hist.Yield())
            hist.AddEntry(self.legend)

    def DrawLegend(self, ratio=False, print_norm=False):
        '''Draw the legend.'''
        if self.legendOn:
            self.CreateLegend(ratio=ratio, print_norm=print_norm)
            self.legend.Draw('same')

    def DrawRatio(self, opt=''):
        '''Draw ratios : h_i / h_0.

        h_0 is the histogram with the smallest layer, and h_i, i>0 are the other histograms.
        if the DataMCPlot object contains N histograms, N-1 ratio plots will be drawn.
        To take another histogram as the denominator, change the layer of this histogram by doing:
        dataMCPlot.Hist("histName").layer = -99 '''
        same = ''
        denom = None
        self.ratios = []
        for hist in self._SortedHistograms():
            if denom == None:
                denom = hist
                continue
            ratio = copy.deepcopy(hist)
            ratio.obj.Divide(denom.obj)
            ratio.obj.Draw(same)
            self.ratios.append(ratio)
            if same == '':
                same = 'same'
        self.DrawLegend(ratio=True)
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawRatio'
        # self.lastDrawArgs = [ opt ]

    def DrawDataOverMCMinus1(self, ymin=-0.5, ymax=0.5):
        stackedHists = []
        dataHist = None
        for hist in self._SortedHistograms():
            if hist.stack is False:
                dataHist = hist
                continue
            stackedHists.append(hist)
        self._BuildStack(stackedHists, ytitle='Data/MC')
        mcHist = self.BGHist()
        if dataHist == None:
            dataHist = mcHist  # this was added to avoid crashes for SR plots (without data)
        self.dataOverMCHist = copy.deepcopy(dataHist)
        # self.dataOverMCHist.Add(mcHist, -1)
        self.dataOverMCHist.Divide(mcHist)
        self.dataOverMCHist.Draw()
        yaxis = self.dataOverMCHist.GetYaxis()
        yaxis.SetRangeUser(ymin + 1., ymax + 1.)
        yaxis.SetTitle('Data/MC')
        yaxis.SetNdivisions(5)
        fraclines = 0.2
        if ymax <= 0.2 or ymin >= -0.2:
            fraclines = 0.1
        self.DrawRatioLines(self.dataOverMCHist, fraclines, 1.)
        if TPad.Pad():
            TPad.Pad().Update()

    def DrawRatioStack(self,
                       opt='',
                       xmin=None,
                       xmax=None,
                       ymin=None,
                       ymax=None):
        '''Draw ratios.

        The stack is considered as a single histogram.'''
        denom = None
        # import pdb; pdb.set_trace()
        histForRatios = []
        denom = None
        for hist in self._SortedHistograms():
            if hist.stack is False:
                # if several unstacked histograms, the highest layer is used
                denom = hist
                continue
            histForRatios.append(hist)
        self._BuildStack(histForRatios, ytitle='MC/Data')
        self.stack.Divide(denom.obj)
        if self.blindminx and self.blindmaxx:
            self.stack.Blind(self.blindminx, self.blindmaxx)
        self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        self.ratios = []
        for hist in self.nostack:
            if hist is denom:
                continue
            ratio = copy.deepcopy(hist)
            ratio.obj.Divide(denom.obj)
            ratio.obj.Draw('same')
            self.ratios.append(ratio)
        self.DrawLegend(ratio=True)
        self.DrawRatioLines(denom, 0.2, 1)
        if TPad.Pad():
            TPad.Pad().Update()

    def DrawNormalizedRatioStack(self,
                                 opt='',
                                 xmin=None,
                                 xmax=None,
                                 ymin=None,
                                 ymax=None):
        '''Draw ratios.

        The stack is considered as a single histogram.
        All histograms are normalized before computing the ratio'''
        denom = None
        histForRatios = []
        for hist in self._SortedHistograms():
            # taking the first histogram (lowest layer)
            # as the denominator histogram.
            if denom == None:
                denom = copy.deepcopy(hist)
                continue
            # other histograms will be divided by the denominator
            histForRatios.append(hist)
        self._BuildStack(histForRatios, ytitle='MC p.d.f. / Data p.d.f.')
        self.stack.Normalize()
        denom.Normalize()
        self.stack.Divide(denom.weighted)
        self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        self.ratios = []
        for hist in self.nostack:
            # print 'nostack ', hist
            ratio = copy.deepcopy(hist)
            ratio.Normalize()
            ratio.obj.Divide(denom.weighted)
            ratio.obj.Draw('same')
            self.ratios.append(ratio)
        self.DrawLegend(ratio=True)
        self.DrawRatioLines(denom, 0.2, 1)
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalizedRatioStack'
        # self.lastDrawArgs = [ opt ]

    def DrawRatioLines(self, hist, frac=0.2, y0=1.):
        '''Draw a line at y = 1, at 1+frac, and at 1-frac.

        hist is used to get the x axis range.'''
        xmin = hist.obj.GetXaxis().GetXmin()
        xmax = hist.obj.GetXaxis().GetXmax()
        line = TLine()
        line.DrawLine(xmin, y0, xmax, y0)
        line.SetLineStyle(2)
        line.DrawLine(xmin, y0 + frac, xmax, y0 + frac)
        line.DrawLine(xmin, y0 - frac, xmax, y0 - frac)

    def GetStack(self):
        '''Returns stack; builds stack if not there yet'''
        if not self.stack:
            self._BuildStack(self._SortedHistograms(), ytitle='Events')
        return self.stack

    def BGHist(self):
        return self.GetStack().totalHist

    def SignalHists(self):
        return [h for h in self.nostack if not h.style.drawAsData]

    def DrawStack(self,
                  opt='',
                  xmin=None,
                  xmax=None,
                  ymin=None,
                  ymax=None,
                  print_norm=False,
                  scale_signal=''):
        '''Draw all histograms, some of them in a stack.

        if Histogram.stack is True, the histogram is put in the stack.
        scale_signal: mc_int -> scale to stack integral'''
        self._BuildStack(self._SortedHistograms(), ytitle='Events')
        same = 'same'
        if len(self.nostack) == 0:
            same = ''
        self.supportHist = None
        for hist in self.nostack:
            if hist.style.drawAsData:
                hist.Draw('SAME' if self.supportHist else '')
            else:
                if scale_signal == 'mc_int':
                    hist.Scale(hist.Yield(weighted=True) / self.stack.integral)
                hist.Draw('SAME HIST' if self.supportHist else 'HIST')
            if not self.supportHist:
                self.supportHist = hist
        self.stack.Draw(opt + same, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
        if self.supportHist is None:
            self.supportHist = self.BGHist()
        if not self.axisWasSet:
            mxsup = self.supportHist.weighted.GetBinContent(
                self.supportHist.weighted.GetMaximumBin())
            mxstack = self.BGHist().weighted.GetBinContent(
                self.BGHist().weighted.GetMaximumBin())
            mx = max(mxsup, mxstack)
            if ymin is None:
                ymin = 0.01
            if ymax is None:
                ymax = mx * 2
                centrality = self.supportHist.weighted.GetRMS() / (
                    self.supportHist.weighted.GetXaxis().GetXmax() -
                    self.supportHist.weighted.GetXaxis().GetXmin())
                if centrality > 0.15:
                    ymax = mx * 2.2

            self.supportHist.GetYaxis().SetRangeUser(ymin, ymax)
            self.axisWasSet = True
        for hist in self.nostack:
            if self.blindminx and hist.style.drawAsData:
                hist.Blind(self.blindminx, self.blindmaxx)
            if hist.style.drawAsData:
                hist.Draw('SAME')
            else:
                hist.Draw('SAME HIST')

        if self.supportHist.weighted.GetMaximumBin(
        ) < self.supportHist.weighted.GetNbinsX() / 2:
            #            self.legendBorders = 0.62, 0.46, 0.88, 0.89
            self.legendBorders = 0.20, 0.78, 0.80, 0.88
            #            self.legendPos = 'right'
            self.legendPos = 'top'

        self.DrawLegend(print_norm=print_norm)
        if TPad.Pad():
            TPad.Pad().Update()


#        set_trace()

    def DrawNormalizedStack(self,
                            opt='',
                            xmin=None,
                            xmax=None,
                            ymin=0.001,
                            ymax=None):
        '''Draw all histograms, some of them in a stack.

        if Histogram.stack is True, the histogram is put in the stack.
        all histograms out of the stack, and the stack itself, are shown as PDFs.'''
        self._BuildStack(self._SortedHistograms(), ytitle='p.d.f.')
        self.stack.DrawNormalized(opt,
                                  xmin=xmin,
                                  xmax=xmax,
                                  ymin=ymin,
                                  ymax=ymax)
        for hist in self.nostack:
            hist.obj.DrawNormalized('same')
        self.DrawLegend()
        if TPad.Pad():
            TPad.Pad().Update()
        # self.lastDraw = 'DrawNormalizedStack'
        # self.lastDrawArgs = [ opt ]

    def Rebin(self, factor):
        '''Rebin, and redraw.'''
        # the dispatching technique is not too pretty,
        # but keeping a self.lastDraw function initialized to one of the Draw functions
        # when calling it creates a problem in deepcopy.
        for hist in self.histos:
            hist.Rebin(factor)
        self.axisWasSet = False

    def NormalizeToBinWidth(self):
        '''Normalize each Histograms bin to the bin width.'''
        for hist in self.histos:
            hist.NormalizeToBinWidth()

    def WriteDataCard(self,
                      filename=None,
                      verbose=True,
                      mode='RECREATE',
                      dir=None,
                      postfix=''):
        '''Export current plot to datacard'''
        if not filename:
            filename = self.name + '.root'

        outf = TFile(filename, mode)
        if dir and outf.Get(dir):
            print 'Directory', dir, 'already present in output file'
            if any(
                    outf.Get(dir + '/' + hist.name + postfix)
                    for hist in self._SortedHistograms()):
                print 'Recreating file because histograms already present'
                outf = TFile(filename, 'RECREATE')
        if dir:
            outf_dir = outf.Get(dir)
            if not outf_dir:
                outf_dir = outf.mkdir(dir)
            outf_dir.cd()

        for hist in self._SortedHistograms():
            'Writing', hist, 'as', hist.name
            hist.weighted.Write(hist.name + postfix)
        outf.Write()

    def _BuildStack(self, hists, ytitle=None):
        '''build a stack from a list of Histograms.

        The histograms for which Histogram.stack is False are put in self.nostack'''
        self.stack = None
        self.stack = Stack(self.name + '_stack', ytitle=ytitle)
        self.nostack = []
        for hist in hists:
            if hist.stack:
                self.stack.Add(hist)
            else:
                self.nostack.append(hist)

    def _GetHistPref(self, name):
        '''Return the preference dictionary for a given component'''
        thePref = None
        for prefpat, pref in self.histPref.iteritems():
            if fnmatch.fnmatch(name, prefpat):
                if thePref is not None:
                    print 'several matching preferences for', name
                thePref = pref
        if thePref is None:
            print 'cannot find preference for hist', name
            thePref = {'style': Style(), 'layer': 999, 'legend': name}
        return thePref

    def _ApplyPrefs(self):
        for hist in self.histos:
            pref = self._GetHistPref(hist.name)
            hist.layer = pref['layer']
            hist.SetStyle(pref['style'])
            hist.legendLine = pref['legend']

    def __str__(self):
        if self.stack is None:
            self._BuildStack(self._SortedHistograms(), ytitle='Events')
        tmp = [' '.join(['DataMCPlot: ', self.name])]
        tmp.append('Histograms:')
        for hist in self._SortedHistograms(reverse=True):
            tmp.append(' '.join(['\t', str(hist)]))
        tmp.append(
            'Stack yield = {integ:7.1f}'.format(integ=self.stack.integral))
        return '\n'.join(tmp)