Пример #1
0
def plot_distributions(input_file, distribution_plots):
  with root_open(input_file) as file:
    for name,histo_configs in distribution_plots.items():
      canvas = Canvas(700,700)
      legend = Legend(len(histo_configs), leftmargin=0.4, margin=0.3)
      histograms = []
      for histo_config in histo_configs:
        histogram = file.Get(histo_config.name)
        if len(histo_config.range)==2:
          histogram.SetAxisRange(histo_config.range[0], histo_config.range[1], 'X')
        histogram.color = histo_config.color
        histogram.markerstyle = histo_config.marker
        legend.AddEntry(histogram, label=histo_config.legend, style='ep')
        histograms.append(histogram)
      xmin, xmax, ymin, ymax = get_limits(histograms, logy=False)
      option = ''
      for histogram in histograms:
        histogram.SetAxisRange(ymin,ymax,'Y')
        histogram.Draw(option)
        option += ' same'
      legend.Draw()
      canvas.Print('plots/{}.png'.format(name))
Пример #2
0
h1.linewidth = 0

h2.fillstyle = 'solid'
h2.fillcolor = 'red'
h2.linecolor = 'red'
h2.linewidth = 0

stack = HistStack()
stack.Add(h1)
stack.Add(h2)

# plot with ROOT
canvas = Canvas(width=700, height=500)

# try setting logy=True and uncommenting the two lines below
xmin, xmax, ymin, ymax = get_limits([stack, h3], logy=False)
stack.SetMaximum(ymax)
#stack.SetMinimum(ymin)
#canvas.SetLogy()

