Beispiel #1
0
    def __init__(self, numerator, denominator=0, **kwargs):
        r"""Initialize a ratio plot for 1-dimensional histograms.

        Create an instance of :class:`.RatioPlot` with the specified (list of)
        **numerator** histogram(s) and **denominator** histogram.

        :param numerator: numerator histogram or list thereof
        :type name: ``Histo1D``, ``TH1D``, ``list``

        :param denominator: denominator histogram which will act as the **baseline**
        :type name: ``Histo1D``, ``TH1D``

        :param \**kwargs: :class:`.RatioPlot` properties
        """
        MethodProxy.__init__(self)
        self._name = uuid4().hex[:8]
        self._drawarrows = False
        self._drawbenchmarklines = False
        if not isinstance(numerator, list):
            numerator = [numerator]
        kwargs.setdefault("template", "common")
        if denominator.InheritsFrom("TH1"):
            self._baseline = Histo1D(
                "{}_RatioPlotDenominator".format(denominator.GetName()), denominator
            )
            self._numeratorhistos = []
            for i, histo in enumerate(numerator, start=1):
                assert histo.InheritsFrom("TH1")
                numname = histo.GetName()
                numerator = Histo1D(
                    "{}_RatioPlotNumerator{}".format(denominator.GetName(), i), histo
                )
                numerator.Divide(
                    histo, self._baseline, 1.0, 1.0, kwargs.get("divideoption", "pois")
                )
                self._numeratorhistos.append(numerator)
            for bn in range(self._baseline.GetNbinsX() + 2):
                try:
                    self._baseline.SetBinError(
                        bn,
                        self._baseline.GetBinError(bn)
                        / self._baseline.GetBinContent(bn),
                    )
                except ZeroDivisionError:
                    self._baseline.SetBinError(bn, 0.0)
                self._baseline.SetBinContent(bn, 1.0)
        elif (
            denominator.InheritsFrom("TFitResult")
            or denominator == 0
            or denominator.InheritsFrom("THStack")
        ):
            raise NotImplementedError
        else:
            raise TypeError
        self._ymin = None
        self._ymax = None
        self._numeratordrawoption = ""
        self.DeclareProperties(**kwargs)
Beispiel #2
0
    def Register(self, histo, **kwargs):
        r"""Register a histogram to the stack.

        If the **stack** keyword arguments (or the associated property of the
        :class:`.Histo1D` **histo**) is ``True`` the **histo** will be drawn in a stack.
        Otherwise the histogram will be displayed overlayed to the stack.

        The properties of the **histo** registered to the stack can be changed by simply
        passing them as keyword arguments.

        :param histo: histogram to be added to the :class:`.Stack` object
        :type histo: ``Histo1D``, ``TH1D``

        :param \**kwargs: :class:`.Histo1D` properties + additional properties (see
            below)

        :Keyword Arguments:

            * **stack** (``bool``) -- if set to ``True`` the histogram will be displayed
              in the stack of all stacked histograms
        """
        stack = kwargs.pop("stack", histo.GetStack())
        histo.SetDrawErrorband(False)
        if self._stacksumhisto is None:
            self._stacksumhisto = Histo1D(
                "{}_totalstack".format(self.GetName()),
                histo,
                title=self._stacksumproperties.get("stacksumtitle", None),
            )
            self._stacksumhisto.Reset()
        if stack:
            self._stacksumhisto.Add(histo)
        histo.DeclareProperties(**kwargs)
        self._store["stack" if stack else "nostack"].append(histo)
Beispiel #3
0
 def Register(self, infile, **kwargs):
     histotype = kwargs.pop("type")
     assert histotype.lower() in self._configs.keys()
     assert "tree" in kwargs
     assert "varexp" not in kwargs
     template = kwargs.pop("template", {})
     self._configs[histotype.lower()].append(
         (infile, MergeDicts(Histo1D.GetTemplate(template), kwargs)))
