示例#1
0
def plotSpectrum():

    useMalbek = False
    eName = "energy_keV"
    eName = "trapENFCal"

    # load workspace
    f = TFile("./data/fitWorkspace.root")
    fitWorkspace = f.Get("fitWorkspace")
    fData = fitWorkspace.allData().front()
    fitResult = fitWorkspace.allGenericObjects().front()
    nPars = fitResult.floatParsFinal().getSize()
    fEnergy = fitWorkspace.var(eName)
    modelPDF = fitWorkspace.pdf("model")
    # fitWorkspace.Print()

    # get a list of pdf names
    pdfNames = []
    pdfList = fitWorkspace.allPdfs()  # goddamn RooArgSet
    itr = pdfList.createIterator()
    var = itr.Next()
    while var:
        name = var.GetName()
        if "ext" in name:
            pdfNames.append(name)
        var = itr.Next()
    pdfNames = sorted(pdfNames)

    # -- create spectrum rooplot --
    nCol = float(gStyle.GetNumberOfColors())
    binSize = 0.2
    nBins = int((eHi - eLo) / binSize + 0.5)
    fSpec = fEnergy.frame(RF.Range(eLo, eHi), RF.Bins(nBins))
    fData.plotOn(fSpec)
    modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel"))
    chiSquare = fSpec.chiSquare(nPars)

    # draw components
    leg = TLegend(0.83, 0.1, 0.97, 0.9)
    leg.AddEntry(fSpec.findObject("FullModel"),
                 "model #chi^{2}=%.3f" % chiSquare, "l")
    for idx in range(len(pdfNames)):
        name = pdfNames[idx]
        lineCol = gStyle.GetColorPalette(int(nCol / len(pdfNames) * idx))
        modelPDF.plotOn(fSpec, RF.Components(name), RF.LineColor(lineCol),
                        RF.Name(name))
        plotName = name
        if "ext-" in name:
            plotName = plotName[4:]
        leg.AddEntry(fSpec.findObject(name), plotName, "l")

    # create normalized residual ("pull") rooplot
    # (draw full model again so residuals are calculated against it)
    modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel"))
    res = fSpec.pullHist()
    fRes = fEnergy.frame(RF.Title(" "))
    fRes.addPlotable(res, "P")

    # -- print fit results --
    print "-- SPECFIT RESULTS -- "
    print "%-10s = %.3f" % ("chiSq", chiSquare)
    fitValsFinal = getRooArgDict(fitResult.floatParsFinal())

    for name in sorted(fitValsFinal):
        fitVal = fitValsFinal[name]
        if "amp-" in name:
            error = fitWorkspace.var(name).getError()
            print "%-10s = best %-7.3f  error %.3f (w/o profile)" % (
                name, fitVal, error)
        elif "mu-" in name:
            # compare the energy offset
            pkName = name[3:]
            pct = 100 * (1 - fitVal / pkList[pkName])
            print "%-10s : fit %-6.3f  lit %-6.3f  (%.3f%%)" % (
                name, fitVal, pkList[pkName], pct)
        elif "sig-" in name:
            # compare the sigma difference
            pkName = name[4:]
            pct = 100 * (1 - fitVal / getSigma(pkList[pkName]))
            print "%-10s : fit %-6.3f  func %-6.3f  (%.3f%%)" % (
                name, fitVal, getSigma(pkList[pkName]), pct)
        else:
            print "%s = %.4f" % (name, fitVal)
            continue

    # -- make spectrum plot w/ residual, w/ all the formatting crap --
    gStyle.SetOptStat(0)
    gStyle.SetPalette(
        ROOT.kRainBow)  # https://root.cern.ch/doc/master/classTColor.html#C06
    c = TCanvas("c", "Bob Ross's Canvas", 1400, 1000)
    p1 = TPad("p1", "spectrum", 0., 0.3, 1., 1.)
    p2 = TPad("p2", "residual", 0., 0., 1., 0.3)
    p1.Draw()
    p2.Draw()
    p1.SetRightMargin(0.2)
    p1.SetBottomMargin(0.)
    p2.SetRightMargin(0.2)
    p2.SetTopMargin(0.)
    p2.SetBottomMargin(0.4)

    p1.cd()
    # p1.SetGrid()
    fSpec.SetTitle("")
    fSpec.GetXaxis().SetLabelSize(0.)
    fSpec.GetYaxis().SetLabelSize(0.05)
    fSpec.GetYaxis().SetTitleSize(0.06)
    fSpec.GetYaxis().SetTitleOffset(0.6)
    fSpec.GetYaxis().SetTitle("Events / (%.1f keV)" % binSize)

    fSpec.Draw()
    leg.Draw("same")

    p2.cd()
    fRes.GetXaxis().SetLabelSize(0.1)
    fRes.GetYaxis().SetLabelSize(0.1)
    fRes.GetXaxis().SetTitleSize(0.15)
    fRes.GetYaxis().SetTitle("Normalized Resid.")
    fRes.GetYaxis().SetTitleOffset(0.3)
    fRes.GetYaxis().SetTitleSize(0.1)
    fRes.Draw()
    zeroLine = ROOT.TLine(eLo, 0., eHi, 0.)
    zeroLine.SetLineColor(ROOT.kBlack)
    zeroLine.Draw("same")
    c.Print("./plots/spectrum.pdf")

    # -- correlation matrix --
    c2 = TCanvas("c2", "Bob Ross's Canvas", 1100, 800)
    c2.SetLeftMargin(0.15)
    c2.SetRightMargin(0.12)
    gStyle.SetPalette(ROOT.kDarkBodyRadiator)
    corrMat = fitResult.correlationHist("Correlation Matrix")
    corrMat.SetTitle("")
    corrMat.GetXaxis().SetLabelSize(0.02)
    corrMat.Draw("colz")
    c2.Print("./plots/corrMatrix.pdf")