stack.Draw('HIST E1 X0')
h3.Draw('SAME E1 X0')
stack.xaxis.SetTitle('Mass')
stack.yaxis.SetTitle('Events')
# set the number of expected legend entries
legend = Legend(3, leftmargin=0.45, margin=0.3)
legend.AddEntry(h1, style='F')
legend.AddEntry(h2, style='F')
legend.AddEntry(h3, style='LEP')
legend.Draw()
label = ROOT.TText(0.3, 0.8, 'ROOT')
Пример #3
0
    def draw_to_canvas(self):
        """
        Draw this figure to a canvas, which is then returned.
        """
        if len(self._plottables) == 0:
            raise IndexError("No plottables defined")
        c = Canvas(width=self.style.canvasWidth,
                   height=self.style.canvasHeight,
                   size_includes_decorations=True)
        if self.legend.position == 'seperate':
            legend_width = .2
            pad_legend = Pad(1 - legend_width, 0, 1., 1., name="legend")
            pad_legend.SetLeftMargin(0.0)
            pad_legend.SetFillStyle(0)  # make this pad transparent
            pad_legend.Draw()
        else:
            legend_width = 0
        pad_plot = Pad(
            0.,
            0.,
            1 - legend_width,
            1.,
            name="plot",
        )
        pad_plot.SetMargin(*self.style.plot_margins)
        pad_plot.Draw()
        pad_plot.cd()

        # awkward hack around a bug in get limits where everything fails if one plottable is shitty...
        xmin, xmax, ymin, ymax = None, None, None, None
        for pdic in self._plottables:
            try:
                limits = get_limits(pdic['p'],
                                    logx=self.plot.logx,
                                    logy=self.plot.logy)
                # Beware: Python 2 evaluates min/max of None in an undefined way with no error! Wow...
                xmin = min([xmin, limits[0]
                            ]) if xmin is not None else limits[0]
                xmax = max([xmax, limits[1]
                            ]) if xmax is not None else limits[1]
                ymin = min([ymin, limits[2]
                            ]) if ymin is not None else limits[2]
                ymax = max([ymax, limits[3]
                            ]) if ymax is not None else limits[3]
            except (TypeError, ValueError):
                # some plottables do not work with this rootpy function (eg. graph without points, tf1)
                # TODO: should be fixed upstream
                pass
        # overwrite these ranges if defaults are given
        if self.plot.xmin is not None:
            xmin = self.plot.xmin
        if self.plot.xmax is not None:
            xmax = self.plot.xmax
        if self.plot.ymax is not None:
            ymax = self.plot.ymax
        if self.plot.ymin is not None:
            ymin = self.plot.ymin

        if not all([val is not None for val in [xmin, xmax, ymin, ymax]]):
            raise TypeError(
                "unable to determine plot axes ranges from the given plottables"
            )

        colors = get_color_generator(self.plot.palette,
                                     self.plot.palette_ncolors)

        # draw an empty frame within the given ranges;
        frame_from_plottable = [
            p for p in self._plottables if p.get('use_as_frame')
        ]
        if len(frame_from_plottable) > 0:
            frame = frame_from_plottable[0]['p'].Clone('__frame')
            frame.Reset()
            frame.SetStats(0)
            frame.xaxis.SetRangeUser(xmin, xmax)
            frame.yaxis.SetRangeUser(ymin, ymax)
            frame.GetXaxis().SetTitle(self.xtitle)
            frame.GetYaxis().SetTitle(self.ytitle)
            self._theme_plottable(frame)
            frame.Draw()
        else:
            frame = Graph()
            frame.SetName("__frame")
            # add a silly point in order to have root draw this frame...
            frame.SetPoint(0, 0, 0)
            frame.GetXaxis().SetLimits(xmin, xmax)
            frame.GetYaxis().SetLimits(ymin, ymax)
            frame.SetMinimum(ymin)
            frame.SetMaximum(ymax)
            frame.GetXaxis().SetTitle(self.xtitle)
            frame.GetYaxis().SetTitle(self.ytitle)
            self._theme_plottable(frame)
            # Draw this frame: 'A' should draw the axis, but does not work if nothing else is drawn.
            # L would draw a line between the points but is seems to do nothing if only one point is present
            # P would also draw that silly point but we don't want that!
            frame.Draw("AL")

        xtick_length = frame.GetXaxis().GetTickLength()
        ytick_length = frame.GetYaxis().GetTickLength()

        for i, pdic in enumerate(self._plottables):
            obj = pdic['p']
            if isinstance(obj, ROOT.TLegendEntry):
                _root_color = Color(pdic['color'])
                _root_markerstyle = MarkerStyle(pdic['markerstyle'])
                obj.SetMarkerStyle(_root_markerstyle('root'))
                obj.SetMarkerColor(_root_color('root'))

            elif isinstance(obj, (ROOT.TH1, ROOT.TGraph, ROOT.TF1)):
                self._theme_plottable(obj)
                obj.SetMarkerStyle(pdic.get('markerstyle', 'circle'))
                if pdic.get('color', None):
                    obj.color = pdic['color']
                else:
                    try:
                        color = next(colors)
                    except StopIteration:
                        log.warning("Ran out of colors; defaulting to black")
                        color = 1
                    obj.color = color
                xaxis = obj.GetXaxis()
                yaxis = obj.GetYaxis()

                # Set the title to the given title:
                obj.title = self.title

                # the xaxis depends on the type of the plottable :P
                if isinstance(obj, ROOT.TGraph):
                    # SetLimit on a TH1 is simply messing up the
                    # lables of the axis to screw over the user, presumably...
                    xaxis.SetLimits(xmin, xmax)
                    yaxis.SetLimits(ymin, ymax)  # for unbinned data
                    # 'P' plots the current marker, 'L' would connect the dots with a simple line
                    # see: https://root.cern.ch/doc/master/classTGraphPainter.html for more draw options
                    drawoption = 'Psame'
                elif isinstance(obj, ROOT.TH1):
                    obj.SetStats(0)
                    xaxis.SetRangeUser(xmin, xmax)
                    yaxis.SetRangeUser(ymin, ymax)
                    drawoption = 'same'
                elif isinstance(obj, ROOT.TF1):
                    # xaxis.SetLimits(xmin, xmax)
                    # yaxis.SetLimits(ymin, ymax)  # for unbinned data
                    drawoption = 'same'
                obj.Draw(drawoption)
            # Its ok if obj is non; then we just add it to the legend.
            else:
                raise TypeError("Un-plottable type given.")
        pad_plot.SetTicks()
        pad_plot.SetLogx(self.plot.logx)
        pad_plot.SetLogy(self.plot.logy)
        pad_plot.SetGridx(self.plot.gridx)
        pad_plot.SetGridy(self.plot.gridy)

        # do we have legend titles?
        if any([pdic.get('legend_title') for pdic in self._plottables]):
            leg = self._create_legend()
            longest_label = 0
            for pdic in self._plottables:
                if not pdic.get('legend_title', False):
                    continue
                leg.AddEntry(pdic['p'], pdic['legend_title'])
                if len(pdic['legend_title']) > longest_label:
                    longest_label = len(pdic['legend_title'])

            # Set the legend position
            # vertical:
            if self.legend.position.startswith('t'):
                leg_hight = leg.y2 - leg.y1
                leg.y2 = 1 - pad_plot.GetTopMargin() - ytick_length
                leg.y1 = leg.y2 - leg_hight
            elif self.legend.position.startswith('b'):
                leg_hight = leg.y2 - leg.y1
                leg.y1 = pad_plot.GetBottomMargin() + ytick_length
                leg.y2 = leg.y1 + leg_hight
            # horizontal:
            if self.legend.position[1:].startswith('l'):
                leg_width = 0.3
                leg.x1 = pad_plot.GetLeftMargin() + xtick_length
                leg.x2 = leg.x1 + leg_width
            elif self.legend.position[1:].startswith('r'):
                leg_width = 0.3
                leg.x2 = 1 - pad_plot.GetRightMargin() - xtick_length
                leg.x1 = leg.x2 - leg_width
            if self.legend.position == 'seperate':
                with pad_legend:
                    leg.Draw()
            else:
                leg.Draw()
        if self.plot.logx:
            pad_plot.SetLogx(True)
        if self.plot.logy:
            pad_plot.SetLogy(True)
        pad_plot.Update(
        )  # needed sometimes with import of canvas. maybe because other "plot" pads exist...
        return c