Beispiel #4
0
    def __init__(self, sighistos, bkghisto, **kwargs):
        r"""Initialize a sensitivity scan plot.

        Creates sensitivity histograms for each of the signal histograms **sighistos**.
        The sensitivity per bin is computed using the function specified by the
        **sensitivitymeasure** property by comparing the signal yield to the number of
        events in the background histograms **bkghisto** as defined by the user.

        :param sighistos: a single or list of signal histograms, or list of tuples where
            the first entry corresponds to the signal histogram and the second to the
            dictionary of properties of the resulting sensitivity histogram
        :type sighistos: ``list``, ``Histo1D``, ``TH1D``

        :param bkghisto: total background histogram
        :type bkghisto: ``Histo1D``, ``TH1D``

        :param \**kwargs: :class:`.SensitivityScan` properties
        """
        self._loadTemplates()
        self._name = "SensitivityScan_{}".format(uuid4().hex[:8])
        self._direction = None
        self._bkghisto = bkghisto
        self._sighistos = []
        properties = []
        if isinstance(sighistos, (list, tuple)):
            for i, entry in enumerate(sighistos):
                if isinstance(entry, (list, tuple)):
                    histo, props = entry
                    self._sighistos.append(histo)
                    properties.append(props)
                else:
                    self._sighistos.append(entry)
                    properties.append({})
        else:
            self._sighistos = [sighistos]
        self._sensitivityhistos = []  # histos to be drawn
        for i, sighisto in enumerate(self._sighistos):
            # Use the same properties as the signal histo:
            scan = Histo1D("{}_SensitivityScan".format(sighisto.GetName()),
                           sighisto)
            scan.Reset()
            self._sensitivityhistos.append(scan)
            self.SetSensitivityHistoProperties(i, **properties[i])
        self._nbins = self._bkghisto.GetNbinsX()
        self._flatbkgsys = 0.3  # relative uncertainty
        self._ymin = None
        self._ymax = None
        self._sensitivitymeasure = None
        for key, value in self.GetTemplate(kwargs.get("template",
                                                      "common")).items():
            kwargs.setdefault(key, value)
        self.DeclareProperties(**kwargs)
        self.BuildHistos()
Beispiel #5
0
        def mephistofy(object):
            # If the object class inherits from MethodProxy return the original object
            # unless copy=True then continue. If not substitute the object with an
            # instance of the corresponding MEPHISTO class - imported here to avoid
            # circular imports - by calling the copy constructor.
            from Text import Text
            from Stack import Stack
            from Graph import Graph
            from Histo1D import Histo1D
            from Histo2D import Histo2D

            def lookupbases(cls):
                bases = list(cls.__bases__)
                for base in bases:
                    bases.extend(lookupbases(base))
                return bases

            clsname = object.__class__.__name__
            if "MethodProxy" in [
                    basecls.__name__
                    for basecls in lookupbases(object.__class__)
            ]:
                if not copy:
                    return object
            suffix = "mephistofied" if not copy else "copy"
            if object.InheritsFrom("TH2"):
                return Histo2D("{}_{}".format(object.GetName(), suffix),
                               object)
            elif object.InheritsFrom("TH1"):
                return Histo1D("{}_{}".format(object.GetName(), suffix),
                               object)
            elif object.InheritsFrom("THStack"):
                return Stack("{}_{}".format(object.GetName(), suffix), object)
            elif object.InheritsFrom("TGraph"):
                return Graph("{}_{}".format(object.GetName(), suffix), object)
            elif object.InheritsFrom("TText") or object.InheritsFrom("TLatex"):
                return Text(object)
            raise NotImplementedError
Beispiel #6
0
 def CreateHistograms(self):
     factories = {}
     for histotype, configs in self._configs.items():
         self._store[histotype] = {}
         for i, (infile, config) in enumerate(configs):
             self._store[histotype][i] = {}
             config = DissectProperties(
                 config, [Histo1D, {
                     "Fill": ["tree", "cuts", "weight"]
                 }])
             tree = config["Fill"]["tree"]
             if not (infile, tree) in factories.keys():
                 factories[infile, tree] = IOManager.Factory(infile, tree)
             for varexp, comparator, cutvalue in self._drawcuts:
                 if not varexp in self._binning:
                     logger.warning("No binning defined for varexp '{}'. "
                                    "Skipping...".format(varexp))
                     continue
                 reducedcuts = list(
                     filter(
                         lambda x: x != "".join(
                             [varexp, comparator, cutvalue]),
                         self._cuts,
                     ))
                 self._store[histotype][i][varexp, cutvalue] = Histo1D(
                     "N1_{}".format(uuid4().hex[:8]), "",
                     self._binning[varexp], **config["Histo1D"])
                 factories[infile, tree].Register(
                     self._store[histotype][i][varexp, cutvalue],
                     varexp=varexp,
                     weight=config["Fill"].get("weight", "1"),
                     cuts=list(config["Fill"].get("cuts", []) +
                               reducedcuts + self._preselection),
                 )
     for factory in factories.values():
         factory.Run()
Beispiel #7
0
                    )
                except ZeroDivisionError:
                    histo.SetBinContent(bn, 0)
            self._stacksumhisto.SetBinContent(bn, 1.0)
        self.DeclareProperties(**kwargs)
        self.BuildStack(sort=False)