示例#2
0
def fitModel(makePlots=False):
    from ROOT import TFile, TH1D, TCanvas, TLegend, gStyle

    # === load data into workspace ===

    tf = TFile("%s/data/latDS%s.root" %
               (dsi.latSWDir, ''.join([str(d) for d in dsList])))
    tt = tf.Get("skimTree")
    tCut = "isEnr==1" if enr is True else "isEnr==0"
    hitE = ROOT.RooRealVar("trapENFCal", "Energy", eLo, eHi, "keV")
    hEnr = ROOT.RooRealVar("isEnr", "isEnr", 0, 1, "")
    # hitW = ROOT.RooRealVar("weight", "weight", 1, 1000, "")
    fData = ROOT.RooDataSet("data", "data", tt, ROOT.RooArgSet(hitE, hEnr),
                            tCut)
    # fData = ROOT.RooDataSet("data", "data", tt, ROOT.RooArgSet(hitE, hEnr, hitW), "", "weight")
    fitWorkspace = ROOT.RooWorkspace("fitWorkspace", "Fit Workspace")
    getattr(fitWorkspace, 'import')(hitE)
    getattr(fitWorkspace, 'import')(fData)
    # getattr(fitWorkspace,'import')(fWeight)

    tf2 = TFile("%s/data/specPDFs.root" % dsi.latSWDir)
    pdfList = ROOT.RooArgList("shapes")

    # === background model ===
    hList = getHistList()
    bkgList = []
    for h in hList:
        hB, name = h[0], h[1]
        pars = bkgVals[name]
        bkN = ROOT.RooRealVar("amp-" + name, "amp-" + name, pars[1], pars[2],
                              pars[3])
        bkDH = ROOT.RooDataHist("dh-" + name, "dh-" + name,
                                ROOT.RooArgList(hitE), RF.Import(hB))
        bkPDF = ROOT.RooHistPdf("pdf-" + name, "pdf-" + name,
                                ROOT.RooArgSet(hitE), bkDH, 2)
        bkExt = ROOT.RooExtendPdf("ext-" + name, "ext-" + name, bkPDF, bkN)
        hitE.setRange(eLo, eHi)
        bkgList.append([bkExt, name, bkN, bkDH, bkPDF])

    # this is separate b/c all the RooVars have to remain in memory
    for bkg in bkgList:
        pdfList.add(bkg[0])

    model = ROOT.RooAddPdf("model", "total PDF", pdfList)

    if makePlots:

        # === make a rooplot of the initial guess parameters, don't let roofit normalize automatically

        leg = TLegend(0.83, 0.5, 0.97, 0.9)
        gStyle.SetPalette(ROOT.kRainBow)
        nCol = float(gStyle.GetNumberOfColors())

        fSpec = hitE.frame(RF.Range(eLo, eHi), RF.Bins(nB))
        fData.plotOn(fSpec)

        # wouter's note: DON'T DELETE
        # "the default behavior is when you plot a p.d.f. on an empty frame it is
        # plotted with unit normalization. When you plot it on a frame with data in
        # it, it will normalize to the number of events in that dataset."
        # (then after you do a fit, the pdf normalization changes again ...)
        nData = fData.numEntries()

        nTot = 0
        for i, ext in enumerate(bkgList):
            extPDF, name = ext[0], ext[1]
            col = gStyle.GetColorPalette(int(nCol / len(bkgList) * i))
            extPDF.plotOn(
                fSpec, RF.LineColor(col),
                RF.Normalization(bkgVals[name][1], ROOT.RooAbsReal.Raw),
                RF.Name(name))
            leg.AddEntry(fSpec.findObject(name), name, "l")
            nTot += bkgVals[name][1]

        model.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("fmodel"),
                     RF.Normalization(nTot, ROOT.RooAbsReal.Raw))
        leg.AddEntry(fSpec.findObject("fmodel"), "Full Model", "l")

        c = TCanvas("c", "c", 1400, 1000)
        fSpec.SetTitle("")
        fSpec.Draw()
        leg.Draw("same")
        c.Print("%s/plots/sf-before.pdf" % dsi.latSWDir)
        c.Clear()

        # === make a pyplot of the same thing

        tCut = "isEnr" if enr else "!isEnr"
        tCut += " && trapENFCal >= %.1f && trapENFCal <= %.1f" % (eLo, eHi)
        n = tt.Draw("trapENFCal", tCut, "goff")
        trapE = tt.GetV1()
        trapE = [trapE[i] for i in range(n)]
        x, hData = wl.GetHisto(trapE, eLo, eHi, epb)
        hErr = np.asarray([np.sqrt(h) for h in hData])
        plt.errorbar(x,
                     hData,
                     yerr=hErr,
                     c='k',
                     ms=5,
                     linewidth=0.5,
                     fmt='.',
                     capsize=1,
                     zorder=1)  # pretty convincing rooplot fake

        cmap = plt.cm.get_cmap('jet', len(hList) + 2)
        pdfs = []
        for i, h in enumerate(hList):
            name = h[1]
            x, y, xpb = wl.npTH1D(h[0])
            x, y = normPDF(x, y, eLo, eHi)
            nCts = bkgVals[h[1]][1]  # the initial guess
            if abs(nCts - np.sum(y * nCts)) > 2:
                print("norm error, %s  nCts %d  y*nCts %d" %
                      (name, nCts, np.sum(y * nCts)))
            # plt.step(x, y * nCts / xpb, c=cmap(i), lw=2, label="%s init cts: %d" % (name, nCts)) # plot the histo

            xS = np.arange(eLo, eHi, 0.001)  # plot a smoothed version
            yS = spline(x - xpb / 2, y, xS)
            plt.plot(xS,
                     yS * nCts / xpb,
                     c=cmap(i),
                     lw=2,
                     label="%s init cts: %d" % (name, nCts))

            pdfs.append([x, y, xpb, nCts])

        xT, yT = getTotalModel(pdfs, eLo, eHi, epb, smooth=True)
        plt.step(xT,
                 yT / epb,
                 c='r',
                 lw=2,
                 label="Raw (no eff. corr): %d cts" % nTot)

        plt.xlabel("Energy (keV)", ha='right', x=1)
        plt.ylabel("Counts / %.1f keV" % epb, ha='right', y=1)
        plt.legend(loc=1, fontsize=12)
        plt.xlim(eLo, eHi)
        plt.ylim(ymin=0)
        plt.tight_layout()
        # plt.show()
        plt.savefig("%s/plots/sf-before-mpl.pdf" % dsi.latSWDir)

    # === alright, now run the fit and output to the workspace
    minimizer = ROOT.RooMinimizer(
        model.createNLL(fData, RF.NumCPU(2, 0), RF.Extended(True)))
    minimizer.setPrintLevel(-1)
    minimizer.setStrategy(2)
    minimizer.migrad()
    fitResult = minimizer.save()

    # according to the internet, covQual==3 is a good indicator that it converged
    print("Fitter is done. Fit Cov Qual:", fitResult.covQual())

    # save workspace to a TFile
    getattr(fitWorkspace, 'import')(fitResult)
    getattr(fitWorkspace, 'import')(model)
    tf3 = TFile("%s/data/fitWorkspace.root" % dsi.latSWDir, "RECREATE")
    fitWorkspace.Write()
    tf3.Close()
