예제 #1
0
def plot2dhistogram(hist, outfilepath, outfmts=['.png'],
		    histtitle=None, logx=False, logy=False, 
		    drawoptions='colztexte', cmin=None, cmax=None,
		    docmstext=False, cms_in_grid=True, 
		    cmstext_size_factor=0.3, extracmstext='', lumitext='',
		    topmargin=None, bottommargin=None, leftmargin=None, rightmargin=None,
		    extrainfos=[], infosize=None, infoleft=None, infotop=None ):
    # options:
    # - cmin and cmax: minimum and maximum values for the color scales
    #   note: in default "colz" behaviour, bins above cmax are colored as cmax,
    #         while bins below cmin are left blank.
    #         they can be colored as cmin by using drawoption "col0z" instead of "colz",
    #         but "col0ztexte" does not seem to write the bin contents for those bins...

    pt.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)
    
    # set global properties
    cheight = 500 # height of canvas
    cwidth = 700 # width of canvas
    titlefont = 4; titlesize = 22
    axtitlefont = 4; axtitlesize = 22
    infofont = 4
    if infosize is None: infosize = 15
    # title offset
    ytitleoffset = 1.5
    xtitleoffset = 1.5
    # margins:
    if topmargin is None: topmargin = 0.15
    if bottommargin is None: bottommargin = 0.15
    if leftmargin is None: leftmargin = 0.15
    if rightmargin is None: rightmargin = 0.15
    xmin = hist.GetXaxis().GetXmin()
    xmax = hist.GetXaxis().GetXmax()
    ymin = hist.GetYaxis().GetXmin()
    ymax = hist.GetYaxis().GetXmax()
    zmin = hist.GetMinimum()
    zmax = hist.GetMaximum()
예제 #2
0
def plotsinglehistogram(hist,
                        figname,
                        title=None,
                        xaxtitle=None,
                        yaxtitle=None,
                        label=None,
                        color=None,
                        logy=False,
                        drawoptions='',
                        lumitext='',
                        extralumitext='',
                        topmargin=None,
                        bottommargin=None,
                        leftmargin=None,
                        rightmargin=None,
                        xaxlabelfont=None,
                        xaxlabelsize=None,
                        writebincontent=False,
                        bincontentfont=None,
                        bincontentsize=None,
                        bincontentfmt=None,
                        extrainfos=[],
                        infosize=None,
                        infoleft=None,
                        infotop=None):
    ### drawing a single histogram
    # - label: string for the legend entry for this histogram.
    #	note: if label is 'auto', the implicit title of the TH1 will be used.
    # - drawoptions: string passed to TH1.Draw.
    #   use "HIST" for histogram style (no error bars)
    #       "E" for error bars
    #       "X0" to suppress horizontal error bars
    #       see https://root.cern/doc/master/classTHistPainter.html for a full list of options
    # - extrainfos is a list of strings with extra info to display
    # - infosize: font size of extra info
    # - infoleft: left border of extra info text (default leftmargin + 0.05)
    # - infotop: top border of extra info text (default 1 - topmargin - 0.1)

    pt.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)

    ### parse arguments
    if color is None: color = ROOT.kAzure - 4

    ### initializations
    # make canvas
    c1 = ROOT.TCanvas("c1", "c1")
    c1.SetCanvasSize(600, 600)
    pad1 = ROOT.TPad("pad1", "", 0., 0., 1., 1.)
    pad1.SetTicks(1, 1)
    pad1.SetFrameLineWidth(2)
    pad1.SetGrid()
    pad1.Draw()
    pad1.cd()
    # set fonts and text sizes
    titlefont = 6
    titlesize = 26
    if xaxlabelfont is None: xaxlabelfont = 4
    if xaxlabelsize is None: xaxlabelsize = 22
    yaxlabelfont = 4
    yaxlabelsize = 22
    axtitlefont = 4
    axtitlesize = 26
    legendfont = 4
    if bincontentfont is None: bincontentfont = 4
    if bincontentsize is None: bincontentsize = 12
    if bincontentfmt is None: bincontentfmt = '{:.3f}'
    infofont = 4
    if infosize is None: infosize = 20
    # margins
    if leftmargin is None: leftmargin = 0.15
    if rightmargin is None: rightmargin = 0.05
    if topmargin is None: topmargin = 0.05
    if bottommargin is None: bottommargin = 0.15
    pad1.SetBottomMargin(bottommargin)
    pad1.SetLeftMargin(leftmargin)
    pad1.SetRightMargin(rightmargin)
    pad1.SetTopMargin(topmargin)
    # extra info box parameters
    if infoleft is None: infoleft = leftmargin + 0.05
    if infotop is None: infotop = 1 - topmargin - 0.1
    # legendbox
    pentryheight = 0.05
    plegendbox = ([
        leftmargin + 0.3, 1 - topmargin - pentryheight, 1 - rightmargin - 0.03,
        1 - topmargin - 0.03
    ])

    ### set histogram properties
    hist.SetLineColor(color)
    hist.SetLineWidth(3)
    hist.Sumw2()

    ### make the legend
    if label is not None:
        leg = ROOT.TLegend(plegendbox[0], plegendbox[1], plegendbox[2],
                           plegendbox[3])
        leg.SetTextFont(10 * legendfont + 3)
        leg.SetFillColor(ROOT.kWhite)
        leg.SetBorderSize(1)
        if label == 'auto': label = hist.GetTitle()
        leg.AddEntry(hist, label, "l")
    hist.Draw(drawoptions)

    ### X-axis layout
    xax = hist.GetXaxis()
    xax.SetNdivisions(5, 4, 0, ROOT.kTRUE)
    xax.SetLabelFont(10 * xaxlabelfont + 3)
    xax.SetLabelSize(xaxlabelsize)
    if xaxtitle is not None:
        xax.SetTitle(xaxtitle)
        xax.SetTitleFont(10 * axtitlefont + 3)
        xax.SetTitleSize(axtitlesize)
        xax.SetTitleOffset(1.2)
    ### Y-axis layout
    if not logy:
        print(hist.GetMaximum())
        hist.SetMaximum(hist.GetMaximum() * 1.2)
        print(hist.GetMaximum())
        hist.SetMinimum(0.)
    else:
        c1.SetLogy()
        hist.SetMaximum(hist.GetMaximum() * 10)
        hist.SetMinimum(hist.GetMaximum() / 1e7)
    yax = hist.GetYaxis()
    yax.SetMaxDigits(3)
    yax.SetNdivisions(8, 4, 0, ROOT.kTRUE)
    yax.SetLabelFont(10 * yaxlabelfont + 3)
    yax.SetLabelSize(yaxlabelsize)
    if yaxtitle is not None:
        yax.SetTitle(yaxtitle)
        yax.SetTitleFont(10 * axtitlefont + 3)
        yax.SetTitleSize(axtitlesize)
        yax.SetTitleOffset(1.5)
    hist.Draw(drawoptions)

    # title
    # note: use of title is not recommended
    if title is not None:
        ttitle = ROOT.TLatex()
        ttitle.SetTextFont(10 * titlefont + 3)
        ttitle.SetTextSize(titlesize)
        titlebox = (0.15, 0.92)

    # draw all objects
    hist.Draw(drawoptions)
    ROOT.gPad.RedrawAxis()
    pt.drawLumi(pad1, extratext=extralumitext, lumitext=lumitext)
    if label is not None: leg.Draw("same")
    if title is not None: ttitle.DrawLatexNDC(titlebox[0], titlebox[1], title)

    # draw extra info
    tinfo = ROOT.TLatex()
    tinfo.SetTextFont(10 * infofont + 3)
    tinfo.SetTextSize(infosize)
    for i, info in enumerate(extrainfos):
        vspace = 0.07 * (float(infosize) / 20)
        tinfo.DrawLatexNDC(infoleft, infotop - (i + 1) * vspace, info)

    # write bin contents
    if writebincontent:
        bintext = ROOT.TLatex()
        bintext.SetTextAlign(21)
        bintext.SetTextFont(bincontentfont)
        bintext.SetTextSize(bincontentsize)
        for i in range(1, hist.GetNbinsX() + 1):
            xcoord = hist.GetXaxis().GetBinCenter(i)
            ycoord = hist.GetBinContent(i) + hist.GetBinErrorUp(i)
            printvalue = hist.GetBinContent(i)
            bintext.DrawLatex(xcoord, ycoord + 0.05,
                              bincontentfmt.format(printvalue))

    c1.SaveAs(figname.split('.')[0] + '.png')
    c1.SaveAs(figname.split('.')[0] + '.eps')
    c1.SaveAs(figname.split('.')[0] + '.pdf')