Пример #4
0
def stack(x, *args, **kwargs):

    ## parse arguments
    _data = kwargs.pop('data', None)
    _bkgs = kwargs.pop('bkgs', None)
    _sigs = kwargs.pop('sigs', None)
    _treename = kwargs.pop('treename', None)
    _datasearchpath = kwargs.pop('datasearchpath', None)
    _datadrivensearchpath = kwargs.pop('datadrivensearchpath', None)
    _bkgsearchpath = kwargs.pop('bkgsearchpath', None)
    _sigsearchpath = kwargs.pop('sigsearchpath', None)
    _lumi = kwargs.pop('lumi', None)

    global data
    global bkgs
    global sigs
    global treename
    global datasearchpath
    global datadrivensearchpath
    global bkgsearchpath
    global sigsearchpath
    global lumi

    data = _data or data
    bkgs = _bkgs or bkgs
    sigs = _sigs or sigs
    treename = _treename or treename
    datasearchpath = _datasearchpath or datasearchpath
    datadrivensearchpath = _datadrivensearchpath or datadrivensearchpath
    bkgsearchpath = _bkgsearchpath or bkgsearchpath
    sigsearchpath = _sigsearchpath or sigsearchpath
    if _lumi:
        lumi = float(_lumi)

    xtitle = kwargs.pop('xtitle', '')
    ytitle = kwargs.pop('ytitle', '')
    logx = bool(kwargs.pop('logx', False))
    logy = bool(kwargs.pop('logy', False))
    blind = kwargs.pop('blind', None)
    has_blinded_data = False

    ## save stuff to bookkeep and return
    stuff = dict()
    stuff['x'] = x

    ## get data histogram
    h_data = None
    if data:
        sp = datasearchpath  # HACK: just data to True!
        newx = '%s::%s::%s' % (sp, treename, x)
        h_data = ipyhep.tree.project(newx, *args, **kwargs)
        if h_data:
            stuff['h_data'] = h_data

    ## blind the data?
    if h_data and not blind is None:
        if isinstance(blind, tuple):
            blind1, blind2 = blind
            nbins = h_data.GetNbinsX()
            for i_bin in xrange(1, nbins +
                                2):  # skip underflow (but not overflow)
                xval1 = h_data.GetXaxis().GetBinLowEdge(i_bin)
                xval2 = h_data.GetXaxis().GetBinUpEdge(i_bin)
                if xval1 >= blind1 and xval2 <= blind2:
                    h_data.SetBinContent(i_bin, 0.0)
                    h_data.SetBinError(i_bin, 0.0)
                    has_blinded_data = True
        else:
            nbins = h_data.GetNbinsX()
            for i_bin in xrange(1, nbins +
                                2):  # skip underflow (but not overflow)
                xval = h_data.GetXaxis().GetBinLowEdge(i_bin)
                if xval >= blind:
                    h_data.SetBinContent(i_bin, 0.0)
                    h_data.SetBinError(i_bin, 0.0)
                    has_blinded_data = True

    ## get background histograms
    h_bkgs = list()
    n_bkgs = list()
    if bkgs:
        for bkg in bkgs:
            if isinstance(bkg, list):
                h_subtotal = None
                for dsid in bkg:
                    assert isinstance(dsid, str)
                    h_bkg = None
                    if dsid.isdigit():
                        ## mc backgrounds
                        sp = bkgsearchpath % int(dsid)
                        newx = '%s::%s::%s' % (sp, treename, x)
                        h_bkg = ipyhep.tree.project(newx, *args, **kwargs)
                    else:
                        ## data-driven backgrounds
                        assert dsid == 'fakes' or dsid == 'efakes'
                        sp = datadrivensearchpath % dsid
                        newx = '%s::%s::%s' % (sp, treename, x)
                        h_bkg = ipyhep.tree.project(newx, *args, **kwargs)
                    if h_bkg:
                        if h_subtotal:
                            h_subtotal.Add(h_bkg)
                        else:
                            h_subtotal = h_bkg.Clone()
                if h_subtotal:
                    h_bkgs.append(h_subtotal)
                    dsid = bkg[0]
                    n_bkgs.append(dsid)
            else:
                dsid = bkg
                assert isinstance(dsid, str)
                h_bkg = None
                if dsid.isdigit():
                    ## mc backgrounds
                    sp = bkgsearchpath % int(dsid)
                    newx = '%s::%s::%s' % (sp, treename, x)
                    h_bkg = ipyhep.tree.project(newx, *args, **kwargs)
                else:
                    ## data-driven backgrounds
                    assert dsid == 'fakes' or dsid == 'efakes'
                    sp = datadrivensearchpath % dsid
                    newx = '%s::%s::%s' % (sp, treename, x)
                    h_bkg = ipyhep.tree.project(newx, *args, **kwargs)
                if h_bkg:
                    h_bkgs.append(h_bkg)
                    n_bkgs.append(dsid)
        if h_bkgs:
            stuff['h_bkgs'] = h_bkgs

    ## get signal histograms
    h_sigs = list()
    n_sigs = list()
    if sigs:
        for dsid in sigs:
            sp = sigsearchpath % int(dsid)
            newx = '%s::%s::%s' % (sp, treename, x)
            h_sig = ipyhep.tree.project(newx, *args, **kwargs)
            if h_sig:
                h_sigs.append(h_sig)
                n_sigs.append(dsid)
        if h_sigs:
            stuff['h_sigs'] = h_sigs

    assert h_sigs

    ## style data
    if h_data:
        h_data.title = 'Data'
        h_data.linecolor = ipyhep.style.black
        h_data.linewidth = 2
        h_data.markercolor = ipyhep.style.black
        h_data.markerstyle = 20
        h_data.markersize = 1.2
        h_data.fillstyle = ipyhep.style.fill_hollow
        h_data.drawstyle = 'PE'
        h_data.legendstyle = 'LP'

    ## scale and style background histograms
    if h_bkgs:
        assert len(h_bkgs) == len(n_bkgs), '%s\n%s' % (h_bkgs, n_bkgs)

        for h, dsid in zip(h_bkgs, n_bkgs):
            sf = ipyhep.sampleops.get_sf(dsid)
            if dsid.isdigit():
                sf *= lumi / __ntuple_lumi
            h.Scale(sf)

            h.title = ipyhep.sampleops.get_label(dsid)
            h.linecolor = ipyhep.style.black
            h.linewidth = 1
            h.markercolor = ipyhep.sampleops.get_color(dsid)
            h.fillcolor = ipyhep.sampleops.get_color(dsid)
            h.fillstyle = ipyhep.style.fill_solid
            h.legendstyle = 'F'

    ## calculate stat error on total background
    h_bkg_total = None
    if h_bkgs:
        for h_bkg in h_bkgs:
            if h_bkg_total:
                h_bkg_total.Add(h_bkg)
            else:
                h_bkg_total = h_bkg.Clone()
        stuff['h_bkg_total'] = h_bkg_total

    ## style h_bkg_total
    if h_bkg_total:
        h_bkg_total.title = 'stat. uncert.'
        h_bkg_total.linecolor = ipyhep.style.black
        h_bkg_total.linewidth = 1
        h_bkg_total.markerstyle = 0
        h_bkg_total.fillcolor = ipyhep.style.dark_gray
        h_bkg_total.fillstyle = ipyhep.style.fill_lines
        h_bkg_total.drawstyle = 'E2'
        h_bkg_total.legendstyle = 'LF'

    ## scale and style signal histograms
    if h_sigs:
        assert len(h_sigs) == len(n_sigs)
        for h, dsid in zip(h_sigs, n_sigs):
            sf = ipyhep.sampleops.get_sf(dsid)
            sf *= lumi / __ntuple_lumi
            h.Scale(sf)

            h.title = ipyhep.sampleops.get_label(dsid)
            h.linecolor = ipyhep.sampleops.get_color(dsid)
            h.linewidth = 3
            h.fillstyle = ipyhep.style.fill_hollow
            h.markerstyle = 0
            h.drawstyle = 'HIST'
            h.legendstyle = 'L'

    ## build list of all_hists
    all_hists = list()
    main_hists = list()
    if h_data:
        all_hists.append(h_data)
        main_hists.append(h_data)
    if h_bkgs:
        all_hists.extend(h_bkgs)
        main_hists.extend(h_bkgs)
    if h_bkg_total:
        all_hists.append(h_bkg_total)
        main_hists.append(h_bkg_total)
    if h_sigs:
        all_hists.extend(h_sigs)

    ## get statistics
    if all_hists:
        stats_list = list()
        for h in all_hists:
            stats_list.extend(get_stats(h))
        html = convert_table_to_html(convert_stats_to_table(stats_list))
        stuff['html'] = html

    ## renormalize for bin widths
    bins = kwargs.pop('bins', None)
    if bins and isinstance(bins, list):
        for h in all_hists:
            renormalize_for_bin_widths(h, bins)

    ## stack background histograms
    if h_bkgs:
        assert len(h_bkgs) == len(n_bkgs), '%s\n%s' % (h_bkgs, n_bkgs)

        h_bkgs.reverse()
        n_bkgs.reverse()

        hstack = HistStack()
        for h in h_bkgs:
            hstack.Add(h)
        hstack.title = 'stack sum'
        hstack.drawstyle = 'HIST'
        stuff['stack'] = hstack

        h_bkgs.reverse()
        n_bkgs.reverse()

