Пример #1
0
def EasyPlots(name,
              histlist,
              bkglist=[],
              signals=[],
              colors=[],
              titles=[],
              logy=False,
              xtitle='',
              ytitle='',
              dataOff=False,
              datastyle='pe'):
    '''Tool to produce plots quickly as .root, .pdf, .png, etc.
    If providing TH2s, only plots the data (histlist) with no comparisons.
    If providing TH1s, plots together the data (histlist), total background (individual components as a stack), and signals with pulls in a lower pane.
    Providing multiple data histograms will plot the separate pads on a single canvas. Up to 6 pads are supported.
    See argument descriptions for plotting options.

    histlist is just the generic list but if bkglist is specified (non-empty)
    then this function will stack the backgrounds and compare against histlist as if 
    it is data. The important bit is that bkglist is a list of lists. The first index
    of bkglist corresponds to the index in histlist (the corresponding data). 
    For example you could have:
      ```python
      histlist = [data1, data2]
      bkglist = [[bkg1_1,bkg2_1],[bkg1_2,bkg2_2]]
      ```

    @param name (str): Name of output file with extension (file type must be supported by TCanvas.Print()).
    @param histlist ([TH1 or TH2]): List of data histograms.
    @param bkglist ([[TH1]], optional): List of list of background histograms. Index of first list level matches
            against index of histlist. Internal list is the group of backgrounds that will be stacked together
            to create the total background estimate. For example you could have: `histlist = [data1, data2]` and 
            `bkglist = [[bkg1_1,bkg2_1],[bkg1_2,bkg2_2]]`. Defaults to [].
    @param signals ([TH1], optional): List of signal histograms where index corresponds to the index of histlist. Defaults to [].
    @param colors ([int], optional): List of integers corresponding to TColor codes where the index corresponds to the index of histlist. Defaults to [].
    @param titles ([str], optional): List of titles for each histogram where the index corresponds to the index of histlist. Defaults to [] which means the input titles of histlist will be used.
    @param logy (bool, optional): Option to plot log y-axis. Defaults to False.
    @param xtitle (str, optional): X-axis title for all histograms on the canvas. Defaults to '' which means the input titles of histlist will be used.
    @param ytitle (str, optional): Y-axis title for all histograms on the canvas. Defaults to '' which means the input titles of histlist will be used.
    @param dataOff (bool, optional): If True, turns off data from being drawn in all pads. Defaults to False.
    @param datastyle (str, optional): Style in which to draw data. Defaults to 'pe'.

    Raises:
        ValueError: If number of requested pads is greater than 6.
    '''
    extension = name.split('.')[-1]
    tag = name.split('.')[0]

    if len(histlist) == 1:
        width = 800
        height = 700
        padx = 1
        pady = 1
    elif len(histlist) == 2:
        width = 1200
        height = 700
        padx = 2
        pady = 1
    elif len(histlist) == 3:
        width = 1600
        height = 700
        padx = 3
        pady = 1
    elif len(histlist) == 4:
        width = 1200
        height = 1000
        padx = 2
        pady = 2
    elif len(histlist) == 6 or len(histlist) == 5:
        width = 1600
        height = 1000
        padx = 3
        pady = 2
    else:
        raise ValueError('histlist of size ' + str(len(histlist)) +
                         ' not currently supported')

    tdrstyle.setTDRStyle()

    myCan = ROOT.TCanvas(tag, tag, width, height)
    myCan.Divide(padx, pady)

    # Just some colors that I think work well together and a bunch of empty lists for storage if needed
    default_colors = [
        ROOT.kRed, ROOT.kMagenta, ROOT.kGreen, ROOT.kCyan, ROOT.kBlue
    ]
    if len(colors) == 0:
        colors = default_colors
    stacks = []
    tot_hists = []
    legends = []
    mains = []
    subs = []
    pulls = []
    logString = ''

    # For each hist/data distribution
    for hist_index, hist in enumerate(histlist):
        # Grab the pad we want to draw in
        myCan.cd(hist_index + 1)
        # if len(histlist) > 1:
        thisPad = myCan.GetPrimitive(tag + '_' + str(hist_index + 1))
        thisPad.cd()

        # If this is a TH2, just draw the lego
        if hist.ClassName().find('TH2') != -1:
            if logy == True:
                ROOT.gPad.SetLogy()
            ROOT.gPad.SetLeftMargin(0.2)
            hist.GetXaxis().SetTitle(xtitle)
            hist.GetYaxis().SetTitle(ytitle)
            hist.GetXaxis().SetTitleOffset(1.5)
            hist.GetYaxis().SetTitleOffset(2.3)
            hist.GetZaxis().SetTitleOffset(1.8)
            if len(titles) > 0:
                hist.SetTitle(titles[hist_index])

            hist.Draw('lego')
            if len(bkglist) > 0:
                print(
                    'ERROR: It seems you are trying to plot backgrounds with data on a 2D plot. This is not supported since there is no good way to view this type of distribution.'
                )

        # Otherwise it's a TH1 hopefully
        else:
            alpha = 1
            if dataOff:
                alpha = 0
            hist.SetLineColorAlpha(ROOT.kBlack, alpha)
            if 'pe' in datastyle.lower():
                hist.SetMarkerColorAlpha(ROOT.kBlack, alpha)
                hist.SetMarkerStyle(8)
            if 'hist' in datastyle.lower():
                hist.SetFillColorAlpha(0, 0)

            # If there are no backgrounds, only plot the data (semilog if desired)
            if len(bkglist) == 0:
                hist.GetXaxis().SetTitle(xtitle)
                hist.GetYaxis().SetTitle(ytitle)
                if len(titles) > 0:
                    hist.SetTitle(titles[hist_index])
                hist.Draw(datastyle)

            # Otherwise...
            else:
                # Create some subpads, a legend, a stack, and a total bkg hist that we'll use for the error bars
                if not dataOff:
                    mains.append(
                        ROOT.TPad(hist.GetName() + '_main',
                                  hist.GetName() + '_main', 0, 0.3, 1, 1))
                    subs.append(
                        ROOT.TPad(hist.GetName() + '_sub',
                                  hist.GetName() + '_sub', 0, 0, 1, 0.3))

                else:
                    mains.append(
                        ROOT.TPad(hist.GetName() + '_main',
                                  hist.GetName() + '_main', 0, 0.1, 1, 1))
                    subs.append(
                        ROOT.TPad(hist.GetName() + '_sub',
                                  hist.GetName() + '_sub', 0, 0, 0, 0))

                legends.append(ROOT.TLegend(0.70, 0.6, 0.95, 0.90))
                stacks.append(
                    ROOT.THStack(hist.GetName() + '_stack',
                                 hist.GetName() + '_stack'))
                tot_hist = hist.Clone(hist.GetName() + '_tot')
                tot_hist.Reset()
                tot_hist.SetTitle(hist.GetName() + '_tot')
                tot_hist.SetMarkerStyle(0)
                tot_hists.append(tot_hist)

                # Set margins and make these two pads primitives of the division, thisPad
                mains[hist_index].SetBottomMargin(0.0)
                mains[hist_index].SetLeftMargin(0.16)
                mains[hist_index].SetRightMargin(0.05)
                mains[hist_index].SetTopMargin(0.1)

                subs[hist_index].SetLeftMargin(0.16)
                subs[hist_index].SetRightMargin(0.05)
                subs[hist_index].SetTopMargin(0)
                subs[hist_index].SetBottomMargin(0.3)
                mains[hist_index].Draw()
                subs[hist_index].Draw()

                # Build the stack
                for bkg_index, bkg in enumerate(
                        bkglist[hist_index]):  # Won't loop if bkglist is empty
                    # bkg.Sumw2()
                    tot_hists[hist_index].Add(bkg)
                    bkg.SetLineColor(ROOT.kBlack)
                    if logy:
                        bkg.SetMinimum(1e-3)

                    if bkg.GetName().find('qcd') != -1:
                        bkg.SetFillColor(ROOT.kYellow)

                    else:
                        if colors[bkg_index] != None:
                            bkg.SetFillColor(colors[bkg_index])
                        else:
                            bkg.SetFillColor(default_colors[bkg_index])

                    stacks[hist_index].Add(bkg)

                    legends[hist_index].AddEntry(bkg,
                                                 bkg.GetName().split('_')[0],
                                                 'f')

                # Go to main pad, set logy if needed
                mains[hist_index].cd()

                # Set y max of all hists to be the same to accomodate the tallest
                histList = [stacks[hist_index], tot_hists[hist_index], hist]

                yMax = histList[0].GetMaximum()
                maxHist = histList[0]
                for h in range(1, len(histList)):
                    if histList[h].GetMaximum() > yMax:
                        yMax = histList[h].GetMaximum()
                        maxHist = histList[h]
                for h in histList:
                    h.SetMaximum(yMax * 1.1)
                    if logy == True:
                        h.SetMaximum(yMax * 10)

                mLS = 0.06
                # Now draw the main pad
                data_leg_title = hist.GetTitle()
                if len(titles) > 0:
                    hist.SetTitle(titles[hist_index])
                hist.SetTitleOffset(1.5, "xy")
                hist.GetYaxis().SetTitle('Events')
                hist.GetYaxis().SetLabelSize(mLS)
                hist.GetYaxis().SetTitleSize(mLS)
                if logy == True:
                    hist.SetMinimum(1e-3)
                hist.Draw(datastyle)

                stacks[hist_index].Draw('same hist')

                # Do the signals
                if len(signals) > 0:
                    signals[hist_index].SetLineColor(kBlue)
                    signals[hist_index].SetLineWidth(2)
                    if logy == True:
                        signals[hist_index].SetMinimum(1e-3)
                    legends[hist_index].AddEntry(
                        signals[hist_index],
                        signals[hist_index].GetName().split('_')[0], 'L')
                    signals[hist_index].Draw('hist same')

                tot_hists[hist_index].SetFillColor(kBlack)
                tot_hists[hist_index].SetFillStyle(3354)

                tot_hists[hist_index].Draw('e2 same')
                # legends[hist_index].Draw()

                if not dataOff:
                    legends[hist_index].AddEntry(hist, 'data', datastyle)
                    hist.Draw(datastyle + ' same')

                ROOT.gPad.RedrawAxis()

                # Draw the pull
                subs[hist_index].cd()
                # Build the pull
                pulls.append(MakePullPlot(hist, tot_hists[hist_index]))
                pulls[hist_index].SetFillColor(ROOT.kBlue)
                pulls[hist_index].SetTitle(";" + hist.GetXaxis().GetTitle() +
                                           ";(Data-Bkg)/#sigma")
                pulls[hist_index].SetStats(0)

                LS = .13

                pulls[hist_index].GetYaxis().SetRangeUser(-2.9, 2.9)
                pulls[hist_index].GetYaxis().SetTitleOffset(0.4)
                pulls[hist_index].GetXaxis().SetTitleOffset(0.9)

                pulls[hist_index].GetYaxis().SetLabelSize(LS)
                pulls[hist_index].GetYaxis().SetTitleSize(LS)
                pulls[hist_index].GetYaxis().SetNdivisions(306)
                pulls[hist_index].GetXaxis().SetLabelSize(LS)
                pulls[hist_index].GetXaxis().SetTitleSize(LS)

                pulls[hist_index].GetXaxis().SetTitle(xtitle)
                pulls[hist_index].GetYaxis().SetTitle("(Data-Bkg)/#sigma")
                pulls[hist_index].Draw('hist')

                if logy == True:
                    mains[hist_index].SetLogy()

                CMS_lumi.CMS_lumi(thisPad, 4, 11)

    myCan.Print(name, extension)