예제 #3
0
def plotdatavsmc(outfile,
                 datahist,
                 mchistlist,
                 mcsysthist=None,
                 mcstathist=None,
                 dostat=True,
                 signals=None,
                 datalabel=None,
                 labelmap=None,
                 colormap=None,
                 xaxtitle=None,
                 xaxinteger=False,
                 yaxtitle=None,
                 yaxlog=False,
                 yaxrange=None,
                 p2yaxtitle=None,
                 p2yaxrange=None,
                 p1legendncols=None,
                 p1legendbox=None,
                 extracmstext='',
                 lumi=None,
                 extrainfos=[],
                 infosize=None):
    ### make a (stacked) simulation vs. data plot
    # arguments:
    # - outfile is the output file where the figure will be saved
    # - datahist is the data histogram
    # - mchistlist is a list of simulated histograms
    #   (note that the title of each histogram will be used to determine its label!)
    #   (note that the title of each histogram will be used to determine its color!)
    # - mcsyshist is a histogram containing the total systematic variation on the simulation
    #   (note: the bin content of mcsyshist must be the absolute difference wrt nominal!)
    #   (note: if dostat is False, mcsysthist must be the total uncertainty, not only systematic!
    #          if dostat is True, mcsysthist is syst only;
    #	       stat error will be taken from mcstathist if it is defined or from mchistlist otherwise!)
    # - mcstathist is a histogram containing the total statistical variation on the simulation
    #   (note: see above!)
    # - signals = list of signal processes (will be put at the top of the plot)
    # - datalabel is a string that will be used as legend entry for datahist (default: Data)
    # - labelmap is a dict mapping strings (histogram titles) to legend entries
    # - colormap is a dict mapping strings (histogram titles) to colors
    # - xaxtitle is the x-axis title (default: no title)
    # - xaxinteger is a boolean whether to use only integer axis labels and ticks for the x-axis
    # - yaxtitle: y-axis title (default: no title)
    # - yaxlog is whether to make the y-axis log scale
    # - yaxrange: tuple of (min,max) for plotting range (default: automatic range)
    # - p2yaxtitle:  y-axis title of bottom pad (default: datalabel/Pred.)
    # - p2yaxrange: tuple of (min,max) for plotting range of bottom pad (default: (0,2))
    # - lumi is the luminosity value (float, in pb-1) that will be displayed
    # - extrainfos is a list of strings with extra info to display
    # - infosize: font size of extra info

    pt.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)

    ### parse arguments
    if datalabel is None: datalabel = "Data"
    # if no labelmap, use histogram titles
    if labelmap is None:
        labelmap = {}
        for hist in mchistlist:
            labelmap[hist.GetTitle()] = hist.GetTitle()
    # if no colormap, use default colors
    if colormap is None:
        clist = ([
            ROOT.kRed - 7, ROOT.kOrange, ROOT.kCyan - 7, ROOT.kYellow + 1,
            ROOT.kBlue - 10, ROOT.kBlue - 6, ROOT.kTeal - 5, ROOT.kMagenta - 7
        ])
        colormap = {}
        for i, hist in enumerate(mchistlist):
            colormap[hist.GetTitle()] = clist[i % len(clist)]
    # y-axis title and range for bottom pad
    if p2yaxtitle is None: p2yaxtitle = datalabel + ' / Pred.'
    if p2yaxrange is None: p2yaxrange = (0., 1.999)

    ### define global parameters for size and positioning
    cheight = 600  # height of canvas
    cwidth = 600  # width of canvas
    rfrac = 0.3  # fraction of ratio plot in canvas
    # fonts and sizes:
    labelfont = 4
    labelsize = 22
    axtitlefont = 4
    axtitlesize = 27
    infofont = 4
    if infosize is None: infosize = 20
    # title offset
    ytitleoffset = 1.5
    # temporary modification for an annoying comment on one figure:
    xtitleoffset = 3.5
    # margins:
    p1topmargin = 0.07
    p2bottommargin = 0.4
    leftmargin = 0.15
    rightmargin = 0.05
    # legend box
    if p1legendbox is None:
        p1legendbox = [
            leftmargin + 0.35, 1 - p1topmargin - 0.3, 1 - rightmargin - 0.03,
            1 - p1topmargin - 0.03
        ]
    if p1legendncols is None:
        p1legendncols = 2
    p2legendbox = [leftmargin + 0.03, 0.84, 1 - rightmargin - 0.03, 0.97]
    # extra info box parameters
    infoleft = leftmargin + 0.05
    infotop = 1 - p1topmargin - 0.1
    # marker properties for data
    markerstyle = 20
    markercolor = 1
    markersize = 0.75

    ### order mc histograms
    # order by sumofweights
    mchistlist = orderhistograms(mchistlist)
    # get signals to the back (i.e. on top of the plot)
    if signals is not None:
        for signal in signals[::-1]:
            sindex = findbytitle(mchistlist, signal)
            if (sindex > -1):
                indices = list(range(len(mchistlist)))
                indices.remove(sindex)
                indices = indices + [sindex]
                mchistlist = [mchistlist[i] for i in indices]

    ### operations on mc histograms
    mchistsum = mchistlist[0].Clone()
    mchistsum.Reset()
    mchiststack = ROOT.THStack("mchiststack", "")
    for hist in mchistlist:
        stackcol(hist, colormap.get(hist.GetTitle(), ROOT.kBlack))
        clip(hist)  # set negative bins to zero!
        mchistsum.Add(hist)
        mchiststack.Add(hist)

    ### calculate total mc error and set its histogram properties
    mcerror = mchistsum.Clone()
    mcstaterror = mchistsum.Clone()
    # in case of dostat=False: mcsysthist represents total variation to plot
    if not dostat:
        if mcsysthist is None:
            raise Exception('ERROR in histplotter.py: option dostat = False' +
                            ' is not compatible with mcsysthist = None')
        else:
            for i in range(1, mchistsum.GetNbinsX() + 1):
                mcerror.SetBinError(i, mcsysthist.GetBinContent(i))
    # in case of dostat=True: add stat errors and syst errors quadratically
    else:
        if mcstathist is not None:
            # take statistical variations from externally provided histograms
            for i in range(1, mchistsum.GetNbinsX() + 1):
                mcstaterror.SetBinError(i, mcstathist.GetBinContent(i))
                mcerror.SetBinError(i, mcstathist.GetBinContent(i))
        if mcsysthist is not None:
            for i in range(1, mchistsum.GetNbinsX() + 1):
                staterror = mcstaterror.GetBinError(i)
                systerror = mcsysthist.GetBinContent(i)
                mcerror.SetBinError(
                    i,
                    np.sqrt(np.power(staterror, 2) + np.power(systerror, 2)))
    mcerror.SetLineWidth(0)
    mcerror.SetMarkerStyle(0)
    mcerror.SetFillStyle(3254)
    mcerror.SetFillColor(ROOT.kBlack)

    ### calculate total and statistical mc error (scaled)
    scstaterror = mcerror.Clone()
    scerror = mcerror.Clone()
    for i in range(1, mcerror.GetNbinsX() + 1):
        scstaterror.SetBinContent(i, 1.)
        scerror.SetBinContent(i, 1.)
        if not mcerror.GetBinContent(i) == 0:
            scstaterror.SetBinError(
                i,
                mcstaterror.GetBinError(i) / mcstaterror.GetBinContent(i))
            scerror.SetBinError(
                i,
                mcerror.GetBinError(i) / mcerror.GetBinContent(i))
        else:
            scstaterror.SetBinError(i, 0.)
            scerror.SetBinError(i, 0.)
    # in case of dostat=False: plot only total scaled variation, same style as unscaled
    if not dostat:
        scstaterror.Reset()
        scerror.SetLineWidth(0)
        scerror.SetMarkerStyle(0)
        scerror.SetFillStyle(3254)
        scerror.SetFillColor(ROOT.kBlack)
    # in case of dostat=True: plot total and stat only scaled variation
    else:
        scstaterror.SetFillStyle(1001)
        scstaterror.SetFillColor(ROOT.kGray + 1)
        scstaterror.SetMarkerStyle(0)
        scerror.SetFillStyle(3254)
        scerror.SetFillColor(ROOT.kBlack)
        scerror.SetMarkerStyle(0)

    ### operations on data histogram
    datahist.SetMarkerStyle(markerstyle)
    datahist.SetMarkerColor(markercolor)
    datahist.SetMarkerSize(markersize)
    datahist.SetLineColor(markercolor)

    ### calculate data to mc ratio
    ratiograph = ROOT.TGraphAsymmErrors(datahist)
    for i in range(1, datahist.GetNbinsX() + 1):
        if not mchistsum.GetBinContent(i) == 0:
            ratiograph.GetY()[i - 1] *= 1. / mchistsum.GetBinContent(i)
            ratiograph.SetPointError(
                i - 1, 0, 0,
                datahist.GetBinErrorLow(i) / mchistsum.GetBinContent(i),
                datahist.GetBinErrorUp(i) / mchistsum.GetBinContent(i))
        # avoid drawing empty mc or data bins
        else:
            ratiograph.GetY()[i - 1] = 1e6
        if (datahist.GetBinContent(i) <= 0): ratiograph.GetY()[i - 1] += 1e6

    ### make legend for upper plot and add all histograms
    legend = ROOT.TLegend(p1legendbox[0], p1legendbox[1], p1legendbox[2],
                          p1legendbox[3])
    legend.SetNColumns(p1legendncols)
    legend.SetFillStyle(0)
    legend.AddEntry(datahist, datalabel, "pe1")
    for hist in mchistlist[::-1]:
        legend.AddEntry(hist, labelmap.get(hist.GetTitle(), '-'), "f")
    legend.AddEntry(mcerror, "Uncertainty", "f")

    ### make legend for lower plot and add all histograms
    # (note: will not be drawn if dostat = False)
    legend2 = ROOT.TLegend(p2legendbox[0], p2legendbox[1], p2legendbox[2],
                           p2legendbox[3])
    legend2.SetNColumns(3)
    legend2.SetFillStyle(0)
    legend2.AddEntry(scstaterror, "Stat. uncertainty", "f")

    ### make canvas and pads
    c1 = ROOT.TCanvas("c1", "c1")
    c1.SetCanvasSize(cwidth, cheight)
    pad1 = ROOT.TPad("pad1", "", 0., rfrac, 1., 1.)
    pad1.SetTopMargin(p1topmargin)
    pad1.SetBottomMargin(0.03)
    pad1.SetLeftMargin(leftmargin)
    pad1.SetRightMargin(rightmargin)
    pad1.SetFrameLineWidth(2)
    pad1.Draw()
    pad2 = ROOT.TPad("pad2", "", 0., 0., 1., rfrac)
    pad2.SetTopMargin(0.01)
    pad2.SetBottomMargin(p2bottommargin)
    pad2.SetLeftMargin(leftmargin)
    pad2.SetRightMargin(rightmargin)
    pad2.SetFrameLineWidth(2)
    pad2.Draw()

    ### make upper part of the plot
    pad1.cd()
    # determine range of pad
    if (yaxlog): pad1.SetLogy()
    (rangemin, rangemax) = getminmax(datahist, mcerror, yaxlog)
    if yaxrange is not None: (rangemin, rangemax) = yaxrange
    # temporary modification for an annoying comment on one figure:
    #rangemax = rangemax*1.2
    mcerror.SetMinimum(rangemin)
    mcerror.SetMaximum(rangemax)

    # X-axis layout
    xax = mcerror.GetXaxis()
    xax.SetNdivisions(10, 5, 0, ROOT.kTRUE)
    xax.SetLabelSize(0)
    # Y-axis layout
    yax = mcerror.GetYaxis()
    yax.SetMaxDigits(4)
    yax.SetNdivisions(10, 5, 0, ROOT.kTRUE)
    yax.SetLabelFont(10 * labelfont + 3)
    yax.SetLabelSize(labelsize)
    if yaxtitle is not None: yax.SetTitle(yaxtitle)
    yax.SetTitleFont(10 * axtitlefont + 3)
    yax.SetTitleSize(axtitlesize)
    yax.SetTitleOffset(ytitleoffset)

    # draw mcerror first to get range correct
    mcerror.Draw("e2")
    # now draw in correct order
    mchiststack.Draw("hist same")
    mcerror.Draw("e2 same")
    datahist.Draw("pe e1 x0 same")
    # (note: e1 draws error bars, x0 suppresses horizontal error bars)
    legend.Draw("same")
    # draw some extra info if needed
    ROOT.gPad.RedrawAxis()

    # draw header
    lumistr = ''
    if lumi is not None:
        lumistr = '{0:.3g}'.format(lumi / 1000.) + ' fb^{-1} (13 TeV)'
    pt.drawLumi(pad1, extratext=extracmstext, lumitext=lumistr)

    # draw extra info
    tinfo = ROOT.TLatex()
    tinfo.SetTextFont(10 * infofont + 3)
    tinfo.SetTextSize(infosize)
    for i, info in enumerate(extrainfos):
        vspace = 0.07 * (float(infosize) / 20)
        tinfo.DrawLatexNDC(infoleft, infotop - (i + 1) * vspace, info)

    ### make the lower part of the plot
    pad2.cd()
    # X-axis layout
    xax = scerror.GetXaxis()
    if xaxinteger:
        # option 1:
        #xax.SetOption("I")
        # does not work properly since it is not defined for TAxis, only TGAxis (?)
        # option 2:
        xax.SetNdivisions(xax.GetNbins() * 2, 0, 0, ROOT.kFALSE)
        for i in range(0, xax.GetNbins() + 1):
            xax.ChangeLabel(2 * i + 1, -1, -1, -1, -1, -1, " ")
        # works, but half-integer ticks are still there...
        # option 3:
        #xax.SetNdivisions(xax.GetNbins(),0,0,ROOT.kFALSE)
        #xax.CenterLabels()
        # does not work properly since labels are still half-integer but simply shifted
    else:
        xax.SetNdivisions(10, 5, 0, ROOT.kTRUE)
    xax.SetLabelSize(labelsize)
    xax.SetLabelFont(10 * labelfont + 3)
    if xaxtitle is not None: xax.SetTitle(xaxtitle)
    xax.SetTitleFont(10 * axtitlefont + 3)
    xax.SetTitleSize(axtitlesize)
    xax.SetTitleOffset(xtitleoffset)
    # Y-axis layout
    yax = scerror.GetYaxis()
    yax.SetRangeUser(p2yaxrange[0], p2yaxrange[1])
    yax.SetTitle(p2yaxtitle)
    yax.SetMaxDigits(3)
    yax.SetNdivisions(4, 5, 0)
    yax.SetLabelFont(10 * labelfont + 3)
    yax.SetLabelSize(labelsize)
    yax.SetTitleFont(10 * axtitlefont + 3)
    yax.SetTitleSize(axtitlesize)
    yax.SetTitleOffset(ytitleoffset)

    # draw objects
    scerror.Draw("e2")
    if dostat: scstaterror.Draw("e2 same")
    ratiograph.Draw("p e1 same")
    if dostat: legend2.Draw("same")
    ROOT.gPad.RedrawAxis()

    # make and draw unit ratio line
    xmax = datahist.GetXaxis().GetBinUpEdge(datahist.GetNbinsX())
    xmin = datahist.GetXaxis().GetBinLowEdge(1)
    line = ROOT.TLine(xmin, 1, xmax, 1)
    line.SetLineStyle(2)
    line.Draw("same")

    ### save the plot
    c1.SaveAs(outfile + '.png')
    c1.SaveAs(outfile + '.eps')
    c1.SaveAs(outfile + '.pdf')