#    ## convert data to TGraphAsymmErrors
#    g_data = None
#    if h_data:
#        if __use_poissonize:
#            g_data = poissonize.GetPoissonizedGraph(h_data)
#        else:
#            g_data = ROOT.TGraphAsymmErrors()
#            i_g = 0
#            nbins = h_data.GetNbinsX()
#            for i_bin in xrange(1, nbins+1): # skip underflow/overflow
#                c = h_data.GetBinContent(i_bin)
#                e = h_data.GetBinError(i_bin)
#                if c != 0.0:
#                    g_data.SetPoint(i_g, h_data.GetBinCenter(i_bin), c)
#                    g_ratio.SetPointError(i_g,
#                            h_data.GetBinWidth(i_bin)/2.,
#                            h_data.GetBinWidth(i_bin)/2.,
#                            e,
#                            e)
#                i_g += 1

## build list of objects to draw
    objects = list()
    if h_bkgs:
        objects.append(stuff['stack'])
        objects.append(stuff['h_bkg_total'])
    if h_sigs:
        objects.extend(h_sigs)
    if h_data:
        objects.append(h_data)

    ## set xlimits and ylimits
    ypadding = 0.21
    logy_crop_value = 7e-3
    xmin, xmax, ymin, ymax = 0.0, 1.0, 0.0, 1.0
    if objects:
        xmin, xmax, ymin, ymax = get_limits(objects,
                                            logx=logx,
                                            logy=logy,
                                            ypadding=ypadding,
                                            logy_crop_value=logy_crop_value)
    if logy:
        ymin = 7e-3
    else:
        ymin = 0.0
    xlimits = (xmin, xmax)
    ylimits = (ymin, ymax)
    stuff['xlimits'] = xlimits
    stuff['ylimits'] = ylimits

    ## remove xtitle for do_ratio
    _xtitle = xtitle
    if h_data and h_bkg_total and kwargs.get('do_ratio'):
        _xtitle = ''

    ## make canvas
    canvas = Canvas(800, 600)
    stuff['canvas'] = canvas

    ## draw the objects
    if objects:
        canvas.cd()
        draw(objects,
             pad=canvas,
             xtitle=_xtitle,
             ytitle=ytitle,
             xlimits=xlimits,
             ylimits=ylimits)

    ## set log x/y, for some reason doesn't work before draw
    if logx or logy:
        if logx:
            canvas.SetLogx()
        if logy:
            canvas.SetLogy()
        canvas.Update()

    ## draw blind_line
    if has_blinded_data:
        if isinstance(blind, tuple):
            blind_list = list(blind)
        else:
            blind_list = [blind]
        blind_lines = list()
        for bl in blind_list:
            line_y1 = ymin
            line_y2 = ymax
            blind_line = ROOT.TLine(bl, line_y1, bl, line_y2)
            blind_line.SetLineColor(ROOT.kGray + 2)
            blind_line.SetLineStyle(7)
            blind_line.SetLineWidth(2)
            blind_line.Draw()
            blind_lines.append(blind_line)
        stuff['blind_lines'] = blind_lines
        canvas.Update()

    ## legend
    lefty = True
    if h_bkg_total:
        lefty = is_left_sided(h_bkg_total)
    elif h_data:
        lefty = is_left_sided(h_data)
    elif h_sigs:
        lefty = is_left_sided(h_sigs[0])

    if main_hists:
        header = '%.1f fb^{-1}, 13 TeV' % (lumi / 1000.0)
        if lefty:
            legend = Legend(main_hists,
                            pad=canvas,
                            header=header,
                            textsize=16,
                            topmargin=0.03,
                            leftmargin=0.60,
                            rightmargin=0.02,
                            entrysep=0.01,
                            entryheight=0.04)
        else:
            legend = Legend(main_hists,
                            pad=canvas,
                            header=header,
                            textsize=16,
                            topmargin=0.03,
                            leftmargin=0.03,
                            rightmargin=0.59,
                            entrysep=0.01,
                            entryheight=0.04)
        legend.Draw()
        stuff['legend'] = legend

    if h_sigs:
        #        header = 'ATLAS Internal'
        header = ''
        if lefty:
            legend2 = Legend(h_sigs,
                             pad=canvas,
                             header=header,
                             textsize=16,
                             topmargin=0.03,
                             leftmargin=0.37,
                             rightmargin=0.23,
                             entrysep=0.01,
                             entryheight=0.04)
        else:
            legend2 = Legend(h_sigs,
                             pad=canvas,
                             header=header,
                             textsize=16,
                             topmargin=0.03,
                             leftmargin=0.20,
                             rightmargin=0.40,
                             entrysep=0.01,
                             entryheight=0.04)
        legend2.Draw()
        stuff['legend2'] = legend2

    ## do_ratio
    if h_data and h_bkg_total and kwargs.get('do_ratio'):

        ## top canvas
        top_canvas = stuff.pop('canvas')
        stuff['top_canvas'] = top_canvas

        ## make SM/SM with error band: h_ratio_band
        i_sfratio = int(kwargs.get('sfratio', -1))
        if i_sfratio < 0:  # ratio plot of Data/Model
            h_ratio_band = h_bkg_total.Clone()
            nbins = h_ratio_band.GetNbinsX()
            for i_bin in xrange(nbins + 2):
                h_ratio_band.SetBinContent(i_bin, 1.0)
                c = h_bkg_total.GetBinContent(i_bin)
                e = h_bkg_total.GetBinError(i_bin) / c if c > 0.0 else 0.0
                h_ratio_band.SetBinError(i_bin, e)
            stuff['h_ratio_band'] = h_ratio_band
        else:  # ratio plot of Scale Factor for ith background
            hi = h_bkgs[i_sfratio]
            h_ratio_band = hi.Clone()
            nbins = h_ratio_band.GetNbinsX()
            for i_bin in xrange(nbins + 2):
                h_ratio_band.SetBinContent(i_bin, 1.0)
                c = hi.GetBinContent(i_bin)
                e = hi.GetBinError(i_bin) / c if c > 0.0 else 0.0
                h_ratio_band.SetBinError(i_bin, e)
            stuff['h_ratio_band'] = h_ratio_band

        ## make data/(SM) h_ratio
        if i_sfratio < 0:
            h_ratio = h_data.Clone()
            h_ratio.Divide(h_data, h_bkg_total, 1.0, 1.0)
            stuff['h_ratio'] = h_ratio
        else:
            ## SF1 = 1.0 + (data - MCtot) / MC1
            sfname = kwargs.get('sfname')
            sffile = kwargs.get('sffile')
            if not sfname:
                sfname = 'h_sf'
            hi = h_bkgs[i_sfratio]
            h_numer = h_data.Clone()
            h_numer.Add(h_bkg_total, -1.0)
            ## do the division
            h_ratio = h_data.Clone(sfname)
            h_ratio.Divide(h_numer, hi, 1.0, 1.0)
            ## add the 1.0
            nbins = h_ratio.GetNbinsX()
            for i_bin in xrange(nbins + 2):
                c = h_ratio.GetBinContent(i_bin)
                h_ratio.SetBinContent(i_bin, c + 1.0)
                h_ratio_band.SetBinContent(i_bin, c + 1.0)
            ## ignore bins with no data for SF
            for i_bin in xrange(nbins + 2):
                c = h_data.GetBinContent(i_bin)
                if c <= 0:
                    h_ratio.SetBinContent(i_bin, 0.0)
                    h_ratio.SetBinError(i_bin, 0.0)
                    h_ratio_band.SetBinError(i_bin, 0.0)
            stuff['h_ratio'] = h_ratio
            if sffile:
                f_out = ipyhep.file.write(h_ratio, sffile)