Пример #2
0
def CompareShapesWithSoverB(outfilename,
                            year,
                            prettyvarname,
                            bkgs={},
                            signals={},
                            names={},
                            colors={},
                            scale=True,
                            stackBkg=True):
    '''Create a plot that compares the shapes of backgrounds versus signal.
       Backgrounds will be stacked together and signals will be plot separately.
       Total background and signals are scaled to 1 if scale = True. Inputs organized 
       as dicts so that keys can match across dicts (ex. bkgs and bkgNames).

       Added sub pad with signal/sqrt(background) calculation

    Args:
        outfilename (string): Path where plot will be saved.
        prettyvarname (string): What will be assigned to as the axis title.
        bkgs ({string:TH1}, optional): . Defaults to {}.
        signals ({string:TH1], optional): [description]. Defaults to {}.
        names ({string:string}, optional): Formatted version of names for backgrounds and signals to appear in legend. Keys must match those in bkgs and signal. Defaults to {}. 
        colors ({string:int}, optional): TColor code for backgrounds and signals to appear in plot. Keys must match those in bkgs and signal. Defaults to {}.
        scale (bool, optional): Scales everything to unity if true. Defaults to True.
        stackBkg (bool, optional): Stack backgrounds together. Defaults to True
    '''
    # Prep everything as with CompareShapes() but first check we're stacking backgrounds or this won't work
    if not stackBkg:
        raise ValueError(
            'Cannot run without backgrounds stacked or s/sqrt(b) will not be valid.'
        )

    # Initialize
    c = ROOT.TCanvas('c', 'c', 800, 700)
    legend = ROOT.TLegend(0.6, 0.72, 0.87, 0.88)
    legend.SetBorderSize(0)
    ROOT.gStyle.SetTextFont(42)
    ROOT.gStyle.SetOptStat(0)
    tot_bkg_int = 0
    if stackBkg:
        bkgStack = ROOT.THStack('Totbkg', 'Total Bkg - ' + prettyvarname)
        bkgStack.SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))
        # Add bkgs to integral
        for bkey in bkgs.keys():
            tot_bkg_int += bkgs[bkey].Integral()
    else:
        for bkey in bkgs.keys():
            bkgs[bkey].SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))

    if colors == None:
        colors = {
            'signal': ROOT.kBlue,
            'qcd': ROOT.kYellow,
            'ttbar': ROOT.kRed,
            'multijet': ROOT.kYellow
        }

    if scale:
        # Scale bkgs to total integral
        for bkey in bkgs.keys():
            if stackBkg: bkgs[bkey].Scale(1.0 / tot_bkg_int)
            else: bkgs[bkey].Scale(1.0 / bkgs[bkey].Integral())
        # Scale signals
        for skey in signals.keys():
            signals[skey].Scale(1.0 / signals[skey].Integral())

    # Now add bkgs to stack, setup legend, and draw!
    colors_in_legend = []
    procs = OrderedDict()
    procs.update(bkgs)
    procs.update(signals)
    for pname in procs.keys():
        h = procs[pname]
        # Legend names
        if pname in names.keys(): leg_name = names[pname]
        else: leg_name = pname
        # If bkg, set fill color and add to stack
        if pname in bkgs.keys():
            h.SetFillColorAlpha(colors[pname], 0.2 if not stackBkg else 1)
            h.SetLineWidth(0)
            if stackBkg: bkgStack.Add(h)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'f')
                colors_in_legend.append(colors[pname])

        # If signal, set line color
        else:
            h.SetLineColor(colors[pname])
            h.SetLineWidth(2)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'l')
                colors_in_legend.append(colors[pname])

    if stackBkg:
        maximum = max(bkgStack.GetMaximum(),
                      signals.values()[0].GetMaximum()) * 1.4
        bkgStack.SetMaximum(maximum)
    else:
        maximum = max(bkgs.values()[0].GetMaximum(),
                      signals.values()[0].GetMaximum()) * 1.4
        for p in procs.values():
            p.SetMaximum(maximum)

    #
    # Build sub pads
    #
    c.cd()
    main = ROOT.TPad('c_main', 'c_main', 0, 0.3, 1, 1)
    SoverB = ROOT.TPad('c_sub', 'c_sub', 0, 0, 1, 0.3)

    main.SetBottomMargin(0.0)
    main.SetLeftMargin(0.1)
    main.SetRightMargin(0.05)
    main.SetTopMargin(0.1)

    SoverB.SetLeftMargin(0.1)
    SoverB.SetRightMargin(0.05)
    SoverB.SetTopMargin(0)
    SoverB.SetBottomMargin(0.3)

    main.Draw()
    SoverB.Draw()

    ################
    # Draw on main #
    ################
    main.cd()
    if len(bkgs.keys()) > 0:
        if stackBkg:
            bkgStack.Draw('hist')
            bkgStack.GetXaxis().SetTitleOffset(1.1)
            bkgStack.Draw('hist')
        else:
            for bkg in bkgs.values():
                bkg.GetXaxis().SetTitleOffset(1.1)
                bkg.Draw('same hist')
    for h in signals.values():
        h.Draw('same hist')
    legend.Draw()

    #
    # Make the S/sqrt(B) sub pad with dedicated function
    #
    s_over_b, line_pos = MakeSoverB(bkgStack, signals.values()[0])
    SoverB.cd()
    s_over_b.GetYaxis().SetTitle('S/#sqrt{B}')
    s_over_b.GetXaxis().SetTitle(prettyvarname)
    s_over_b.SetTitle('')
    s_over_b.SetLineColorAlpha(ROOT.kBlack, 1)
    s_over_b.SetLineWidth(2)
    s_over_b.SetFillColorAlpha(ROOT.kWhite, 0)
    s_over_b.GetYaxis().SetLabelSize(0.08)
    s_over_b.GetYaxis().SetTitleSize(0.08)
    s_over_b.GetYaxis().SetNdivisions(306)
    s_over_b.GetXaxis().SetLabelSize(0.09)
    s_over_b.GetXaxis().SetTitleSize(0.09)
    s_over_b.GetYaxis().SetTitleOffset(0.4)
    s_over_b.Draw('hist')
    if line_pos:
        line = ROOT.TLine(line_pos, s_over_b.GetMinimum(), line_pos,
                          s_over_b.GetMaximum())
        line.SetLineColor(ROOT.kRed)
        line.SetLineStyle(10)
        line.SetLineWidth(2)
        line.Draw('same')

    # Canvas wide settings
    c.cd()
    c.SetBottomMargin(0.12)
    c.SetTopMargin(0.08)
    c.SetRightMargin(0.11)
    CMS_lumi.writeExtraText = 1
    CMS_lumi.extraText = "Preliminary simulation"
    CMS_lumi.lumi_sqrtS = "13 TeV"
    CMS_lumi.cmsTextSize = 0.6
    CMS_lumi.CMS_lumi(c, year, 11)

    c.Print(outfilename, 'png')