示例#3
0
def plotFit(plotRate=False):

    from ROOT import TFile, TCanvas, TH1D, TLegend, gStyle

    f = TFile("%s/data/fitWorkspace.root" % dsi.latSWDir)
    fitWorkspace = f.Get("fitWorkspace")
    fData = fitWorkspace.allData().front()
    fitResult = fitWorkspace.allGenericObjects().front()
    nPars = fitResult.floatParsFinal().getSize()
    hitE = fitWorkspace.var("trapENFCal")
    model = fitWorkspace.pdf("model")
    nData = fData.numEntries()
    fCov = fitResult.covQual()
    # fitWorkspace.Print()

    # === get fit results: {name : [nCts, err]} ===
    fitVals = {}
    for i in range(nPars):
        fp = fitResult.floatParsFinal()
        name = fp.at(i).GetName()
        fitVal, fitErr = fp.at(i).getValV(), fp.at(i).getError()
        if "amp" in name:
            fitVals[name.split('-')[1]] = [fitVal, fitErr]

    # for f in fitVals:
    # print(f, fitVals[f])

    # === make a rooplot of the fit ===

    leg = TLegend(0.83, 0.5, 0.97, 0.9)
    gStyle.SetPalette(ROOT.kRainBow)
    nCol = float(gStyle.GetNumberOfColors())

    fSpec = hitE.frame(RF.Range(eLo, eHi), RF.Bins(nB))

    fData.plotOn(fSpec)

    for i, name in enumerate(bkgModel):
        pdfName = "ext-" + name
        col = gStyle.GetColorPalette(int(nCol / len(bkgModel) * i))
        model.plotOn(fSpec, RF.Components(pdfName), RF.LineColor(col),
                     RF.LineStyle(ROOT.kDashed), RF.Name(name))
        leg.AddEntry(fSpec.findObject(name), name, "l")

    chiSquare = fSpec.chiSquare(nPars)
    model.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("fmodel"))
    leg.AddEntry(fSpec.findObject("fmodel"),
                 "Full Model, #chi^{2}/NDF = %.3f" % chiSquare, "l")

    c = TCanvas("c", "c", 1400, 1000)
    fSpec.SetTitle("")
    fSpec.Draw()
    leg.Draw("same")
    c.Print("%s/plots/sf-after.pdf" % dsi.latSWDir)
    c.Clear()

    # === duplicate the rooplot in matplotlib ===
    plt.close()

    tf = TFile("%s/data/latDS%s.root" %
               (dsi.latSWDir, ''.join([str(d) for d in dsList])))
    tt = tf.Get("skimTree")
    tCut = "isEnr==1" if enr is True else "isEnr==0"
    tCut = "isEnr" if enr else "!isEnr"
    tCut += " && trapENFCal >= %.1f && trapENFCal <= %.1f" % (eLo, eHi)
    n = tt.Draw("trapENFCal", tCut, "goff")
    trapE = tt.GetV1()
    trapE = [trapE[i] for i in range(n)]
    x, hData = wl.GetHisto(trapE, eLo, eHi, epb)

    if plotRate:
        hErr = np.asarray([np.sqrt(h) / detExp
                           for h in hData])  # statistical error
        plt.errorbar(x,
                     hData / detExp,
                     yerr=hErr,
                     c='k',
                     ms=5,
                     linewidth=0.5,
                     fmt='.',
                     capsize=1,
                     zorder=1)
    else:
        hErr = np.asarray([np.sqrt(h) for h in hData])  # statistical error
        plt.errorbar(x,
                     hData,
                     yerr=hErr,
                     c='k',
                     ms=5,
                     linewidth=0.5,
                     fmt='.',
                     capsize=1,
                     zorder=1)

    # get the list of histograms and plot the components
    hList = getHistList()
    cmap = plt.cm.get_cmap('jet', len(hList) + 2)
    pdfs, pdfsCorr, nTot, nTotC = [], [], 0, 0
    for i, h in enumerate(hList):
        name = h[1]
        x, y, xpb = wl.npTH1D(h[0])
        x, y = normPDF(x, y, eLo, eHi)
        nCts, nErr = fitVals[name]  # final result
        yc = nCts * getEffCorr(x, y, inv=True)
        if abs(nCts - np.sum(y * nCts)) > 2:
            print("norm error, %s  nCts %d  y*nCts %d" %
                  (name, nCts, np.sum(y * nCts)))
        # plt.step(x, y * nCts * (epb/xpb), c=cmap(i), lw=2, label="%s cts: %.2f±%.2f" % (name, nCts, nErr)) # plot the histo

        xS = np.arange(eLo, eHi, 0.001)  # plot a smoothed version
        yS = spline(x - xpb / 2, y, xS)
        if plotRate:
            plt.plot(xS,
                     yS * nCts * (epb / xpb) / detExp,
                     "--",
                     c=cmap(i),
                     lw=2,
                     label="%s %.2f ± %.2f" %
                     (name, nCts / detExp, nErr / detExp))
        else:
            plt.plot(xS,
                     yS * nCts * (epb / xpb),
                     c=cmap(i),
                     lw=2,
                     label="%s cts: %d" % (name, nCts))

        pdfs.append([x, y, xpb, nCts])
        pdfsCorr.append([x, yc, xpb, nCts])
        nTot += nCts
        nTotC += np.sum(
            yc
        )  # reverse the efficiency correction to get the "true" number of counts

    # get the fit model, and the efficiency-corrected final model
    xT, yT = getTotalModel(pdfs, eLo, eHi, epb, smooth=True)
    xTc, yTc = getTotalModel(pdfsCorr, eLo, eHi, epb, smooth=True, amp=False)

    if plotRate:
        plt.plot(xT,
                 yT / detExp,
                 'r',
                 lw=2,
                 alpha=0.7,
                 label="Rate: %.2f" % (nTot / detExp))
        plt.plot(xTc,
                 yTc / detExp,
                 c='m',
                 lw=3,
                 alpha=0.7,
                 label="Eff.corr: %.2f " % (nTotC / detExp))
        plt.ylabel("Counts / keV / kg-d", ha='right', y=1)
    else:
        plt.plot(xT,
                 yT,
                 'r',
                 lw=2,
                 alpha=0.7,
                 label="Raw (no eff. corr): %d cts" % nTot)
        plt.plot(xTc,
                 yTc,
                 c='m',
                 lw=3,
                 alpha=0.7,
                 label="Efficiency corrected: %d cts" % nTotC)
        plt.ylabel("Counts / %.1f keV" % epb, ha='right', y=1)

    plt.xlabel("Energy (keV)", ha='right', x=1)
    plt.legend(loc=1, fontsize=12)
    plt.xticks(np.arange(int(eLo) - 1, eHi + 1, 5))
    plt.xlim(eLo, eHi)
    plt.ylim(ymin=0)
    plt.tight_layout()
    # plt.show()
    plt.savefig("%s/plots/sf-after-mpl.pdf" % dsi.latSWDir)