#                f_out.Close()

## convert ratio to a TGraphErrors so that Draw('E0')
## shows error bars for points off the pad
        g_ratio = ROOT.TGraphErrors()
        i_g = 0
        for i_bin in xrange(1, nbins + 1):  # skip underflow/overflow
            ratio_content = h_ratio.GetBinContent(i_bin)
            if ratio_content != 0.0:
                g_ratio.SetPoint(i_g, h_ratio.GetBinCenter(i_bin),
                                 ratio_content)
                g_ratio.SetPointError(i_g,
                                      h_ratio.GetBinWidth(i_bin) / 2.,
                                      h_ratio.GetBinError(i_bin))
                i_g += 1
            else:
                h_ratio.SetBinError(i_bin, 0.0)
        stuff['g_ratio'] = g_ratio

        ## style ratio
        h_ratio_band.title = 'bkg uncert.'
        if i_sfratio < 0:
            h_ratio_band.linecolor = ipyhep.style.yellow
        else:
            h_ratio_band.linecolor = ipyhep.style.light_gray
        h_ratio_band.linewidth = 0
        h_ratio_band.markerstyle = 0
        if i_sfratio < 0:
            h_ratio_band.fillcolor = ipyhep.style.yellow
        else:
            h_ratio_band.linecolor = ipyhep.style.light_gray
        h_ratio_band.fillstyle = ipyhep.style.fill_solid
        h_ratio_band.drawstyle = 'E2'
        h_ratio_band.legendstyle = 'F'
        h_ratio.title = 'ratio'
        h_ratio.linecolor = ipyhep.style.black
        h_ratio.linewidth = 2
        h_ratio.markercolor = ipyhep.style.black
        h_ratio.markerstyle = 20
        h_ratio.markersize = 1.2
        h_ratio.fillstyle = ipyhep.style.fill_hollow
        h_ratio.drawstyle = 'PE'
        h_ratio.legendstyle = 'LP'

        ## bottom canvas
        bottom_canvas = Canvas(800, 600)
        bottom_canvas.cd()
        stuff['bottom_canvas'] = bottom_canvas

        ## set ratio ylimits
        ratio_min = kwargs.get('ratio_min', -0.2)
        ratio_max = kwargs.get('ratio_max', 2.2)
        ratio_ylimits = (ratio_min, ratio_max)

        ## draw ratio band
        if i_sfratio < 0:
            _ytitle = 'Data / Model'
        else:
            hi = h_bkgs[i_sfratio]
            _ytitle = 'SF(%s)' % hi.title
        draw([h_ratio_band],
             pad=bottom_canvas,
             xtitle=xtitle,
             ytitle=_ytitle,
             xlimits=xlimits,
             ylimits=ratio_ylimits)

        ## set log x/y, for some reason doesn't work before draw?
        if logx:
            bottom_canvas.SetLogx()
            bottom_canvas.Update()

        ### make horiz lines in ratio plot every 0.5:
        line_ys = [
            y / 10.0
            for y in range(10 *
                           int(round(ratio_min)), 10 * int(round(ratio_max)) +
                           5, 5)
        ]
        line_x1 = canvas.GetUxmin()
        line_x2 = canvas.GetUxmax()
        line_xwidth = abs(line_x2 - line_x1)
        lines = []
        for line_y in line_ys:
            line = ROOT.TLine(line_x1 + 0.02 * line_xwidth, line_y,
                              line_x2 - 0.02 * line_xwidth, line_y)
            line.SetLineWidth(1)
            line.SetLineStyle(7)
            if line_y == 1.0:
                line.SetLineColor(ROOT.kGray + 2)
            else:
                line.SetLineColor(ROOT.kGray + 0)
            line.Draw()
            lines.append(line)
        stuff['lines'] = lines

        ## draw blind_line
        if has_blinded_data:
            if isinstance(blind, tuple):
                blind_list = list(blind)
            else:
                blind_list = [blind]
            blind_lines = list()
            for bl in blind_list:
                line_y1 = ymin
                line_y2 = ymax
                blind_line = ROOT.TLine(bl, line_y1, bl, line_y2)
                blind_line.SetLineColor(ROOT.kGray + 2)
                blind_line.SetLineStyle(7)
                blind_line.SetLineWidth(2)
                blind_line.Draw()
                blind_lines.append(blind_line)
            stuff['blind_lines2'] = blind_lines
            canvas.Update()

        ## draw ratio
        g_ratio.Draw('PE0')
        #        h_ratio.GetYaxis().SetRangeUser(ratio_min, ratio_max)
        #        h_ratio.Draw('PE,SAME')

        ## shared canvas
        shared_canvas = Canvas(800, 800)
        shared_plot = plot_shared_axis(top_canvas,
                                       bottom_canvas,
                                       canvas=shared_canvas,
                                       split=0.35,
                                       axissep=0.01)
        stuff['canvas'] = shared_canvas
        canvas = shared_canvas

    ## save figures
    save = kwargs.get('save')

    if save is None:  # NOTE: save can be False to skip saving
        save = ['pdf', 'png']

    if save:
        ipyhep.file.save_figures(canvas, x, save)

    global results
    results = stuff
    return stuff