Пример #3
0
def CompareShapes(outfilename,
                  year,
                  prettyvarname,
                  bkgs={},
                  signals={},
                  names={},
                  colors={},
                  scale=True,
                  stackBkg=False,
                  doSoverB=False,
                  forceForward=False,
                  forceBackward=False):
    '''Create a plot that compares the shapes of backgrounds versus signal.
    If stackBkg, backgrounds will be stacked together and signals will be plot separately.
    Total background and signals are scaled to 1 if scale == True. Inputs organized 
    as dicts so that keys can match across dicts (ex. bkgs and bkgNames). If
    doSoverB is True, add a sub pad with signal/sqrt(background) calculation.

    @param outfilename (string): Path where plot will be saved.
    @param year (int): Year to determine luminosity value on plot. Options are 16, 17, 18, 1 (for full Run II),
        or 2 (for full Run II but represented as separate years, ie. 2016+2017+2018).
    @param prettyvarname (string): What will be assigned to as the axis title.
    @param bkgs ({string:TH1}, optional): Dictionary of backgrounds to plot. Defaults to {}.
    @param signals ({string:TH1}, optional): Dictionary of signals to plot. Defaults to {}.
    @param names ({string:string}, optional): Formatted version of names for backgrounds and signals to
        appear in legend. Keys must match those in bkgs and signal. Defaults to {}. 
    @param colors ({string:int}, optional): TColor code for backgrounds and signals to appear in plot.
        Keys must match those in bkgs and signal. Defaults to {}.
    @param scale (bool, optional): If True, scales total background to unity and signals (separately)
        to unity. Defaults to True.
    @param stackBkg (bool, optional): If True, backgrounds will be stacked and the total will be
        normalized to 1 (if scale==True). Defaults to False.
    @param doSoverB (bool, optional): If True, add a sub pad with signal/sqrt(background) calculation. Defaults to False.
    @param forceForward (bool, optional): If True, force define cumulative distribution from left to right. Defaults to False.
    @param forceBackward (bool, optional): If True, force define cumulative distribution from right to left. Defaults to False.

    Returns:
        None
    '''
    if not stackBkg and doSoverB:
        raise ValueError(
            'Cannot run without backgrounds stacked or s/sqrt(b) will not be valid.'
        )

    # Initialize
    c = ROOT.TCanvas('c', 'c', 800, 700)
    c.SetBottomMargin(0.12)
    c.SetTopMargin(0.08)
    c.SetRightMargin(0.05)
    legend = ROOT.TLegend(
        0.73, 0.8 - 0.04 * (len(bkgs.keys()) + len(signals.keys()) - 1), 0.9,
        0.88)
    legend.SetBorderSize(0)
    # ROOT.gStyle.SetTextFont(42)
    ROOT.gStyle.SetOptStat(0)
    tot_bkg_int = 0
    if stackBkg:
        bkgStack = ROOT.THStack('Totbkg', 'Total Bkg - ' + prettyvarname)
        bkgStack.SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))
        # Add bkgs to integral
        for bkey in bkgs.keys():
            tot_bkg_int += bkgs[bkey].Integral()
    else:
        for bkey in bkgs.keys():
            bkgs[bkey].SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))

    if scale:
        # Scale bkgs to total integral
        for bkey in bkgs.keys():
            if stackBkg: bkgs[bkey].Scale(1.0 / tot_bkg_int)
            else: bkgs[bkey].Scale(1.0 / bkgs[bkey].Integral())
        # Scale signals
        for skey in signals.keys():
            signals[skey].Scale(1.0 / signals[skey].Integral())

    # Now add bkgs to stack, setup legend, and draw!
    colors_in_legend = []
    procs = OrderedDict()
    procs.update(bkgs)
    procs.update(signals)
    icolor = 2
    for pname in procs.keys():
        h = procs[pname]
        # Colors
        if pname not in colors.keys():
            colors[pname] = icolor
            icolor += 1
        # Legend names
        if pname in names.keys(): leg_name = names[pname]
        else: leg_name = pname
        # If bkg, set fill color and add to stack
        if pname in bkgs.keys():
            h.SetFillColorAlpha(colors[pname], 0.2 if not stackBkg else 1)
            h.SetLineWidth(0)
            if stackBkg: bkgStack.Add(h)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'f')
                colors_in_legend.append(colors[pname])

        # If signal, set line color
        else:
            h.SetLineColor(colors[pname])
            h.SetLineWidth(2)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'l')
                colors_in_legend.append(colors[pname])

    if stackBkg:
        maximum = max(bkgStack.GetMaximum(),
                      max([s.GetMaximum() for s in signals.values()])) * 1.4
        bkgStack.SetMaximum(maximum)
    else:
        if len(bkgs.values()) > 0: bkgmax = list(bkgs.values())[0].GetMaximum()
        else: bkgmax = 0
        if len(signals.values()) > 0:
            sigmax = list(signals.values())[0].GetMaximum()
        else:
            sigmax = 0

        maximum = max(bkgmax, sigmax) * 1.4
        for p in procs.values():
            p.SetMaximum(maximum)

    c.cd()
    if doSoverB:  # build sub pads
        main = ROOT.TPad('c_main', 'c_main', 0, 0.3, 1, 1)
        SoverB = ROOT.TPad('c_sub', 'c_sub', 0, 0, 1, 0.3)

        main.SetBottomMargin(0.0)
        main.SetLeftMargin(0.1)
        main.SetRightMargin(0.05)
        main.SetTopMargin(0.1)

        SoverB.SetLeftMargin(0.1)
        SoverB.SetRightMargin(0.05)
        SoverB.SetTopMargin(0)
        SoverB.SetBottomMargin(0.3)

        main.Draw()
        SoverB.Draw()

        main.cd()

    if len(bkgs.keys()) > 0:
        if stackBkg:
            bkgStack.Draw('hist')
            bkgStack.GetXaxis().SetTitleOffset(1.1)
            bkgStack.Draw('hist')
        else:
            for bkg in bkgs.values():
                bkg.GetXaxis().SetTitleOffset(1.1)
                bkg.Draw('same hist')
    for h in signals.values():
        h.Draw('same hist')
    legend.Draw()

    if doSoverB:
        s_over_b, line_pos, maximums = MakeSoverB(bkgStack,
                                                  list(signals.values())[0],
                                                  forceForward, forceBackward)
        SoverB.cd()
        s_over_b.GetYaxis().SetTitle('S/#sqrt{B}')
        s_over_b.GetXaxis().SetTitle(prettyvarname)
        s_over_b.SetTitle('')
        s_over_b.SetLineColorAlpha(ROOT.kBlack, 1)
        s_over_b.SetLineWidth(2)
        s_over_b.SetFillColorAlpha(ROOT.kWhite, 0)
        s_over_b.GetYaxis().SetLabelSize(0.08)
        s_over_b.GetYaxis().SetTitleSize(0.08)
        s_over_b.GetYaxis().SetNdivisions(306)
        s_over_b.GetXaxis().SetLabelSize(0.09)
        s_over_b.GetXaxis().SetTitleSize(0.09)
        s_over_b.GetYaxis().SetTitleOffset(0.4)
        s_over_b.Draw('hist')
        if line_pos:  # split line
            line = ROOT.TLine(line_pos, s_over_b.GetMinimum(), line_pos,
                              s_over_b.GetMaximum())
            line.SetLineColor(ROOT.kRed)
            line.SetLineStyle(10)
            line.SetLineWidth(2)
            line.Draw('same')
        # Optimization line
        temp = []
        for m in maximums:
            low = s_over_b.GetMinimum()
            high = s_over_b.GetMaximum()
            mline = ROOT.TLine(m, low, m, high)
            mline.SetLineColor(ROOT.kBlue)
            mline.SetLineStyle(0)
            mline.Draw('same')
            temp.append(mline)
            text = ROOT.TText(m, low + (high - low) / 3, " %.2f " % m)
            if (s_over_b.GetXaxis().GetXmax() -
                    m) < (0.9 * s_over_b.GetXaxis().GetXmax()):
                text.SetTextAlign(31)
                text.SetTextSize(0.09)
            text.Draw()
            temp.append(text)
    c.cd()

    # CMS_lumi.writeExtraText = 1
    # CMS_lumi.extraText = "Preliminary simulation"
    # CMS_lumi.cmsTextSize = 0.6
    # CMS_lumi.lumiTextSize = 0.75
    # CMS_lumi.cmsTextSize = 0.85
    CMS_lumi.CMS_lumi(c, iPeriod=year, sim=True)

    c.Print(outfilename, outfilename.split('.')[-1])