예제 #4
0
def plotmultihistograms(histlist,
                        figname=None,
                        title=None,
                        xaxtitle=None,
                        yaxtitle=None,
                        normalize=False,
                        normalizefirst=False,
                        dolegend=True,
                        labellist=None,
                        colorlist=None,
                        logy=False,
                        ymaxlinfactor=1.8,
                        yminlogfactor=0.2,
                        ymaxlogfactor=100,
                        drawoptions='',
                        lumitext='',
                        extralumitext='',
                        doratio=False,
                        ratiorange=None,
                        ylims=None,
                        yminzero=False):
    ### plot multiple overlaying histograms (e.g. for shape comparison)
    # note: the ratio plot will show ratios w.r.t. the first histogram in the list!
    # arguments:
    # - histlist, colorlist, labellist: lists of TH1, ROOT colors and labels respectively
    # - figname: name of the figure to save (if None, do not save but return plot dictionary)
    # - title, xaxtitle, yaxtitle, figname: self-explanatory
    # - normalize: boolean whether to put all histogram integrals to unit surface area
    # - normalizefirst: boolean whether to normalize first histogram in list and scale others
    #                   by the same factor (do not use together with normalize)
    # - dolegend: boolean whether to make a legend (histogram title is used if no labellist)
    # - logy: boolean whether to make y-axis logarithmic
    # - ymaxlinfactor: factor by which to multiply maximum y value (for linear y-axis)
    # - yminlogfactor and ymaxlogfactor: same as above but for log scale
    # - drawoptions: string passed to TH1.Draw
    #   see https://root.cern/doc/master/classTHistPainter.html for a full list of options
    # - lumitext and extralumitext: luminosity value and extra text
    # - ratiorange: a tuple of (ylow,yhigh) for the ratio pad, default (0,2)
    # - ylims: a tuple of (ylow,yhigh) for the upper pad
    # - yminzero: whether to clip minimum y to zero.

    pt.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)

    ### parse arguments
    if colorlist is None:
        colorlist = ([
            ROOT.kAzure - 4, ROOT.kAzure + 6, ROOT.kViolet, ROOT.kMagenta - 9,
            ROOT.kRed, ROOT.kGreen + 1, ROOT.kGreen - 1
        ])
    if (len(histlist) > len(colorlist)):
        raise Exception('ERROR in plotmultihistograms:' +
                        ' histogram list is longer than color list')
    if (labellist is not None and len(labellist) != len(histlist)):
        raise Exception(
            'ERROR in plotmultihistograms:' +
            ' length of label list does not agree with histogram list')

    ### define global parameters for size and positioning
    cheight = 600  # height of canvas
    cwidth = 600  # width of canvas
    rfrac = 0.33  # fraction of bottom plot showing the ratio
    if not doratio: rfrac = 0
    # fonts and sizes:
    labelfont = 4
    labelsize = 22
    axtitlefont = 4
    axtitlesize = 26
    infofont = 4
    infosize = 26
    legendfont = 4
    # margins and title offsets
    ytitleoffset = 1.5
    p1topmargin = 0.05
    p1bottommargin = 0.15
    xtitleoffset = 1.
    if doratio:
        p1topmargin = 0.07
        p1bottommargin = 0.03
        xtitleoffset = 3.5
    p2topmargin = 0.01
    p2bottommargin = 0.4
    leftmargin = 0.15
    rightmargin = 0.05
    # legend box
    pentryheight = 0.05
    if doratio: pentryheight = 0.07
    nentries = 1 + len(histlist)
    if nentries > 3: pentryheight = pentryheight * 0.8
    plegendbox = ([
        leftmargin + 0.3, 1 - p1topmargin - 0.03 - pentryheight * nentries,
        1 - rightmargin - 0.03, 1 - p1topmargin - 0.03
    ])

    ### normalization and style operations on histograms
    scale = 1
    if (normalizefirst): scale = histlist[0].Integral("width")
    for i, hist in enumerate(histlist):
        hist.SetLineWidth(3)
        hist.SetLineColor(colorlist[i])
        hist.SetMarkerSize(0)
        if normalize: scale = hist.Integral("width")
        for j in range(0, hist.GetNbinsX() + 2):
            hist.SetBinContent(j, hist.GetBinContent(j) / scale)
            hist.SetBinError(j, hist.GetBinError(j) / scale)

    ### make ratio histograms
    ratiohistlist = []
    for hist in histlist:
        rhist = hist.Clone()
        for j in range(0, rhist.GetNbinsX() + 2):
            scale = histlist[0].GetBinContent(j)
            if scale < 1e-12:
                rhist.SetBinContent(j, 0)
                rhist.SetBinError(j, 10)
            else:
                rhist.SetBinContent(j, rhist.GetBinContent(j) / scale)
                rhist.SetBinError(j, rhist.GetBinError(j) / scale)
        ratiohistlist.append(rhist)

    ### make legend for upper plot and add all histograms
    legend = ROOT.TLegend(plegendbox[0], plegendbox[1], plegendbox[2],
                          plegendbox[3])
    legend.SetNColumns(1)
    legend.SetFillColor(ROOT.kWhite)
    legend.SetTextFont(10 * legendfont + 3)
    legend.SetBorderSize(1)
    for i, hist in enumerate(histlist):
        label = hist.GetTitle()
        if labellist is not None: label = labellist[i]
        legend.AddEntry(hist, label, "l")

    ### make canvas and pads
    c1 = ROOT.TCanvas("c1", "c1")
    c1.SetCanvasSize(cwidth, cheight)
    pad1 = ROOT.TPad("pad1", "", 0., rfrac, 1., 1.)
    pad1.SetTopMargin(p1topmargin)
    pad1.SetBottomMargin(p1bottommargin)
    pad1.SetLeftMargin(leftmargin)
    pad1.SetRightMargin(rightmargin)
    pad1.SetTicks(1, 1)
    pad1.SetFrameLineWidth(2)
    pad1.SetGrid()
    pad1.Draw()
    if doratio:
        pad2 = ROOT.TPad("pad2", "", 0., 0., 1., rfrac)
        pad2.SetTopMargin(p2topmargin)
        pad2.SetBottomMargin(p2bottommargin)
        pad2.SetLeftMargin(leftmargin)
        pad2.SetRightMargin(rightmargin)
        pad2.SetTicks(1, 1)
        pad2.SetFrameLineWidth(2)
        pad2.SetGrid()
        pad2.Draw()

    ### make upper part of the plot
    pad1.cd()

    # get x-limits (for later use)
    nbins = histlist[0].GetNbinsX()
    xlims = (histlist[0].GetBinLowEdge(1),
             histlist[0].GetBinLowEdge(nbins) + histlist[0].GetBinWidth(nbins))
    # get and set y-limits
    (totmin, totmax) = ht.getminmax(histlist)
    # in case of log scale
    if logy:
        pad1.SetLogy()
        if ylims is None:
            ylims = (totmin * yminlogfactor, totmax * ymaxlogfactor)
    # in case of lin scale
    else:
        if ylims is None: ylims = (0., totmax * ymaxlinfactor)
    if yminzero and ylims[0] < 0: ylims = (0., ylims[1])
    histlist[0].SetMaximum(ylims[1])
    histlist[0].SetMinimum(ylims[0])

    # X-axis layout
    xax = histlist[0].GetXaxis()
    xax.SetNdivisions(5, 4, 0, ROOT.kTRUE)
    if doratio:
        xax.SetLabelSize(0)
    else:
        xax.SetLabelSize(labelsize)
        xax.SetLabelFont(10 * labelfont + 3)
        if xaxtitle is not None:
            xax.SetTitle(xaxtitle)
            xax.SetTitleFont(10 * axtitlefont + 3)
            xax.SetTitleSize(axtitlesize)
            xax.SetTitleOffset(xtitleoffset)
    # Y-axis layout
    yax = histlist[0].GetYaxis()
    yax.SetMaxDigits(3)
    yax.SetNdivisions(8, 4, 0, ROOT.kTRUE)
    yax.SetLabelFont(10 * labelfont + 3)
    yax.SetLabelSize(labelsize)
    if yaxtitle is not None:
        yax.SetTitle(yaxtitle)
        yax.SetTitleFont(10 * axtitlefont + 3)
        yax.SetTitleSize(axtitlesize)
        yax.SetTitleOffset(ytitleoffset)

    # histograms
    histlist[0].Draw(drawoptions)
    for hist in histlist[1:]:
        hist.Draw("same " + drawoptions)
    if dolegend:
        legend.Draw("same")
    ROOT.gPad.RedrawAxis()

    # draw header
    pt.drawLumi(pad1, extratext=extralumitext, lumitext=lumitext, rfrac=rfrac)

    if not doratio:
        # return or save the plot
        if figname is None:
            plotobject = {
                'canvas': c1,
                'pads': (pad1),
                'xlims': xlims,
                'ylims': ylims,
                'legend': legend
            }
            return plotobject
        else:
            c1.SaveAs(figname.replace('.png', '') + '.png')
            c1.SaveAs(figname.replace('.png', '') + '.eps')
            c1.SaveAs(figname.replace('.png', '') + '.pdf')
            return None

    ### make the lower part of the plot
    pad2.cd()
    xax = ratiohistlist[0].GetXaxis()
    xax.SetNdivisions(5, 4, 0, ROOT.kTRUE)
    xax.SetLabelSize(labelsize)
    xax.SetLabelFont(10 * labelfont + 3)
    if xaxtitle is not None:
        xax.SetTitle(xaxtitle)
        xax.SetTitleFont(10 * axtitlefont + 3)
        xax.SetTitleSize(axtitlesize)
        xax.SetTitleOffset(xtitleoffset)
    # Y-axis layout
    yax = ratiohistlist[0].GetYaxis()
    if ratiorange == None: ratiorange = (0, 1.999)
    yax.SetRangeUser(ratiorange[0], ratiorange[1])
    yax.SetMaxDigits(3)
    yax.SetNdivisions(4, 5, 0, ROOT.kTRUE)
    yax.SetLabelFont(10 * labelfont + 3)
    yax.SetLabelSize(labelsize)
    yax.SetTitle('Ratio')
    yax.SetTitleFont(10 * axtitlefont + 3)
    yax.SetTitleSize(axtitlesize)
    yax.SetTitleOffset(ytitleoffset)

    # draw objects
    ratiohistlist[0].Draw(drawoptions)
    for hist in ratiohistlist[1:]:
        hist.Draw("same " + drawoptions)
    ROOT.gPad.RedrawAxis()

    # make and draw unit ratio line
    xmax = histlist[0].GetXaxis().GetBinUpEdge(histlist[0].GetNbinsX())
    xmin = histlist[0].GetXaxis().GetBinLowEdge(1)
    line = ROOT.TLine(xmin, 1, xmax, 1)
    line.SetLineStyle(2)
    line.Draw("same")

    # return or save the plot
    if figname is None:
        plotobject = {
            'canvas': c1,
            'pads': (pad1, pad2),
            'xlims': xlims,
            'ylims': ylims,
            'legend': legend
        }
        return plotobject
    else:
        c1.SaveAs(figname.replace('.png', '') + '.png')
        c1.SaveAs(figname.replace('.png', '') + '.eps')
        c1.SaveAs(figname.replace('.png', '') + '.pdf')
        return None