Пример #5
0
def calc_fake_factors(name,
                      numers_data,
                      numers_bkg,
                      denoms_data,
                      denoms_bkg,
                      labels,
                      xtitle,
                      ytitle,
                      save=None,
                      ylimits=None):

    assert numers_data, numers_bkg

    if not isinstance(numers_data, list):
        numers_data = [numers_data]
    if not isinstance(numers_bkg, list):
        numers_bkg = [numers_bkg]
    if not isinstance(denoms_data, list):
        denoms_data = [denoms_data]
    if not isinstance(denoms_bkg, list):
        denoms_bkg = [denoms_bkg]

    if save is None:
        save = ['pdf', 'png']

    stuff = dict()
    objects = list()

    colors = [
        ipyhep.style.red,
        ipyhep.style.blue,
        ipyhep.style.orange,
        ipyhep.style.violet,
    ]

    i_ff = 0
    for numer_data, numer_bkg, denom_data, denom_bkg in zip(
            numers_data, numers_bkg, denoms_data, denoms_bkg):

        for h in (numer_data, numer_bkg, denom_data, denom_bkg):
            if h and not h.GetSumw2N():
                h.Sumw2()

        h_numer = numer_data.Clone()
        h_numer.SetDirectory(0)
        if numer_bkg:
            set_negative_bins_zero(numer_bkg)
            h_numer.Add(numer_bkg, -1.0)

        h_denom = denom_data.Clone()
        h_denom.SetDirectory(0)
        if denom_bkg:
            set_negative_bins_zero(denom_bkg)
            h_denom.Add(denom_bkg, -1.0)

        h_ff = h_numer.Clone()
        h_ff.SetDirectory(0)
        divide_option = ''
        h_ff.Divide(h_numer, h_denom, 1.0, 1.0, divide_option)

        h_ff.linecolor = colors[i_ff]
        h_ff.markercolor = colors[i_ff]
        h_ff.markerstyle = 20
        h_ff.markersize = 1.2
        h_ff.fillstyle = ipyhep.style.fill_hollow
        h_ff.drawstyle = 'PE'
        h_ff.legendstyle = 'LP'
        if labels:
            h_ff.title = '%s' % labels[i_ff]
        else:
            h_ff.title = '%i' % i_ff
        h_ff.name = 'h_ff_%s' % h_ff.title

        stuff['h_ff_%s' % h_ff.title] = h_ff
        objects.append(h_ff)
        i_ff += 1

    ## make canvas
    canvas = Canvas(800, 600)
    stuff['canvas'] = canvas

    xmin, xmax, ymin, ymax = get_limits(objects)
    xlimits = (xmin, xmax)
    #    ylimits = (ymin, ymax)
    ylimits = ylimits or (0.0, 1.0)

    draw(objects,
         pad=canvas,
         xtitle=xtitle,
         ytitle=ytitle,
         xlimits=xlimits,
         ylimits=ylimits)

    ## legend
    header = ''
    legend = Legend(objects,
                    pad=canvas,
                    header=header,
                    textsize=16,
                    topmargin=0.02,
                    leftmargin=0.60,
                    rightmargin=0.02,
                    entrysep=0.01,
                    entryheight=0.04)

    legend.Draw()
    stuff['legend'] = legend

    ## save figures
    if save:
        ipyhep.file.save_figures(canvas, 'h_ff', save)

    ## save fake factors
    timestamp = time.strftime('%Y_%m_%d_%Hh%M')
    outfile = 'fakefactors-%s.root' % timestamp
    f_out = None
    for h_ff in objects:
        f_out = ipyhep.file.write(h_ff, outfile)