if __name__ == "__main__":

    from Plot import Plot
    from Histo1D import Histo1D
    from IOManager import IOManager

    filename = "../data/ds_data18.root"
    h1 = Histo1D("h1", "h1", 20, 0.0, 400.0)
    h2 = Histo1D("h2", "h2", 20, 0.0, 400.0)
    h3 = Histo1D("h3", "h3", 20, 0.0, 400.0)

    h1.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>600")
    h2.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>725")
    h3.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>850")

    s = Stack()
    s.Register(h1, stack=True, template="background", fillcolor=ROOT.kBlue)
    s.Register(h2, stack=True, template="background", fillcolor=ROOT.kGreen)
    s.Register(h3, stack=True, template="background", fillcolor=ROOT.kRed)

    c = ContributionPlot(s)

    # p1 = Plot(npads=1)
Beispiel #8
0
        :returntype: ``bool``
        """
        return self._drawbenchmarklines


if __name__ == "__main__":

    from Plot import Plot
    from IOManager import IOManager

    filename = "../data/ds_data18.root"

    RatioPlot.PrintAvailableProperties()

    h1 = Histo1D("test1", "test1", 20, 0.0, 400.0)
    h2 = Histo1D("test2", "test2", 20, 0.0, 400.0)
    h3 = Histo1D("test3", "test3", 20, 0.0, 400.0)
    h1.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>650")
    h2.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>620")
    h3.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>700")

    h1.DeclareProperty("template", "background")
    h2.DeclareProperties(template="data", markercolor="#a7126b", linecolor="#a7126b")
    h3.DeclareProperties(template="signal", linecolor="#17a242")
    rp = RatioPlot([h2, h3], h1)

    p1 = Plot(npads=2)
    p1.Register(h1, 0)
    p1.Register(h2, 0, xunits="GeV")
    p1.Register(h3, 0)
Beispiel #9
0
    def __init__(self, name=None, *args, **kwargs):
        r"""Initialize a collection of 1-dimensional histograms.

        Create an instance of :class:`.Stack` with the specified **name**. Can also be
        used to copy another histogram (or upgrade from a :class:`ROOT.THStack`).

        :param name: name of the stack (default: random 8-digits HEX hash value)
        :type name: ``str``

        :param \*args: see below

        :param \**kwargs: :class:`.Stack` properties

        :Arguments:
            Depending on the number of arguments (besides **name**) there are two ways
            to initialize a :class:`.Stack` object\:

            * *zero* arguments\: an empty stack is created

            * *one* argument\:

                #. **stack** (``Stack``, ``THStack``) -- existing stack to be copied
        """
        MethodProxy.__init__(self)
        if name is None:
            name = uuid4().hex[:8]
        self._stacksumhisto = None
        self._stacksumproperties = {}
        self._stacksorting = None
        self._drawoption = ""
        self._drawstacksum = False
        self._store = dict(stack=[], nostack=[])
        if len(args) == 0:
            ROOT.THStack.__init__(self, name, "")
        elif len(args) == 1:
            if isinstance(args[0], str):
                ROOT.THStack.__init__(self, name, args[0])
            elif args[0].InheritsFrom("THStack"):
                ROOT.THStack.__init__(self, args[0])
                self.SetName(name)
                if args[0].__class__.__name__ == "Stack":
                    if args[0]._stacksumhisto is not None:
                        self._stacksumhisto = Histo1D(
                            "{}_stacksum", args[0]._stacksumhisto
                        )
                    for key, store in args[0]._store.items():
                        for histo in store:
                            self._store[key].append(
                                Histo1D("{}_{}".format(histo.GetName(), name), histo)
                            )
                    self.DeclareProperties(**args[0].GetProperties())
            else:
                raise TypeError
        else:
            raise TypeError
        for key, value in self.GetTemplate(kwargs.get("template", "common")).items():
            kwargs.setdefault(key, value)
        properties = DissectProperties(
            kwargs,
            [
                {
                    "Stacksum": [
                        "stacksum{}".format(p) for p in Histo1D.GetListOfProperties()
                    ]
                },
                Stack,
            ],
        )
        self.DeclareProperties(**properties["Stack"])
        self._stacksumproperties = properties["Stacksum"]
Beispiel #10
0
    def Print(self, path, **kwargs):
        r"""Print the histogram to a file.

        Creates a PDF/PNG/... file with the absolute path defined by **path**. If a file
        with the same name already exists it will be overwritten (can be changed  with
        the **overwrite** keyword argument). If **mkdir** is set to ``True`` (default:
        ``False``) directories in **path** with do not yet exist will be created
        automatically. The styling of the stack, pad and canvas can be configured via
        their respective properties passed as keyword arguments.

        Additional plots can be added to :class:`.Pad` s below the main one where the
        :class:`.Stack` object is drawn using the **ratio**, **contribution** and/or
        **sensitivity** flag (see below).

        :param path: path of the output file (must end with '.pdf', '.png', ...)
        :type path: ``str``

        :param \**kwargs: :class:`.Stack`, :class:`.Plot`, :class:`.Canvas` and
            :class:`.Pad` properties + additional properties (see below)

        Keyword Arguments:

            * **sort** (``bool``) -- define whether the stack is sorted before it is
              drawn (see :func:`Stack.SetStackSorting`, default: ``True``)

            * **contribution** (``bool``) -- draw the relative per-bin contribution of
              each stacked histogram in an additional :class:`.Pad` (default: ``False``)

            * **ratio** (``bool``) -- draw a :class:`RatioPlot` of (assumed) data versus
              the (assumed) total background in an additional :class:`.Pad` (default:
              ``False``)
            * **sensitivity** (``bool``) -- draw a sensitivity scan for all (assumed)
              signal histograms and using the (asumed) total background as reference in
              an additional :class:`.Pad` (default: ``False``)

            * **inject<N>** (``list``, ``tuple``, ``ROOT.TObject``) -- inject a (list
              of) *drawable* :class:`ROOT` object(s) to pad **<N>** (default: 0), object
              properties can be specified by passing instead a ``tuple`` of the format
              :code:`(obj, props)` where :code:`props` is a ``dict`` holding the object
              properties (default: \[\])

            * **overwrite** (``bool``) -- overwrite an existing file located at **path**
              (default: ``True``)

            * **mkdir** (``bool``) -- create non-existing directories in **path**
              (default: ``False``)
        """
        from RatioPlot import RatioPlot
        from SensitivityScan import SensitivityScan
        from ContributionPlot import ContributionPlot

        sort = kwargs.pop("sort", True)
        contribution = kwargs.pop("contribution", False)
        ratio = kwargs.pop("ratio", False)
        sensitivity = kwargs.pop("sensitivity", False)
        if kwargs.get("drawstacksum"):
            self.SetDrawStackSum(kwargs.pop("drawstacksum"))
        injections = {
            k: kwargs.pop(k) for k in dict(kwargs).keys() if k.startswith("inject")
        }
        npads = sum([contribution, bool(ratio), bool(sensitivity)]) + 1
        addcls = [RatioPlot] if ratio else []
        addcls += [ContributionPlot] if contribution else []
        addcls += [SensitivityScan] if sensitivity else []
        properties = DissectProperties(kwargs, [Stack, Plot, Canvas, Pad] + addcls)
        self.BuildStack(sort=sort)
        if self.GetNhists() == 0:
            injections = {"inject0": injections.get("inject0", [])}
            if contribution:
                logger.warning("Cannot create ContributionPlot: Missing stack!")
                contribution = False
            if ratio:
                logger.warning("Cannot create RatioPlot: Missing stack!")
                ratio = False
            if sensitivity:
                logger.warning("Cannot create SensitivityScan: Missing stack!")
                sensitivity = False
        if ratio is True:
            try:  # it's just a guess...
                datahisto = filter(
                    lambda h: not h.GetDrawOption().upper().startswith("HIST"),
                    self._store["nostack"],
                )[0]
                ratio = [datahisto, self._stacksumhisto]  # overwrite boolean
            except IndexError:
                if self._store["nostack"]:
                    ratio = [self._store["nostack"][0], self._stacksumhisto]
                else:
                    logger.error(
                        "Failed to identify appropriate numerator histogram for "
                        "RatioPlot pad!"
                    )
                    ratio = False
        if sensitivity:
            try:  # again just making assumptions here...
                sensitivity = filter(
                    lambda h: "HIST" in h.GetDrawOption().upper(),
                    self._store["nostack"],  # overwrite boolean with list of sig histos
                )
            except IndexError:
                logger.error(
                    "Failed to identify appropriate numerator histogram for "
                    "SensitivityScan pad!"
                )
                sensitivity = False
        plot = Plot(npads=npads, **properties["Plot"])
        # Register the Stack to the upper Pad (pad=0):
        plot.Register(self, **MergeDicts(properties["Stack"], properties["Pad"]))
        if self._drawstacksum and self.GetNhists() > 1:
            # Dummy histo with the correct legend entry styling:
            htmp = Histo1D(
                "{}_legendentry".format(self._stacksumhisto.GetName()),
                self._stacksumhisto.GetTitle(),
                [
                    self._stacksumhisto._lowbinedges[0],
                    self._stacksumhisto._lowbinedges[self._stacksumhisto._nbins],
                ],
                **{
                    k[8:]: v
                    for k, v in DissectProperties(
                        properties["Stack"],
                        [
                            {
                                "Stacksum": [
                                    "stacksum{}".format(p)
                                    for p in Histo1D.GetListOfProperties()
                                ]
                            }
                        ],
                    )["Stacksum"].items()
                }
            )
            plot.Register(
                htmp,
                fillcolor=self._stacksumhisto._errorband.GetFillColor(),
                fillstyle=self._stacksumhisto._errorband.GetFillStyle(),
                legenddrawoption=self._stacksumhisto.GetLegendDrawOption(),
                **properties["Pad"]
            )
        idx = 1
        xaxisprops = {
            k: v for k, v in properties["Pad"].items() if k in ["xtitle", "xunits"]
        }
        if contribution:
            contribplot = ContributionPlot(self)
            properties["ContributionPlot"].update(
                xaxisprops, **properties["ContributionPlot"]
            )
            plot.Register(
                contribplot,
                pad=idx,
                ytitle="Contrib.",
                logy=False,
                ymin=0,
                ymax=1,
                **{k: v for k, v in properties["Pad"].items() if k.startswith("x")}
            )
            idx += 1
        if ratio:
            ratioplot = RatioPlot(*ratio, **properties["RatioPlot"])
            properties["RatioPlot"].update(xaxisprops)
            plot.Register(
                ratioplot,
                pad=idx,
                ytitle="Data / SM",
                logy=False,
                ymin=0.2,
                ymax=1.8,
                **{k: v for k, v in properties["Pad"].items() if k.startswith("x")}
            )
            idx += 1
        if sensitivity:
            sensitivityscan = SensitivityScan(
                sensitivity, self._stacksumhisto, **properties["SensitivityScan"]
            )
            properties["SensitivityScan"].update(xaxisprops)
            plot.Register(
                sensitivityscan,
                pad=idx,
                ytitle="Z_{A}-value",
                logy=False,
                **{k: v for k, v in properties["Pad"].items() if k.startswith("x")}
            )
        plot.Print(path, **MergeDicts(properties["Canvas"], injections))
Beispiel #11
0
    nbkgs = 6
    t = Text(0.0, 0.5, "MEEP", ndc=False, textcolor=ROOT.kRed)  # to be injected

    threshold = (
        [220]
        + [450]
        + [500 + i * (200.0 / nbkgs) for i in range(nbkgs - 1)]
        + [420, 470]
    )

    h = {}
    s = Stack()
    nbkgs += 1
    for i in range(1, 1 + nbkgs + 2, 1):
        h[i] = Histo1D("h{}".format(i), "Histogram {}".format(i), 20, 0.0, 400.0)
        h[i].Fill(
            filename,
            tree="DirectStau",
            varexp="MET",
            cuts="tau1Pt>{}".format(threshold[i - 1]),
        )
        if i > 1 and i <= nbkgs:
            s.Register(h[i], stack=True, template="background", fillcolor=i)
    s.Register(h[1], stack=False, template="data")
    s.Register(h[nbkgs + 1], stack=False, template="signal", linecolor="#ff8200")
    s.Register(h[nbkgs + 2], stack=False, template="signal", linecolor="#00c892")

    s.PrintYieldTable("tmp/table.pdf", mkdir=True)

    for j in range(4):
Beispiel #12
0
                     bm,
                     xmax,
                     bm,
                     linestyle=7,
                     linecoloralpha=(ROOT.kBlack, 0.6)))
        for line in self._benchmarklines:
            line.Draw()


if __name__ == "__main__":

    from Plot import Plot

    filename = "../data/ds_data18.root"

    h_bkg = Histo1D("h_bkg", "Background", 20, 0.0, 400.0)
    h_sig1 = Histo1D("h_sig1", "Signal 1", 20, 0.0, 400.0)
    h_sig2 = Histo1D("h_sig2", "Signal 2", 20, 0.0, 400.0)

    h_bkg.Fill(filename, tree="DirectStau", varexp="MET", weight="100.0/MET")
    h_sig1.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>600")
    h_sig2.Fill(filename, tree="DirectStau", varexp="MET", cuts="tau1Pt>800")

    h_sig1_props = dict(template="signal", linecolor=ROOT.kRed)
    h_sig2_props = dict(template="signal", linecolor=ROOT.kBlue)

    scan = SensitivityScan([(h_sig1, h_sig1_props), (h_sig2, h_sig2_props)],
                           h_bkg)

    p = Plot(npads=2)
    p.Register(h_bkg, template="background", fillcolor=ROOT.kGray)