예제 #5
0
def multi_contour_plot(histlist,
                       outfilepath,
                       outfmts=['.png'],
                       contours=None,
                       labellist=None,
                       colormaplist=None,
                       title=None,
                       titlesize=None,
                       xaxtitle=None,
                       xaxtitlesize=None,
                       xaxtitleoffset=None,
                       yaxtitle=None,
                       yaxtitlesize=None,
                       yaxtitleoffset=None,
                       extracmstext='',
                       lumitext='',
                       extrainfos=[],
                       infosize=None,
                       infoleft=None,
                       infotop=None):
    ### draw multiple 2D contours in a figure
    # input arguments:
    # - histlist: list of TH2D objects
    # - outfilepath: path where to store the figure
    # - outfmts: list of output formats (.png, .pdf, .eps and possibly others are allowed)
    # - contours: either a list with countour levels to draw,
    #             or an integer number of (equally-spaced) contour levels (default 5).
    #		  note: contour levels are the same for each histogram
    #                   and determined based on the first histogram in histlist;
    #                   possibly to be made more flexible later!
    # - labellist: list of legend entries
    # - colorlist: list of ROOT color maps
    #              note that the maps must be in string format and not contain 'ROOT.',
    #              so for example 'kBird', 'kViridis', etc.
    # notes:
    # - the axes will be drawn based on the first histogram in histlist
    #   (though in most foreseen use cases they would all have the same axis ranges anyway)

    pt.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)

    # set global properties
    cheight = 500  # height of canvas
    cwidth = 700  # width of canvas
    titlefont = 4
    if titlesize is None: titlesize = 22
    xaxtitlefont = 4
    if xaxtitlesize is None: xaxtitlesize = 22
    yaxtitlefont = 4
    if yaxtitlesize is None: yaxtitlesize = 22
    infofont = 4
    if infosize is None: infosize = 15
    # title offset
    if yaxtitleoffset is None: ytitleoffset = 1.5
    if xaxtitleoffset is None: xtitleoffset = 1.5
    # margins
    topmargin = 0.15
    bottommargin = 0.15
    leftmargin = 0.15
    rightmargin = 0.15
    # legend box
    legendbox = [
        leftmargin + 0.45, 1 - topmargin - 0.2, 1 - rightmargin - 0.03,
        1 - topmargin - 0.03
    ]
    # extra info box parameters
    if infoleft is None: infoleft = leftmargin + 0.05
    if infotop is None: infotop = 1 - topmargin - 0.1
    # get a reference histogram for axes
    hist = histlist[0]
    # get axis properties
    xmin = hist.GetXaxis().GetXmin()
    xmax = hist.GetXaxis().GetXmax()
    ymin = hist.GetYaxis().GetXmin()
    ymax = hist.GetYaxis().GetXmax()
    zmin = hist.GetMinimum()
    zmax = hist.GetMaximum()

    # parse color map list argument
    # note: using multiple color maps in one canvas is not easy in ROOT,
    #       and requires the magic below.
    if colormaplist is None: colormaplist = ['kViridis', 'kSolar']
    if len(histlist) > len(colormaplist):
        raise Exception(
            'ERROR: this many histograms do not support automatic colors.' +
            ' Please provide a color map list explicitly.')
    colormaplist = colormaplist[:len(histlist)]
    texeclist = []
    for i, cmap in enumerate(colormaplist):
        texeclist.append(
            ROOT.TExec('ex{}'.format(i),
                       'gStyle->SetPalette({});'.format(cmap)))

    # parse labellist argument
    dolegend = True
    if labellist is None:
        labellist = [''] * len(histlist)
        dolegend = False

    # set the line widths and colors for all histograms
    # note: line colors are not used for drawing (color palettes are used instead),
    #       but they are displayed in the legend.
    # note: again, a bit of ROOT magic seems to be needed to simply extract
    #       a color from a color map...
    for ihist, cmap in zip(histlist, colormaplist):
        ihist.SetLineWidth(2)
        cmapnb = getattr(ROOT, cmap)
        ROOT.gStyle.SetPalette(cmapnb)
        color = ROOT.TColor.GetPalette().At(128)
        ihist.SetLineColor(color)

    # make legend and add all histograms
    legend = ROOT.TLegend(legendbox[0], legendbox[1], legendbox[2],
                          legendbox[3])
    legend.SetFillStyle(1001)
    legend.SetFillColor(ROOT.kWhite)
    for ihist, ilabel in zip(histlist, labellist):
        legend.AddEntry(ihist, ilabel, "l")

    # parse contours argument
    if contours is None: contours = 5
    if isinstance(contours, int):
        contours = list(np.linspace(zmin, zmax, contours, endpoint=False))
    ncontours = len(contours)
    contours = array.array('d', contours)

    # set the contours for all histograms
    for ihist in histlist:
        ihist.SetContour(ncontours, contours)

    # create canvas
    c1 = ROOT.TCanvas("c1", "c1")
    c1.SetCanvasSize(cwidth, cheight)
    c1.SetTopMargin(topmargin)
    c1.SetBottomMargin(bottommargin)
    c1.SetLeftMargin(leftmargin)
    c1.SetRightMargin(rightmargin)

    # set title and label properties
    if xaxtitle is None: xaxtitle = ''
    hist.GetXaxis().SetTitle(xaxtitle)
    hist.GetXaxis().SetTitleOffset(xtitleoffset)
    hist.GetXaxis().SetTitleFont(xaxtitlefont * 10 + 3)
    hist.GetXaxis().SetTitleSize(xaxtitlesize)
    if yaxtitle is None: yaxtitle = ''
    hist.GetYaxis().SetTitle(yaxtitle)
    hist.GetYaxis().SetTitleOffset(ytitleoffset)
    hist.GetYaxis().SetTitleFont(yaxtitlefont * 10 + 3)
    hist.GetYaxis().SetTitleSize(yaxtitlesize)

    # set title
    ttitle = ROOT.TLatex()
    ttitle.SetTextFont(titlefont * 10 + 3)
    ttitle.SetTextSize(titlesize)

    # draw contours
    hist.Draw()
    for i, ihist in enumerate(histlist):
        texeclist[i].Draw('same')
        ihist.Draw('cont1 same')
        c1.Update()
    # redraw first histogram to get the axes
    texeclist[0].Draw('same')
    hist.Draw('cont1 same')
    c1.Update()

    # draw diagonal line
    bisect = ROOT.TLine(xmin, ymin, xmax, ymax)
    bisect.SetLineWidth(1)
    bisect.SetLineColor(ROOT.kRed)
    bisect.SetLineStyle(9)
    bisect.Draw('same')

    # draw header
    if dolegend: legend.Draw('same')
    ttitle.DrawLatexNDC(leftmargin, 0.9, histtitle)
    pt.drawLumi(c1,
                extratext=extracmstext,
                cmstext_size_factor=0.3,
                lumitext=lumitext)

    # draw extra info
    tinfo = ROOT.TLatex()
    tinfo.SetTextFont(10 * infofont + 3)
    tinfo.SetTextSize(infosize)
    for i, info in enumerate(extrainfos):
        vspace = 0.07 * (float(infosize) / 20)
        tinfo.DrawLatexNDC(infoleft, infotop - (i + 1) * vspace, info)

    # save the plot
    c1.Update()
    outfilepath = os.path.splitext(outfilepath)[0]
    for outfmt in outfmts:
        c1.SaveAs(outfilepath + outfmt)

    # set gStyle back to default after messing with it
    ROOT.gStyle.SetPalette(ROOT.kBird)