#    f_out.Close()

#    ipyhep.file.close_all_files()

    return stuff
h1.linewidth = 0

h2.fillstyle = 'solid'
h2.fillcolor = 'red'
h2.linecolor = 'red'
h2.linewidth = 0

stack = HistStack()
stack.Add(h1)
stack.Add(h2)

# plot with ROOT
canvas = Canvas(width=700, height=500)

# try setting logy=True and uncommenting the two lines below
xmin, xmax, ymin, ymax = get_limits([stack, h3], logy=False)
stack.SetMaximum(ymax)
#stack.SetMinimum(ymin)
#canvas.SetLogy()

stack.Draw('HIST E1 X0')
h3.Draw('SAME E1 X0')
stack.xaxis.SetTitle('Mass')
stack.yaxis.SetTitle('Events')
# set the number of expected legend entries
legend = Legend(3, leftmargin=0.45, margin=0.3)
legend.AddEntry(h1, style='F')
legend.AddEntry(h2, style='F')
legend.AddEntry(h3, style='LEP')
legend.Draw()
label = ROOT.TText(0.3, 0.8, 'ROOT')
Пример #7
0
    def draw_to_canvas(self):
        """
        Draw this figure to a canvas, which is then returned.
        """
        if len(self._plottables) == 0:
            raise IndexError("No plottables defined")
        c = Canvas(width=self.style.canvasWidth,
                   height=self.style.canvasHeight,
                   size_includes_decorations=True)
        if self.legend.position == 'seperate':
            legend_width = .2
            pad_legend = Pad(1 - legend_width, 0, 1., 1., name="legend")
            pad_legend.SetLeftMargin(0.0)
            pad_legend.SetFillStyle(0)  # make this pad transparent
            pad_legend.Draw()
        else:
            legend_width = 0
        pad_plot = Pad(0., 0., 1 - legend_width, 1., name="plot", )
        pad_plot.SetMargin(*self.style.plot_margins)
        pad_plot.Draw()
        pad_plot.cd()

        # awkward hack around a bug in get limits where everything fails if one plottable is shitty...
        xmin, xmax, ymin, ymax = None, None, None, None
        for pdic in self._plottables:
            try:
                limits = get_limits(pdic['p'], logx=self.plot.logx, logy=self.plot.logy)
                # Beware: Python 2 evaluates min/max of None in an undefined way with no error! Wow...
                xmin = min([xmin, limits[0]]) if xmin is not None else limits[0]
                xmax = max([xmax, limits[1]]) if xmax is not None else limits[1]
                ymin = min([ymin, limits[2]]) if ymin is not None else limits[2]
                ymax = max([ymax, limits[3]]) if ymax is not None else limits[3]
            except (TypeError, ValueError):
                # some plottables do not work with this rootpy function (eg. graph without points, tf1)
                # TODO: should be fixed upstream
                pass
        # overwrite these ranges if defaults are given
        if self.plot.xmin is not None:
            xmin = self.plot.xmin
        if self.plot.xmax is not None:
            xmax = self.plot.xmax
        if self.plot.ymax is not None:
            ymax = self.plot.ymax
        if self.plot.ymin is not None:
            ymin = self.plot.ymin

        if not all([val is not None for val in [xmin, xmax, ymin, ymax]]):
            raise TypeError("unable to determine plot axes ranges from the given plottables")

        colors = get_color_generator(self.plot.palette, self.plot.palette_ncolors)

        # draw an empty frame within the given ranges;
        frame_from_plottable = [p for p in self._plottables if p.get('use_as_frame')]
        if len(frame_from_plottable) > 0:
            frame = frame_from_plottable[0]['p'].Clone('__frame')
            frame.Reset()
            frame.SetStats(0)
            frame.xaxis.SetRangeUser(xmin, xmax)
            frame.yaxis.SetRangeUser(ymin, ymax)
            frame.GetXaxis().SetTitle(self.xtitle)
            frame.GetYaxis().SetTitle(self.ytitle)
            self._theme_plottable(frame)
            frame.Draw()
        else:
            frame = Graph()
            frame.SetName("__frame")
            # add a silly point in order to have root draw this frame...
            frame.SetPoint(0, 0, 0)
            frame.GetXaxis().SetLimits(xmin, xmax)
            frame.GetYaxis().SetLimits(ymin, ymax)
            frame.SetMinimum(ymin)
            frame.SetMaximum(ymax)
            frame.GetXaxis().SetTitle(self.xtitle)
            frame.GetYaxis().SetTitle(self.ytitle)
            self._theme_plottable(frame)
            # Draw this frame: 'A' should draw the axis, but does not work if nothing else is drawn.
            # L would draw a line between the points but is seems to do nothing if only one point is present
            # P would also draw that silly point but we don't want that!
            frame.Draw("AL")

        xtick_length = frame.GetXaxis().GetTickLength()
        ytick_length = frame.GetYaxis().GetTickLength()

        for i, pdic in enumerate(self._plottables):
            obj = pdic['p']
            if isinstance(obj, ROOT.TLegendEntry):
                _root_color = Color(pdic['color'])
                _root_markerstyle = MarkerStyle(pdic['markerstyle'])
                obj.SetMarkerStyle(_root_markerstyle('root'))
                obj.SetMarkerColor(_root_color('root'))

            elif isinstance(obj, (ROOT.TH1, ROOT.TGraph, ROOT.TF1)):
                self._theme_plottable(obj)
                obj.SetMarkerStyle(pdic.get('markerstyle', 'circle'))
                if pdic.get('color', None):
                    obj.color = pdic['color']
                else:
                    try:
                        color = next(colors)
                    except StopIteration:
                        log.warning("Ran out of colors; defaulting to black")
                        color = 1
                    obj.color = color
                xaxis = obj.GetXaxis()
                yaxis = obj.GetYaxis()

                # Set the title to the given title:
                obj.title = self.title

                # the xaxis depends on the type of the plottable :P
                if isinstance(obj, ROOT.TGraph):
                    # SetLimit on a TH1 is simply messing up the
                    # lables of the axis to screw over the user, presumably...
                    xaxis.SetLimits(xmin, xmax)
                    yaxis.SetLimits(ymin, ymax)  # for unbinned data
                    # 'P' plots the current marker, 'L' would connect the dots with a simple line
                    # see: https://root.cern.ch/doc/master/classTGraphPainter.html for more draw options
                    drawoption = 'Psame'
                elif isinstance(obj, ROOT.TH1):
                    obj.SetStats(0)
                    xaxis.SetRangeUser(xmin, xmax)
                    yaxis.SetRangeUser(ymin, ymax)
                    drawoption = 'same'
                elif isinstance(obj, ROOT.TF1):
                    # xaxis.SetLimits(xmin, xmax)
                    # yaxis.SetLimits(ymin, ymax)  # for unbinned data
                    drawoption = 'same'
                obj.Draw(drawoption)
            # Its ok if obj is non; then we just add it to the legend.
            else:
                raise TypeError("Un-plottable type given.")
        pad_plot.SetTicks()
        pad_plot.SetLogx(self.plot.logx)
        pad_plot.SetLogy(self.plot.logy)
        pad_plot.SetGridx(self.plot.gridx)
        pad_plot.SetGridy(self.plot.gridy)

        # do we have legend titles?
        if any([pdic.get('legend_title') for pdic in self._plottables]):
            leg = self._create_legend()
            longest_label = 0
            for pdic in self._plottables:
                if not pdic.get('legend_title', False):
                    continue
                leg.AddEntry(pdic['p'], pdic['legend_title'])
                if len(pdic['legend_title']) > longest_label:
                    longest_label = len(pdic['legend_title'])

            # Set the legend position
            # vertical:
            if self.legend.position.startswith('t'):
                leg_hight = leg.y2 - leg.y1
                leg.y2 = 1 - pad_plot.GetTopMargin() - ytick_length
                leg.y1 = leg.y2 - leg_hight
            elif self.legend.position.startswith('b'):
                leg_hight = leg.y2 - leg.y1
                leg.y1 = pad_plot.GetBottomMargin() + ytick_length
                leg.y2 = leg.y1 + leg_hight
            # horizontal:
            if self.legend.position[1:].startswith('l'):
                leg_width = 0.3
                leg.x1 = pad_plot.GetLeftMargin() + xtick_length
                leg.x2 = leg.x1 + leg_width
            elif self.legend.position[1:].startswith('r'):
                leg_width = 0.3
                leg.x2 = 1 - pad_plot.GetRightMargin() - xtick_length
                leg.x1 = leg.x2 - leg_width
            if self.legend.position == 'seperate':
                with pad_legend:
                    leg.Draw()
            else:
                leg.Draw()
        if self.plot.logx:
            pad_plot.SetLogx(True)
        if self.plot.logy:
            pad_plot.SetLogy(True)
        pad_plot.Update()  # needed sometimes with import of canvas. maybe because other "plot" pads exist...
        return c