示例#4
0
def plotFit():

    axPeak = 2.464

    # load workspace
    f = TFile("./data/splitWorkspace.root")
    fitWorkspace = f.Get("fitWorkspace")
    fData = fitWorkspace.allData().front()
    fitResult = fitWorkspace.allGenericObjects().front()
    nPars = fitResult.floatParsFinal().getSize()
    fEnergy = fitWorkspace.var("energy_keV")
    modelPDF = fitWorkspace.pdf("model")
    # fitWorkspace.Print()

    pdfNames = ["ext-axion"]

    # -- create spectrum rooplot --
    nCol = float(gStyle.GetNumberOfColors())
    binSize = 0.04
    nBins = int((eHi - eLo) / binSize + 0.5)
    fSpec = fEnergy.frame(RF.Range(eLo, eHi), RF.Bins(nBins))
    fData.plotOn(fSpec)
    modelPDF.plotOn(fSpec, RF.LineColor(ROOT.kRed), RF.Name("FullModel"))
    chiSquare = fSpec.chiSquare(nPars)
    modelPDF.plotOn(fSpec, RF.Components(pdfNames[0]),
                    RF.LineColor(ROOT.kBlue), RF.Name(pdfNames[0]))

    # -- make a fake Gaussian --
    # gaus = ROOT.TF1("g","gaus",-3, eHi)
    # gaus.SetParameters(10., axPeak, getSigma(axPeak))
    # gaus.SetParameter(0,10.)
    # gaus.SetParameter(1, axPeak)
    # gaus.SetParameter(2, getSigma(axPeak))
    # erf
    # rrvGaus = ROOT.RooRealVar("ax","ax",-3.,3.)
    # rarGaus = RF.bindFunction("gaus", ROOT.TMath.Erf, rrvGaus)
    # rarGaus.Print()
    # frame2 = rrvGaus.frame(RF.Title("mygaus"))
    # rarGaus.plotOn(frame2, RF.LineColor(ROOT.kGreen), RF.LineStyle(ROOT.kDashed), RF.Name("axGaus"))
    # c0 = TCanvas("c0","",800,600)
    # frame2.Draw()
    # c0.Print("./plots/testGaus.pdf")

    # -- print fit results --
    print "-- SHIFTFIT RESULTS -- "
    print "%-10s = %.3f" % ("chiSq", chiSquare)
    fitValsFinal = getRooArgDict(fitResult.floatParsFinal())

    bkgVal = 0.
    for name in sorted(fitValsFinal):
        fitVal = fitValsFinal[name]
        if "amp-" in name:
            error = fitWorkspace.var(name).getError()
            print "%-10s = best %-7.3f  error %.3f (w/o profile)" % (
                name, fitVal, error)
        elif "mu-" in name:
            # compare the energy offset
            pkName = name[3:]
            pct = 100 * (1 - fitVal / axPeak)
            print "%-10s : fit %-6.3f  lit %-6.3f  (%.3f%%)" % (name, fitVal,
                                                                axPeak, pct)
        elif "sig-" in name:
            # compare the sigma difference
            pkName = name[4:]
            pct = 100 * (1 - fitVal / getSigma(axPeak))
            print "%-10s : fit %-6.3f  func %-6.3f  (%.3f%%)" % (
                name, fitVal, getSigma(axPeak), pct)
        else:
            print "%s = %.4f (%.4f)" % (name, fitVal, fitVal / nBins)
            bkgVal = fitVal / nBins
            continue

    # -- make spectrum plot --
    c = TCanvas("c", "Bob Ross's Canvas", 1400, 1000)
    c.SetRightMargin(0.2)
    fSpec.SetTitle(" ")
    fSpec.Draw()

    ymax = fSpec.GetMaximum()
    l1 = ROOT.TLine(axPeak, 0., axPeak, ymax)
    l1.SetLineColor(ROOT.kBlue)
    l1.SetLineWidth(2)
    l1.Draw("same")

    leg = TLegend(0.83, 0.6, 0.97, 0.9)
    leg.AddEntry(fSpec.findObject("FullModel"),
                 "model #chi^{2}=%.3f" % chiSquare, "l")
    leg.AddEntry(fSpec.findObject("FullModel"), "cts/bin=%.2f" % bkgVal, "")
    leg.AddEntry(fSpec.findObject(pdfNames[0]), "axion gaussian", "l")
    leg.AddEntry(l1, "axion-%.2f" % axPeak, "l")
    leg.Draw("same")

    c.Print("./plots/shiftFit.pdf")

    # -- get FC Limit --
    # Calculate the confidence interval for the axion peak, which is too low to use profile likelihood.
    # TFeldmanCousins version:
    # https://root.cern.ch/root/html/tutorials/math/FeldmanCousins.C.html
    # RooStats version:
    # https://root.cern.ch/root/html/tutorials/roostats/StandardFeldmanCousinsDemo.C.html
    # FC Paper: https://arxiv.org/pdf/physics/9711021.pdf
    f = TFeldmanCousins(0.95)
    N_obs = 1.
    N_bkg = bkgVal / 3.
    ul = f.CalculateUpperLimit(N_obs, N_bkg)
    ll = f.GetLowerLimit()
    print "For %.2f events observed, and %.2f background events," % (N_obs,
                                                                     N_bkg)
    print "F-C method gives UL: %.2f and LL %.2f (90%% CL)" % (ul, ll)