예제 #6
0
def plot2dhistogram(hist,
                    outfilepath,
                    histtitle='',
                    logx=False,
                    logy=False,
                    drawoptions='colztexte'):

    tools.setTDRstyle()
    ROOT.gROOT.SetBatch(ROOT.kTRUE)

    # set global properties
    cheight = 500  # height of canvas
    cwidth = 700  # width of canvas
    titlefont = 4
    titlesize = 22
    axtitlefont = 4
    axtitlesize = 22
    # title offset
    ytitleoffset = 1.5
    xtitleoffset = 1.5
    # margins:
    topmargin = 0.15
    bottommargin = 0.15
    leftmargin = 0.15
    rightmargin = 0.15
    xmin = hist.GetXaxis().GetXmin()
    xmax = hist.GetXaxis().GetXmax()
    ymin = hist.GetYaxis().GetXmin()
    ymax = hist.GetYaxis().GetXmax()
    zmin = hist.GetMinimum()
    zmax = hist.GetMaximum()

    # create canvas
    c1 = ROOT.TCanvas("c1", "c1")
    c1.SetCanvasSize(cwidth, cheight)
    c1.SetTopMargin(topmargin)
    c1.SetBottomMargin(bottommargin)
    c1.SetLeftMargin(leftmargin)
    c1.SetRightMargin(rightmargin)

    # create copy of histogram with adapted outerflow bins if needed
    if (logx or logy):
        hist = getsmallouterflowhist(hist)
        hist.SetMinimum(zmin)
        hist.SetMaximum(zmax)

    # set title and label properties
    hist.GetXaxis().SetTitleOffset(xtitleoffset)
    hist.GetXaxis().SetTitleFont(axtitlefont * 10 + 3)
    hist.GetXaxis().SetTitleSize(axtitlesize)
    hist.GetYaxis().SetTitleOffset(ytitleoffset)
    hist.GetYaxis().SetTitleFont(axtitlefont * 10 + 3)
    hist.GetYaxis().SetTitleSize(axtitlesize)

    if logx:
        c1.SetLogx()
        hist.GetXaxis().SetMoreLogLabels()

    if logy:
        c1.SetLogy()
        hist.GetYaxis().SetMoreLogLabels()

    # set title
    ttitle = ROOT.TLatex()
    ttitle.SetTextFont(titlefont * 10 + 3)
    ttitle.SetTextSize(titlesize)

    # draw
    hist.Draw(drawoptions)
    ttitle.DrawLatexNDC(leftmargin, 0.9, histtitle)

    # save the plot
    c1.Update()
    c1.SaveAs(outfilepath)