Пример #4
0
def CompareShapes(outfilename,
                  year,
                  prettyvarname,
                  bkgs={},
                  signals={},
                  names={},
                  colors={},
                  scale=True,
                  stackBkg=False):
    '''Create a plot that compares the shapes of backgrounds versus signal.
    If stackBkg, backgrounds will be stacked together and signals will be plot separately.
    Total background and signals are scaled to 1 if scale == True. Inputs organized 
    as dicts so that keys can match across dicts (ex. bkgs and bkgNames).

    @param outfilename (string): Path where plot will be saved.
    @param year (int): Year to determine luminosity value on plot. Options are 16, 17, 18, 1 (for full Run II),
        or 2 (for full Run II but represented as separate years, ie. 2016+2017+2018).
    @param prettyvarname (string): What will be assigned to as the axis title.
    @param bkgs ({string:TH1}, optional): Dictionary of backgrounds to plot. Defaults to {}.
    @param signals ({string:TH1}, optional): Dictionary of signals to plot. Defaults to {}.
    @param names ({string:string}, optional): Formatted version of names for backgrounds and signals to appear in legend. Keys must match those in bkgs and signal. Defaults to {}. 
    @param colors ({string:int}, optional): TColor code for backgrounds and signals to appear in plot. Keys must match those in bkgs and signal. Defaults to {}.
    @param scale (bool, optional): If True, scales total background to unity and signals (separately) to unity. Defaults to True.
    @param stackBkg (bool, optional): If True, backgrounds will be stacked and the total will be normalized to 1 (if scale==True). Defaults to False.
    '''
    # Initialize
    c = ROOT.TCanvas('c', 'c', 800, 700)
    legend = ROOT.TLegend(0.6, 0.72, 0.87, 0.88)
    legend.SetBorderSize(0)
    ROOT.gStyle.SetTextFont(42)
    ROOT.gStyle.SetOptStat(0)
    tot_bkg_int = 0
    if stackBkg:
        bkgStack = ROOT.THStack('Totbkg', 'Total Bkg - ' + prettyvarname)
        bkgStack.SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))
        # Add bkgs to integral
        for bkey in bkgs.keys():
            tot_bkg_int += bkgs[bkey].Integral()
    else:
        for bkey in bkgs.keys():
            bkgs[bkey].SetTitle(';%s;%s' % (prettyvarname, 'A.U.'))

    if colors == None:
        colors = {
            'signal': ROOT.kBlue,
            'qcd': ROOT.kYellow,
            'ttbar': ROOT.kRed,
            'multijet': ROOT.kYellow
        }

    if scale:
        # Scale bkgs to total integral
        for bkey in bkgs.keys():
            if stackBkg: bkgs[bkey].Scale(1.0 / tot_bkg_int)
            else: bkgs[bkey].Scale(1.0 / bkgs[bkey].Integral())
        # Scale signals
        for skey in signals.keys():
            signals[skey].Scale(1.0 / signals[skey].Integral())

    # Now add bkgs to stack, setup legend, and draw!
    colors_in_legend = []
    procs = OrderedDict()
    procs.update(bkgs)
    procs.update(signals)
    for pname in procs.keys():
        h = procs[pname]
        # Legend names
        if pname in names.keys(): leg_name = names[pname]
        else: leg_name = pname
        # If bkg, set fill color and add to stack
        if pname in bkgs.keys():
            h.SetFillColorAlpha(colors[pname], 0.2 if not stackBkg else 1)
            h.SetLineWidth(0)
            if stackBkg: bkgStack.Add(h)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'f')
                colors_in_legend.append(colors[pname])

        # If signal, set line color
        else:
            h.SetLineColor(colors[pname])
            h.SetLineWidth(2)
            if colors[pname] not in colors_in_legend:
                legend.AddEntry(h, leg_name, 'l')
                colors_in_legend.append(colors[pname])

    if stackBkg:
        maximum = max(bkgStack.GetMaximum(),
                      signals.values()[0].GetMaximum()) * 1.4
        bkgStack.SetMaximum(maximum)
    else:
        maximum = max(bkgs.values()[0].GetMaximum(),
                      signals.values()[0].GetMaximum()) * 1.4
        for p in procs.values():
            p.SetMaximum(maximum)

    c.cd()
    if len(bkgs.keys()) > 0:
        if stackBkg:
            bkgStack.Draw('hist')
            bkgStack.GetXaxis().SetTitleOffset(1.1)
            bkgStack.Draw('hist')
        else:
            for bkg in bkgs.values():
                bkg.GetXaxis().SetTitleOffset(1.1)
                bkg.Draw('same hist')
    for h in signals.values():
        h.Draw('same hist')
    legend.Draw()

    c.SetBottomMargin(0.12)
    c.SetTopMargin(0.08)
    c.SetRightMargin(0.11)
    CMS_lumi.writeExtraText = 1
    CMS_lumi.extraText = "Preliminary simulation"
    CMS_lumi.lumi_sqrtS = "13 TeV"
    CMS_lumi.cmsTextSize = 0.6
    CMS_lumi.CMS_lumi(c, year, 11)

    c.Print(outfilename, 'png')