def buildCorrectionHisto(
    hndRealDataCr,
    hndFakeDataLo,
    hndFakeDataHi,
    hndFakeMcLo,
    hndFakeMcHi,
    histoname="lep_corHFRate",
    nIter=1,
    verbose=False,
    plotdir=None,
):
    hRealEff = buildRatioHistogram(hndRealDataCr["num"], hndRealDataCr["den"], "real_eff")
    corrected = dict([(nd, hndFakeDataLo[nd].Clone("corrected_" + nd)) for nd in ["num", "den"]])
    print "buildCorrectionHisto with nIter ", nIter
    can = r.TCanvas("c_" + histoname, "")
    can.Draw()
    can.cd()
    hRealEff.GetYaxis().SetRangeUser(0.0, 1.0)
    hRealEff.Draw("axis")
    tex = r.TLatex()
    tex.SetNDC(True)
    tex.DrawLatex(0.15, 0.915, histoname)
    can._tex = tex
    can._histos = [hRealEff]
    can._leg = r.TLegend(0.925, 0.25, 1.0, 0.9, "iter")
    can._leg.SetBorderSize(0)
    can._leg.SetFillColor(0)
    for iteration in range(nIter):
        print "iter ", iteration
        rate = buildRatioHistogram(corrected["num"], corrected["den"])  # temporary rate (?)
        if verbose:
            print "Iteration %d, corrected values:" % iteration
            print "  num   %s" % lf2s(getBinContents(corrected["num"]))
            print "  den   %s" % lf2s(getBinContents(corrected["den"]))
            print "  ratio %s" % lf2s(getBinContents(rate))
        dataNum, dataDen = hndFakeDataHi["num"], hndFakeDataHi["den"]
        for nd, tl in [("num", "tight"), ("den", "loose")]:
            corr, dataLow = corrected[nd], hndFakeDataLo[nd]
            mcLow, mcHi = hndFakeMcLo[nd], hndFakeMcHi[nd]
            corrFact = getCorrFactors(hRealEff, rate, dataNum, dataDen, mcHi, tl)
            corr = correctRate(corr, dataLow, mcLow, corrFact)
        rate.SetLineColor(20 + iteration)
        rate.SetMarkerColor(20 + iteration)
        rate.SetLineWidth(4)
        rate.SetMarkerStyle(r.kFullCross)
        can._histos.append(rate.DrawClone("el same"))
        can._leg.AddEntry(can._histos[-1], "%d" % iteration, "lp")
    ratio = buildRatioHistogram(corrected["num"], corrected["den"], histoname)
    can._leg.Draw()
    can.Update()
    if plotdir:
        can.SaveAs(plotdir + "/" + histoname + "_iterations.png")
    return ratio
def computeAndPlotHfSf2d(fileIter, fileHf, lepton, variable_name, outdir) :
    eff_da = fileIter.Get(lepton+'_corHFRate_eta')
    eff_mc = buildRate(fileHf, lepton+'_fakeHF_all_l_pt_eta')
    ratio = buildRatioHistogram(eff_da, eff_mc)
    ratio.Print()
    xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis()
    print ratio.GetName(),": bins (%d, %d)"%(ratio.GetNbinsX(), ratio.GetNbinsY())
    nEtaBins = yAx.GetNbins()
    print 'nEtaBins: ',nEtaBins
    xMin, xMax = xAx.GetXmin(), xAx.GetXmax()
    fitFunc = r.TF1('fit_func_const_'+ratio.GetName(), '[0]', xMin, xMax)
    etaBins = range(1, 1+nEtaBins)
    slices = [ratio.ProjectionX("%s_bin%d"%(ratio.GetName(), b), b, b, 'e') for b in etaBins]
    for b, s in zip(etaBins, slices) :
        s.SetTitle(lepton+" data/mc heavyflavor : eta bin %d"%b)
        s.Fit(fitFunc.GetName(), '0RQ') # do not draw, range, quiet
        p0, p0Err, chi2, ndf = fitResults(fitFunc)
        p0, p0Err = pdgRound(p0, p0Err)
        print "bin %d :  %s +/- %s"%(b, p0, p0Err)
        can = r.TCanvas('')
        s.Draw('ep')
        fitFunc.Draw('same')
        tex = r.TLatex()
        tex.SetNDC(True)
        fitParLabel = "Const. fit : %s #pm %s"%(p0, p0Err)
        fitGoodLabel = "#chi^{2}/DOF : %.2f / %d"%(chi2, ndf)
        tex.SetTextSize(yAx.GetTitleSize())
        tex.SetTextFont(yAx.GetTitleFont())
        tex.DrawLatex(0.15, 0.45, s.GetTitle())
        tex.DrawLatex(0.15, 0.40, "#splitline{%s}{%s}"%(fitParLabel, fitGoodLabel))
        can.Update()
        for ext in ['eps','png'] : can.SaveAs(outdir+'/fit_'+lepton+"_heavyflavor_etabin%d.%s"%(b, ext))
    return p0
def buildSideBandSubRate(file, lepton, variable_name):
    hZwindow = getNumDenHistos(file, lepton + '_realCR_' + variable_name)
    hSideLo = getNumDenHistos(file, lepton + '_realSideLow_' + variable_name)
    hSideHi = getNumDenHistos(file, lepton + '_realSideHigh_' + variable_name)

    def sbErr(el, ew, eh):
        "SidebandError; DG this doesn't make any sense to me; ask Matt"
        return sqrt(ew * ew - (el * el + eh * eh))

    def be(h):
        return [h.GetBinError(b) for b in range(1, 1 + h.GetNbinsX())]

    errs = dict([(k, [
        sbErr(l, w, h)
        for l, w, h in zip(be(hSideLo[k]), be(hZwindow[k]), be(hSideHi[k]))
    ]) for k in ['num', 'den']])
    hSideLo['num'].Add(hSideHi['num'])
    hSideLo['den'].Add(hSideHi['den'])
    hZwindow['num'].Add(hSideLo['num'], -1)
    hZwindow['den'].Add(hSideLo['den'], -1)
    for nd in ['num', 'den']:
        h, err = hZwindow[nd], errs[nd]
        nbins = h.GetNbinsX()
        for i, b in zip(range(nbins), range(1, 1 + nbins)):
            h.SetBinError(b, err[i])
    return buildRatioHistogram(hZwindow['num'], hZwindow['den'])
def computeAndPlotConvSf(fileData, fileMc, lepton, variable_name, outdir):
    "Electron conversion: simplest case, just data/mc"
    eff_da = buildRate(fileData, lepton + '_fakeConv_' + variable_name)
    eff_mc = buildRate(fileMc, lepton + '_fakeConv_' + variable_name)
    ratio = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s fake conv : %s +/- %s" % (lepton, p0, p0Err)
    graphics = {
        'xtitle': xTitle(lepton, variable_name),
        'ytitle': lepton + ' p(tight | fake conv)',
        'colors': {
            'data': r.kBlack,
            'mc': mcColor(lepton)
        },
        'markers': {
            'data': r.kFullCircle,
            'mc': mcMarker(lepton)
        },
        'labels': {
            'data': 'Data: Conversion CR',
            'mc': 'MC Comb: Conv CR'
        }
    }
    plotHistRatioAndFit({
        'data': eff_da,
        'mc': eff_mc
    }, ratio, fitFunc, outdir + lepton + '_fakeconv', graphics)
def computeAndPlotRealSf(file_data, file_mc, lepton, variable_name, outdir):
    "Scale factor from the real control region, Z tag and probe"
    eff_da = buildSideBandSubRate(file_data, lepton, variable_name)
    eff_mc = buildSideBandSubRate(file_mc, lepton, variable_name)
    ratio = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s real : %s +/- %s" % (lepton, p0, p0Err)
    graphics = {
        'xtitle': xTitle(lepton, variable_name),
        'ytitle': lepton + ' p(tight | real)',
        'colors': {
            'data': r.kBlack,
            'mc': mcColor(lepton)
        },
        'markers': {
            'data': r.kFullCircle,
            'mc': mcMarker(lepton)
        },
        'labels': {
            'data': 'Data: Z Tag and Probe',
            'mc': 'MC Comb: Z Tag and Probe'
        }
    }
    plotHistRatioAndFit({
        'data': eff_da,
        'mc': eff_mc
    }, ratio, fitFunc, outdir + lepton + '_real', graphics)
def drawBot(pad, histo_data, histo_mc, err_band_r, xaxis_title='') :
    pad.Draw()
    pad.cd()
    ratio = buildRatioHistogram(histo_data, histo_mc)
    yMin, yMax = 0.0, 2.0
    ratio.SetMinimum(yMin)
    ratio.SetMaximum(yMax)
    ratio.SetStats(0)
    ratio.Draw('axis')
    x_lo, x_hi = getXrange(ratio)
    refLines = [referenceLine(x_lo, x_hi, y, y) for y in [0.5, 1.0, 1.5]]
    for l in refLines : l.Draw()
    err_band_r.Draw('E2 same')
    ratio.Draw('ep same')
    xA, yA = ratio.GetXaxis(), ratio.GetYaxis()
    textScaleUp = 1.0/pad.GetHNDC()
    if xaxis_title : xA.SetTitle(xaxis_title)
    yA.SetNdivisions(-104)
    yA.SetTitle('Data/SM')
    yA.CenterTitle()
    yA.SetTitleOffset(yA.GetTitleOffset()/textScaleUp)
    for a in [xA, yA] :
        a.SetLabelSize(a.GetLabelSize()*textScaleUp)
        a.SetTitleSize(a.GetTitleSize()*textScaleUp)
    pad._graphical_objects = [ratio, err_band_r] + refLines # avoid garbage collection
    pad.Update()
def computeAndPlotHfSf(fileIter, fileHf, lepton, variable_name, outdir):
    "HF tag and probe; in this case we need to subract out the contamination"
    eff_da = fileIter.Get(lepton + '_corHFRate')
    eff_mc = buildRate(fileHf, lepton + '_fakeHF_' + variable_name)
    ratio = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s fake HF : %s +/- %s" % (lepton, p0, p0Err)
    graphics = {
        'xtitle': xTitle(lepton, variable_name),
        'ytitle': lepton + ' p(tight | fake hf)',
        'colors': {
            'data': r.kBlack,
            'mc': mcColor(lepton)
        },
        'markers': {
            'data': r.kFullCircle,
            'mc': mcMarker(lepton)
        },
        'labels': {
            'data': 'Data HF Tag and Probe (Iterative Subtraction)',
            'mc': 'b#bar{b}/c#bar{c} MC: HF Tag and Probe'
        }
    }
    plotHistRatioAndFit({
        'data': eff_da,
        'mc': eff_mc
    }, ratio, fitFunc, outdir + lepton + '_fakehf', graphics)
def drawBot(pad, histo_data, histo_mc, err_band_r, xaxis_title='') :
    pad.Draw()
    pad.cd()
    ratio = buildRatioHistogram(histo_data, histo_mc)
    yMin, yMax = 0.0, 2.0
    ratio.SetMinimum(yMin)
    ratio.SetMaximum(yMax)
    ratio.SetStats(0)
    ratio.Draw('axis')
    x_lo, x_hi = getXrange(ratio)
    refLines = [referenceLine(x_lo, x_hi, y, y) for y in [0.5, 1.0, 1.5]]
    for l in refLines : l.Draw()
    err_band_r.Draw('E2 same')
    ratio.Draw('ep same')
    xA, yA = ratio.GetXaxis(), ratio.GetYaxis()
    textScaleUp = 1.0/pad.GetHNDC()
    if xaxis_title : xA.SetTitle(xaxis_title)
    yA.SetNdivisions(-104)
    yA.SetTitle('Data/SM')
    yA.CenterTitle()
    yA.SetTitleOffset(yA.GetTitleOffset()/textScaleUp)
    for a in [xA, yA] :
        a.SetLabelSize(a.GetLabelSize()*textScaleUp)
        a.SetTitleSize(a.GetTitleSize()*textScaleUp)
    pad._graphical_objects = [ratio, err_band_r] + refLines # avoid garbage collection
    pad.Update()
def computeAndPlotRealSf(file_data, file_mc, lepton, variable_name, outdir) :
    "Scale factor from the real control region, Z tag and probe"
    eff_da = buildSideBandSubRate(file_data, lepton, variable_name)
    eff_mc = buildSideBandSubRate(file_mc,   lepton, variable_name)
    ratio = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s real : %s +/- %s"%(lepton, p0, p0Err)
    graphics = {'xtitle' : xTitle(lepton, variable_name),
                'ytitle' : lepton+' p(tight | real)',
                'colors' : {'data' : r.kBlack, 'mc' : mcColor(lepton)},
                'markers': {'data' : r.kFullCircle, 'mc' : mcMarker(lepton)},
                'labels' : {'data' : 'Data: Z Tag and Probe',
                            'mc'   : 'MC Comb: Z Tag and Probe'}}
    plotHistRatioAndFit({'data':eff_da, 'mc':eff_mc}, ratio, fitFunc, outdir+lepton+'_real',
                        graphics)
def computeAndPlotConvSf(fileData, fileMc, lepton, variable_name, outdir) :
    "Electron conversion: simplest case, just data/mc"
    eff_da = buildRate(fileData, lepton+'_fakeConv_'+variable_name)
    eff_mc = buildRate(fileMc,   lepton+'_fakeConv_'+variable_name)
    ratio  = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s fake conv : %s +/- %s"%(lepton, p0, p0Err)
    graphics = {'xtitle' : xTitle(lepton, variable_name),
                'ytitle' : lepton+' p(tight | fake conv)',
                'colors' : {'data' : r.kBlack, 'mc' : mcColor(lepton)},
                'markers': {'data' : r.kFullCircle, 'mc' : mcMarker(lepton)},
                'labels' : {'data' : 'Data: Conversion CR',
                            'mc'   : 'MC Comb: Conv CR'}}
    plotHistRatioAndFit({'data':eff_da, 'mc':eff_mc}, ratio, fitFunc, outdir+lepton+'_fakeconv',
                        graphics)
def computeAndPlotHfSf(fileIter, fileHf, lepton, variable_name, outdir) :
    "HF tag and probe; in this case we need to subract out the contamination"
    eff_da = fileIter.Get(lepton+'_corHFRate')
    eff_mc = buildRate(fileHf, lepton+'_fakeHF_'+variable_name)
    ratio = buildRatioHistogram(eff_da, eff_mc)
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s fake HF : %s +/- %s"%(lepton, p0, p0Err)
    graphics = {'xtitle' : xTitle(lepton, variable_name),
                'ytitle' : lepton+' p(tight | fake hf)',
                'colors' : {'data' : r.kBlack, 'mc' : mcColor(lepton)},
                'markers': {'data' : r.kFullCircle, 'mc' : mcMarker(lepton)},
                'labels' : {'data' : 'Data HF Tag and Probe (Iterative Subtraction)',
                            'mc'   : 'b#bar{b}/c#bar{c} MC: HF Tag and Probe'}}
    plotHistRatioAndFit({'data':eff_da, 'mc':eff_mc}, ratio, fitFunc, outdir+lepton+'_fakehf',
                        graphics)
def computeAndPlotConvSf2d(fileData, fileMc, lepton, variable_name, outdir) :
    "Electron conversion: simplest case, just data/mc"
    histoname = 'elec_fakeConv_all_l_pt_eta'
    eff_da = buildRate(fileData, histoname)
    eff_mc = buildRate(fileMc,   histoname)
    print 'SF conversion: using histo ',histoname
    ratio  = buildRatioHistogram(eff_da, eff_mc)
    ratio.Print()
    xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis()
    #pt_eta : check that x is pt, y is eta
    nEtaBins = yAx.GetNbins()
    xMin, xMax = xAx.GetXmin(), xAx.GetXmax()
    fitFunc = r.TF1('fit_func_const_'+ratio.GetName(), '[0]', xMin, xMax)
    etaBins = range(1, 1+nEtaBins)
    slices = [ratio.ProjectionX("%s_bin%d"%(ratio.GetName(), b), b, b, 'e') for b in etaBins]
    for b, s in zip(etaBins, slices) :
        s.SetTitle("data/mc conversion : eta bin %d"%b)
        s.Fit(fitFunc.GetName(), '0RQ') # do not draw, range, quiet
        p0, p0Err, chi2, ndf = fitResults(fitFunc)
        p0, p0Err = pdgRound(p0, p0Err)
        print "bin %d :  %s +/- %s"%(b, p0, p0Err)
        can = r.TCanvas('')
        s.Draw('ep')
        fitFunc.Draw('same')
        tex = r.TLatex()
        tex.SetNDC(True)
        fitParLabel = "Const. fit : %s #pm %s"%(p0, p0Err)
        fitGoodLabel = "#chi^{2}/DOF : %.2f / %d"%(chi2, ndf)
        tex.SetTextSize(yAx.GetTitleSize())
        tex.SetTextFont(yAx.GetTitleFont())
        tex.DrawLatex(0.15, 0.45, s.GetTitle())
        tex.DrawLatex(0.15, 0.40, "#splitline{%s}{%s}"%(fitParLabel, fitGoodLabel))
        can.Update()
        for ext in ['eps','png'] : can.SaveAs(outdir+"/fit_el_conv_etabin%d.%s"%(b, ext))
#     graphics = {'xtitle' : xTitle(lepton, variable_name),
#                 'ytitle' : lepton+' p(tight | fake conv)',
#                 'colors' : {'data' : r.kBlack, 'mc' : mcColor(lepton)},
#                 'markers': {'data' : r.kFullCircle, 'mc' : mcMarker(lepton)},
#                 'labels' : {'data' : 'Data: Conversion CR',
#                             'mc'   : 'MC Comb: Conv CR'}}
#     plotHistRatioAndFit({'data':eff_da, 'mc':eff_mc}, ratio, fitFunc, outdir+lepton+'_fakeconv',
#                         graphics)
    return p0
def computeAndPlotConvSf(fileData, fileMc, lepton, variable_name, outdir, outfile=None) :
    "Electron conversion: simplest case, just data/mc"
    eff_da = buildRate(fileData, lepton+'_fakeConv_'+variable_name)
    eff_mc = buildRate(fileMc,   lepton+'_fakeConv_'+variable_name)
    ratio  = buildRatioHistogram(eff_da, eff_mc)
    print ratio.GetName(),' : ',["%.3f"%ratio.GetBinContent(b) for b in range(1, 1+ratio.GetNbinsX())]
    fitFunc = fitWithConst(ratio)
    p0, p0Err, chi2, ndf = fitResults(fitFunc)
    p0, p0Err = pdgRound(p0, p0Err)
    print "SF for %s fake conv : %s +/- %s"%(lepton, p0, p0Err)
    graphics = {'xtitle' : xTitle(lepton, variable_name),
                'ytitle' : lepton+' p(tight | fake conv)',
                'colors' : {'data' : r.kBlack, 'mc' : mcColor(lepton)},
                'markers': {'data' : r.kFullCircle, 'mc' : mcMarker(lepton)},
                'labels' : {'data' : 'Data: Conversion CR',
                            'mc'   : 'MC Comb: Conv CR'}}
    plotHistRatioAndFit({'data':eff_da, 'mc':eff_mc}, ratio, fitFunc, outdir+'/fit_'+lepton+'_conv', graphics)
    if outfile : saveObject(outfile, ratio, 'elec_convSF_pt')
    return p0
def buildRatioHistos(histosNum={}, histosDen={}) :
    "assume that the histos are organized in two dict[vt][sample] provided by buildHistos"
    histosPerVtypePerSample = {}
    def sameLists(l1=[], l2=[]) : return len(l1)==len(l2) and sorted(l1)==sorted(l2)
    assert sameLists(histosNum.keys(), histosDen.keys()),"num and den w/ different vtype keys"
    result = {}
    for vt in histosDen.keys() :
        resultPerSample = {}
        hvn, hvd = histosNum[vt], histosDen[vt]
        assert sameLists(hvn.keys(), hvd.keys()),"num and den w/ different sample keys"
        for s in hvd.keys() :
            hnum, hden = hvn[s], hvd[s]
            hNames = [h.GetName() for h in hnum, hden]
            pre, suf = commonPrefix(hNames), commonSuffix(hNames)
            hName = pre+'_ratio_'+suf if len(pre) and len(suf) else '_over_'.join(hNames)
            h = buildRatioHistogram(hnum, hden, hName)
            resetErrors(h)
            resultPerSample[s] = h
        result[vt] = resultPerSample
    return result
def buildRatioHistos(histosNum={}, histosDen={}) :
    "assume that the histos are organized in two dict[vt][sample] provided by buildHistos"
    histosPerVtypePerSample = {}
    def sameLists(l1=[], l2=[]) : return len(l1)==len(l2) and sorted(l1)==sorted(l2)
    assert sameLists(histosNum.keys(), histosDen.keys()),"num and den w/ different vtype keys"
    result = {}
    for vt in histosDen.keys() :
        resultPerSample = {}
        hvn, hvd = histosNum[vt], histosDen[vt]
        assert sameLists(hvn.keys(), hvd.keys()),"num and den w/ different sample keys"
        for s in hvd.keys() :
            hnum, hden = hvn[s], hvd[s]
            hNames = [h.GetName() for h in hnum, hden]
            pre, suf = commonPrefix(hNames), commonSuffix(hNames)
            hName = pre+'_ratio_'+suf if len(pre) and len(suf) else '_over_'.join(hNames)
            h = buildRatioHistogram(hnum, hden, hName)
            resetErrors(h)
            resultPerSample[s] = h
        result[vt] = resultPerSample
    return result
def buildSideBandSubRate(file, lepton, variable_name) :
    hZwindow = getNumDenHistos(file, lepton+'_realCR_'      +variable_name)
    hSideLo  = getNumDenHistos(file, lepton+'_realSideLow_' +variable_name)
    hSideHi  = getNumDenHistos(file, lepton+'_realSideHigh_'+variable_name)
    def sbErr(el, ew, eh) :
        "SidebandError; DG this doesn't make any sense to me; ask Matt"
        return sqrt(ew*ew - (el*el + eh*eh))
    def be(h) : return [h.GetBinError(b) for b in range(1, 1+h.GetNbinsX())]
    errs = dict([(k, [sbErr(l,w,h)
                      for l, w, h in zip(be(hSideLo[k]), be(hZwindow[k]), be(hSideHi[k]))])
                 for k in ['num','den']])
    hSideLo ['num'].Add(hSideHi['num'])
    hSideLo ['den'].Add(hSideHi['den'])
    hZwindow['num'].Add(hSideLo['num'], -1)
    hZwindow['den'].Add(hSideLo['den'], -1)
    for nd in ['num', 'den'] :
        h, err = hZwindow[nd], errs[nd]
        nbins = h.GetNbinsX()
        for i, b in  zip(range(nbins), range(1, 1+nbins)) :
            h.SetBinError(b, err[i])
    return buildRatioHistogram(hZwindow['num'], hZwindow['den'])
def buildRatio(inputFile=None, histoBaseName='') :
    num, den = inputFile.Get(histoBaseName+'_num'), inputFile.Get(histoBaseName+'_den')
    return buildRatioHistogram(num, den, histoBaseName +'_rat')
def plotHistosRatio(histosPairs=[], canvasName='') :
    histosRatio = [buildRatioHistogram(hh['num'], hh['den']) for hh in histosPairs]
    plotHistos(histosRatio, canvasName)
def main() :
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('-n', '--n_iter', type='int', default=8)
    parser.add_option('-m', '--input_mc')
    parser.add_option('-d', '--input_data')
    parser.add_option('-o', '--output')
    parser.add_option('-p', '--plot', help='plot inputs') # todo: implement sanity plot vs. n_iter
    parser.add_option('-v','--verbose', action='store_true', default=False)
    (opts, args) = parser.parse_args()
    requiredOptions = ['n_iter', 'input_mc', 'input_data', 'output']
    otherOptions = ['plot', 'verbose']
    allOptions = requiredOptions + otherOptions
    def optIsNotSpecified(o) : return not hasattr(opts, o) or getattr(opts,o) is None
    if any(optIsNotSpecified(o) for o in requiredOptions) : parser.error('Missing required option')
    nIter        = opts.n_iter
    fnameInputMc = opts.input_mc
    fnameInputDa = opts.input_data
    fnameOutput  = opts.output
    plot         = opts.plot
    verbose      = opts.verbose
    if verbose : print ('\nUsing the following options:\n'
                        +'\n'.join("%s : %s"%(o, str(getattr(opts, o))) for o in allOptions))
    fileData = r.TFile.Open(fnameInputDa)
    fileMc   = r.TFile.Open(fnameInputMc)
    assert fileData and fileMc, "Missing input files: data %s, mc %s"%(str(fileData), str(fileMc))
    correctionHistos = {}
    for lep in ['muon', 'elec'] :
        if verbose : print "Lepton: %s"%lep
        hRealDataCr = getNumDenHistos(fileData, lep+'_realCR_all_l_pt')
        hFakeDataLo = getNumDenHistos(fileData, lep+'_fakeHF_all_l_pt')
        hFakeDataHi = getNumDenHistos(fileData, lep+'_fakeHF_high_all_l_pt')
        hFakeMcLo   = getNumDenHistos(fileMc,   lep+'_fakeHF_all_l_pt')
        hFakeMcHi   = getNumDenHistos(fileMc,   lep+'_fakeHF_high_all_l_pt')
        if plot :
            hNumDen = [hFakeDataLo, hFakeDataHi, hFakeMcLo, hFakeMcHi]
            for nd in ['num','den'] : plotHistos([h[nd] for h in hNumDen], 'c_'+lep+'_'+nd)
            plotHistosRatio(hNumDen, 'c_'+lep+'_ratio')
        def missingInputHisto(ndHistos) : return any(not h for h in ndHistos.values())
        histoCollToBeChecked = ['hRealDataCr','hFakeDataLo','hFakeDataHi','hFakeMcLo','hFakeMcHi']
        missingHistos = dict([(nhc,hp) for nhc,hp in [(hc, eval(hc)) for hc in histoCollToBeChecked]
                              if missingInputHisto(hp)])
        if len(missingHistos) :
            print (lep+' : missing histograms: \n'
                   +'\n'.join(["%s: num %s den %s"%(k, v['num'], v['den'])
                               for k,v in missingHistos.iteritems()]))
            continue
        hRealEff = buildRatioHistogram(hRealDataCr['num'], hRealDataCr['den'], 'real_eff')
        corrected = dict([(nd, hFakeDataLo[nd].Clone('corrected_'+nd)) for nd in ['num', 'den']])
        for iteration in range(nIter) :
            rate = buildRatioHistogram(corrected['num'], corrected['den']) # temporary rate (?)
            if verbose :
                def lf2s(l) : return ', '.join(["%.3f"%e for e in l])
                print "Iteration %d, corrected values:"%iteration
                print "  num   %s"%lf2s(binContents(corrected['num']))
                print "  den   %s"%lf2s(binContents(corrected['den']))
                print "  ratio %s"%lf2s(binContents(rate))
            dataNum, dataDen = hFakeDataHi['num'], hFakeDataHi['den']
            for nd,tl in [('num','tight'), ('den','loose')] :
                corr, dataLow = corrected[nd], hFakeDataLo[nd]
                mcLow, mcHi = hFakeMcLo[nd], hFakeMcHi[nd]
                corrFact = getCorrFactors(hRealEff, rate, dataNum, dataDen, mcHi, tl)
                corr = correctRate(corr, dataLow, mcLow, corrFact)
        ratio = buildRatioHistogram(corrected['num'], corrected['den'], lep+'_corHFRate')
        correctionHistos[lep] = ratio
    if verbose : print "saving output to ",fnameOutput
    fileOut = r.TFile.Open(fnameOutput, 'recreate')
    fileOut.cd()
    for l,h in correctionHistos.iteritems() :
        if verbose : print "%s : writing %s\n%s"%(l, h.GetName(),histo1dToTxt(h))
        h.Write()
    fileOut.Close()
def main() :
    parser = argparse.ArgumentParser(description=description,
                                     epilog=epilog,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    add_arg = parser.add_argument
    add_arg('-o', '--output-dir', default="./")
    add_arg('-p', '--process', help='one physics process, eg. ttw')
    add_arg('-s', '--systematic', help='one of the systematic variations')
    add_arg('-v', '--verbose', action='store_true')
    add_arg('-d', '--debug', action='store_true')
    args = parser.parse_args()

    set_log(args.verbose, args.debug)
    outdir = args.output_dir
    process = args.process
    systematic = args.systematic

    available_processes = get_input_samples().keys()
    if process not in available_processes:
        raise StandardError("invalid process %s, should be one of %s"%(process, str(available_processes)))
    available_systematics = get_input_samples()[process].keys()
    if systematic not in available_systematics:
        raise StandardError("invalid systematic %s, should be one of %s"%(systematic, str(available_systematics)))

    file_label = process+'_sys_'+systematic
    plot_label = process+' sys. '+systematic

    normalize_to_unity = False # True
    luminosity = 1.0

    combiner = HistogramCombiner()

    combiner.build_samples(process=process, systematic=systematic)
    histogram_names = ['h_meff', 'h_jetN',
                       # 'h_electronPt', 'h_muonPt',
                       # 'h_bjetEmulN', 'h_bjetN',
                       # 'h_bjetEmulN_sr3b', 'h_bjetN_sr3b',
                       # 'h_bjetEmulN_sr1b', 'h_bjetN_sr1b',
                       # 'h_bjetEmulN_cr2bttV', 'h_bjetN_cr2bttV',
                       # 'h_meff_sr3b',    'h_jetN_sr3b',    'h_met_sr3b',
                       # 'h_meff_sr1b',    'h_jetN_sr1b',    'h_met_sr1b',
                       # 'h_meff_sr0b5j',  'h_jetN_sr0b5j',  'h_met_sr0b5j',
                       # 'h_meff_sr0b4j',  'h_jetN_sr0b4j',  'h_met_sr0b4j',
                       # 'h_meff_sr0b3j',  'h_jetN_sr0b3j',  'h_met_sr0b3j',
                       # 'h_meff_cr2bttV', 'h_jetN_cr2bttV', 'h_met_cr2bttV'
                       ]

    combiner.compute_normalization_factors()
    output_pdf_name = outdir+'/'+file_label+'.pdf'
    c_summary = R.TCanvas('c_summary', 'plotExplicitSamples sampes summary ')
    combiner.print_sample_summary_to_pdf(c_summary, label="%s: nominal vs. %s systematic"%(process, systematic))
    c_summary.SaveAs(output_pdf_name+'(')


    for histogram_name in histogram_names:
        rebin = 'meff' in histogram_name # and '_sr' in histogram_name # non-inclusive histos: low stats
        rebin_factor = (2 if 'meff' in histogram_name else 2 if 'jetN' in histogram_name else 1) if rebin else 1
        histograms = combiner.get_histograms(histogram_name=histogram_name)
        h_nom = histograms['nominal']
        h_up  = histograms['up']
        h_dn  = histograms['down']
        if 'h_jetFlavorMultiplicity' in histogram_name:
            h_nom = emulate_btag_multiplicity_from_truth_flavor(h_nom, 'nom')
            h_up  = emulate_btag_multiplicity_from_truth_flavor(h_up, 'up')
            h_dn  = emulate_btag_multiplicity_from_truth_flavor(h_dn, 'dn')
        histos = [h_nom, h_up, h_dn]
        for h in set(histos): # set: avoid rebinning twice when up==down
            h.Rebin(rebin_factor)
        h_nom.SetLineWidth(2*h_nom.GetLineWidth())
        h_nom.SetLineColor(R.kBlack)
        h_up.SetLineColor(R.kBlue)
        h_dn.SetLineColor(R.kRed)

        pad_master = h_nom
        pad_master.SetMaximum(1.1*max([h.GetMaximum() for h in histos]))
        pad_master.SetMinimum(1.0*min([0.0]+[h.GetMinimum() for h in histos]))
        pad_master.GetYaxis().SetTitle('Arbitrary Units')
        pad_master.SetStats(0)
        can = R.TCanvas('c_ttV_syst_'+histogram_name, 'ttV explicit variations '+pad_master.GetTitle(), 700, 700)
        botPad, topPad = ru.buildBotTopPads(can, squeezeMargins=False)
        # top
        can.cd()
        topPad.Draw()
        topPad.cd()
        topPad._po = [pad_master] # persistent objects
        pad_master.GetXaxis().SetTitleSize(0)
        pad_master.GetXaxis().SetLabelSize(0)
        pad_master.Draw('axis')
        # ru.topRightLabel(topPad, pad_master.GetTitle(), xpos=0.5)
        ru.topRightLabel(topPad, "#bf{#it{ATLAS}} Simulation Preliminary", xpos=0.85, ypos=0.9)
        ru.topRightLabel(topPad, "#sqrt{s} = 13 TeV",                   xpos=0.85, ypos=0.8)

        leg = ru.topRightLegend(can, legWidth=0.225, legHeight=0.300, hShift=-0.10, vShift=-0.25)
        leg.SetBorderSize(0)
        # leg.SetHeader(plot_label+ ("(norm=1)" if normalize_to_unity else "(lumi %.1f)"%luminosity))
        topPad._po.append(leg)
        def format_legend_label(h, l):
            return "{0}: {1:.2E} ({2:.0f})".format(l, h.Integral(), h.GetEntries())
        def pretty_scale_legend_label(h, l):
            return ("nominal" if l is 'nom' else
                    "#mu = 2.0 #mu_{0}" if l is 'up' else
                    "#mu = 0.5 #mu_{0}" if l is 'dn' else
                    'unknown')
        for h,l in [(h_nom, 'nom'), (h_up, 'up'), (h_dn, 'dn')]:
            h.Draw('hist same')
            leg.AddEntry(h, pretty_scale_legend_label(h, l), 'l')
            topPad._po.append(h)
        leg.Draw('same')
        def integral_and_error(h):
            error = R.Double(0.0)
            integral = h.IntegralAndError(1, h.GetNbinsX()+1, error)
            return integral, error
        def ratio_and_error(ave=(1.0, 0.01), bve=(2.0, 0.001)):
            a, sa = ave
            b, sb = bve
            if a and b:
                r = a/b
                e = r * sqrt((sa/a)*(sa/a)+(sb/b)*(sb/b))
                return r, e
            else:
                return 0.0, 0.0
        print_normalization_summary = histogram_name.startswith('h_meff')
        if print_normalization_summary:
            nom_int = h_nom.Integral()
            up_int = h_up.Integral()
            dn_int = h_dn.Integral()
            nom_int, nom_err = integral_and_error(h_nom)
            up_int, up_err = integral_and_error(h_up)
            dn_int, dn_err = integral_and_error(h_dn)
            rup, rupe = ratio_and_error((up_int, up_err), (nom_int, nom_err))
            rdn, rdne = ratio_and_error((dn_int, dn_err), (nom_int, nom_err))
            # print ("normalization change: "
            #        +"{} up {:.1%} down {:.1%} (nom {:.1f}, up {:.1f}, do {:.1f})".format(h_nom.GetName(),
            #                                                                           1.0-up_int/nom_int if nom_int else 1.0,
            #                                                                           1.0-dn_int/nom_int if nom_int else 1.0,
            #                                                                           nom_int,
            #                                                                           up_int,
            #                                                                           dn_int))
            print ("normalization change: "
                   +"{} up {:.1%} +/- {:.1%} down {:.1%} +/- {:.1%} ".format(h_nom.GetName(), 1.0-rup, rupe, 1.0-rdn, rdne)
                   +"(integral: "
                   +"nom {:.2E}  +/- {:.2E}, up {:.2E} +/- {:.2E}, do {:.2E} +/- {:.2E})".format(nom_int, nom_err,
                                                                                                 up_int, up_err,
                                                                                                 dn_int, dn_err)
                   +" (entries nom {:.2E} up {:.2E} do {:.2E}".format(h_nom.GetEntries(), h_up.GetEntries(), h_dn.GetEntries()))
            print ("tex normalization change: "
                   +"{} up ${:.1%} \pm {:.1%}$ down ${:.1%} \pm {:.1%}$ ".format(h_nom.GetName(), 1.0-rup, rupe, 1.0-rdn, rdne)
                   )

            def bc(h): return [h.GetBinContent(i) for i in range(1,1+h.GetNbinsX())]
            def max_frac_variation(h1, h2):
                "maximum bin-by-bin fractional variation; h1 is denominator, empty bins skipped"
                bc1 = bc(h1)
                bc2 = bc(h2)
                return max([abs(b2/b1) for b1, b2 in zip(bc1, bc2) if b1 and b2])
            def max_frac_variation_within10(h1, h2):
                """maximum bin-by-bin fractional variation; h1 is denominator.
                Bins with <0.1*peak are skipped"""
                bc1 = bc(h1)
                bc2 = bc(h2)
                m1 = max(bc1)
                m2 = max(bc2)
                return max([abs(b2/b1) for b1, b2 in zip(bc1, bc2) if b1>0.1*m1 and b2>0.1*m2])

            # print ("shape change: "
            #        +"{} up {:.1%} down {:.1%} ".format(h_nom.GetName(),
            #                                            1.0-max_frac_variation_within10(h_up, h_nom),
            #                                            1.0-max_frac_variation_within10(h_dn, h_nom)))

        topPad.Update()
        # bottom
        can.cd()
        botPad.SetTopMargin(1.25*botPad.GetTopMargin())
        botPad.Draw()
        botPad.cd()
        ratio_up = ru.buildRatioHistogram(h_up, h_nom)
        ratio_dn = ru.buildRatioHistogram(h_dn, h_nom)
        yMin, yMax = 0.5, 1.5
        ratioPadMaster = pad_master.Clone(pad_master.GetName()+'_ratio')
        ratioPadMaster.SetMinimum(yMin)
        ratioPadMaster.SetMaximum(yMax)
        ratioPadMaster.SetStats(0)
        ratioPadMaster.Draw('axis')
        x_lo, x_hi = ru.getXrange(ratioPadMaster)
        refLines = [ru.referenceLine(x_lo, x_hi, y, y) for y in [0.5, 1.0, 1.5] if y>yMin and y<yMax]
        for l in refLines : l.Draw()
        ratio_up.Draw('same')
        ratio_dn.Draw('same')
        xA, yA = ratioPadMaster.GetXaxis(), ratioPadMaster.GetYaxis()
        textScaleUp = 0.75*1.0/botPad.GetHNDC()
        yA.SetNdivisions(-102)
        yA.SetTitle('Ratio')
        yA.CenterTitle()
        yA.SetTitleOffset(yA.GetTitleOffset()/textScaleUp)
        xA.SetTitleSize(yA.GetTitleSize()) # x- was set to 0 for padmaster, restore it
        xA.SetLabelSize(yA.GetLabelSize())
        xA.SetTitle(prettify_title(xA.GetTitle()))
        for a in [xA, yA] :
            a.SetLabelSize(a.GetLabelSize()*textScaleUp)
            a.SetTitleSize(a.GetTitleSize()*textScaleUp)
        botPad._graphical_objects = [ratio_up, ratio_dn, ratioPadMaster] + refLines # avoid garbage collection
        botPad.Update()
        can.Update()
        first_histo = histogram_name is histogram_names[0]
        last_histo  = histogram_name is histogram_names[-1]
        can.SaveAs(outdir+'/'+can.GetName()+'.png')
        can.SaveAs(outdir+'/'+can.GetName()+'.eps')
        can.SaveAs(output_pdf_name+ (')' if last_histo else ''))
def buildRate(file, histo_basename) :
    hs = getNumDenHistos(file, histo_basename)
    return buildRatioHistogram(hs['num'], hs['den'])
Beispiel #22
0
def buildRatio(inputFile=None, histoBaseName=''):
    num, den = inputFile.Get(histoBaseName +
                             '_num'), inputFile.Get(histoBaseName + '_den')
    return buildRatioHistogram(num, den, histoBaseName + '_rat')
def buildRate(file, histo_basename):
    hs = getNumDenHistos(file, histo_basename)
    return buildRatioHistogram(hs['num'], hs['den'])
def plotHistosRatio(histosPairs=[], canvasName=''):
    histosRatio = [
        buildRatioHistogram(hh['num'], hh['den']) for hh in histosPairs
    ]
    plotHistos(histosRatio, canvasName)
Beispiel #25
0
def main():
    parser = argparse.ArgumentParser(
        description=description,
        epilog=epilog,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    add_arg = parser.add_argument
    add_arg('-o', '--output-dir', default="./")
    add_arg('-p', '--process', help='one physics process, eg. ttw')
    add_arg('-s', '--systematic', help='one of the systematic variations')
    add_arg('-v', '--verbose', action='store_true')
    add_arg('-d', '--debug', action='store_true')
    args = parser.parse_args()

    set_log(args.verbose, args.debug)
    outdir = args.output_dir
    process = args.process
    systematic = args.systematic

    available_processes = get_input_samples().keys()
    if process not in available_processes:
        raise StandardError("invalid process %s, should be one of %s" %
                            (process, str(available_processes)))
    available_systematics = get_input_samples()[process].keys()
    if systematic not in available_systematics:
        raise StandardError("invalid systematic %s, should be one of %s" %
                            (systematic, str(available_systematics)))

    file_label = process + '_sys_' + systematic
    plot_label = process + ' sys. ' + systematic

    normalize_to_unity = False  # True
    luminosity = 1.0

    combiner = HistogramCombiner()

    combiner.build_samples(process=process, systematic=systematic)
    histogram_names = [
        'h_meff',
        'h_jetN',
        # 'h_electronPt', 'h_muonPt',
        # 'h_bjetEmulN', 'h_bjetN',
        # 'h_bjetEmulN_sr3b', 'h_bjetN_sr3b',
        # 'h_bjetEmulN_sr1b', 'h_bjetN_sr1b',
        # 'h_bjetEmulN_cr2bttV', 'h_bjetN_cr2bttV',
        # 'h_meff_sr3b',    'h_jetN_sr3b',    'h_met_sr3b',
        # 'h_meff_sr1b',    'h_jetN_sr1b',    'h_met_sr1b',
        # 'h_meff_sr0b5j',  'h_jetN_sr0b5j',  'h_met_sr0b5j',
        # 'h_meff_sr0b4j',  'h_jetN_sr0b4j',  'h_met_sr0b4j',
        # 'h_meff_sr0b3j',  'h_jetN_sr0b3j',  'h_met_sr0b3j',
        # 'h_meff_cr2bttV', 'h_jetN_cr2bttV', 'h_met_cr2bttV'
    ]

    combiner.compute_normalization_factors()
    output_pdf_name = outdir + '/' + file_label + '.pdf'
    c_summary = R.TCanvas('c_summary', 'plotExplicitSamples sampes summary ')
    combiner.print_sample_summary_to_pdf(
        c_summary,
        label="%s: nominal vs. %s systematic" % (process, systematic))
    c_summary.SaveAs(output_pdf_name + '(')

    for histogram_name in histogram_names:
        rebin = 'meff' in histogram_name  # and '_sr' in histogram_name # non-inclusive histos: low stats
        rebin_factor = (2 if 'meff' in histogram_name else
                        2 if 'jetN' in histogram_name else 1) if rebin else 1
        histograms = combiner.get_histograms(histogram_name=histogram_name)
        h_nom = histograms['nominal']
        h_up = histograms['up']
        h_dn = histograms['down']
        if 'h_jetFlavorMultiplicity' in histogram_name:
            h_nom = emulate_btag_multiplicity_from_truth_flavor(h_nom, 'nom')
            h_up = emulate_btag_multiplicity_from_truth_flavor(h_up, 'up')
            h_dn = emulate_btag_multiplicity_from_truth_flavor(h_dn, 'dn')
        histos = [h_nom, h_up, h_dn]
        for h in set(histos):  # set: avoid rebinning twice when up==down
            h.Rebin(rebin_factor)
        h_nom.SetLineWidth(2 * h_nom.GetLineWidth())
        h_nom.SetLineColor(R.kBlack)
        h_up.SetLineColor(R.kBlue)
        h_dn.SetLineColor(R.kRed)

        pad_master = h_nom
        pad_master.SetMaximum(1.1 * max([h.GetMaximum() for h in histos]))
        pad_master.SetMinimum(1.0 *
                              min([0.0] + [h.GetMinimum() for h in histos]))
        pad_master.GetYaxis().SetTitle('Arbitrary Units')
        pad_master.SetStats(0)
        can = R.TCanvas('c_ttV_syst_' + histogram_name,
                        'ttV explicit variations ' + pad_master.GetTitle(),
                        700, 700)
        botPad, topPad = ru.buildBotTopPads(can, squeezeMargins=False)
        # top
        can.cd()
        topPad.Draw()
        topPad.cd()
        topPad._po = [pad_master]  # persistent objects
        pad_master.GetXaxis().SetTitleSize(0)
        pad_master.GetXaxis().SetLabelSize(0)
        pad_master.Draw('axis')
        # ru.topRightLabel(topPad, pad_master.GetTitle(), xpos=0.5)
        ru.topRightLabel(topPad,
                         "#bf{#it{ATLAS}} Simulation Preliminary",
                         xpos=0.85,
                         ypos=0.9)
        ru.topRightLabel(topPad, "#sqrt{s} = 13 TeV", xpos=0.85, ypos=0.8)

        leg = ru.topRightLegend(can,
                                legWidth=0.225,
                                legHeight=0.300,
                                hShift=-0.10,
                                vShift=-0.25)
        leg.SetBorderSize(0)
        # leg.SetHeader(plot_label+ ("(norm=1)" if normalize_to_unity else "(lumi %.1f)"%luminosity))
        topPad._po.append(leg)

        def format_legend_label(h, l):
            return "{0}: {1:.2E} ({2:.0f})".format(l, h.Integral(),
                                                   h.GetEntries())

        def pretty_scale_legend_label(h, l):
            return ("nominal"
                    if l is 'nom' else "#mu = 2.0 #mu_{0}" if l is 'up' else
                    "#mu = 0.5 #mu_{0}" if l is 'dn' else 'unknown')

        for h, l in [(h_nom, 'nom'), (h_up, 'up'), (h_dn, 'dn')]:
            h.Draw('hist same')
            leg.AddEntry(h, pretty_scale_legend_label(h, l), 'l')
            topPad._po.append(h)
        leg.Draw('same')

        def integral_and_error(h):
            error = R.Double(0.0)
            integral = h.IntegralAndError(1, h.GetNbinsX() + 1, error)
            return integral, error

        def ratio_and_error(ave=(1.0, 0.01), bve=(2.0, 0.001)):
            a, sa = ave
            b, sb = bve
            if a and b:
                r = a / b
                e = r * sqrt((sa / a) * (sa / a) + (sb / b) * (sb / b))
                return r, e
            else:
                return 0.0, 0.0

        print_normalization_summary = histogram_name.startswith('h_meff')
        if print_normalization_summary:
            nom_int = h_nom.Integral()
            up_int = h_up.Integral()
            dn_int = h_dn.Integral()
            nom_int, nom_err = integral_and_error(h_nom)
            up_int, up_err = integral_and_error(h_up)
            dn_int, dn_err = integral_and_error(h_dn)
            rup, rupe = ratio_and_error((up_int, up_err), (nom_int, nom_err))
            rdn, rdne = ratio_and_error((dn_int, dn_err), (nom_int, nom_err))
            # print ("normalization change: "
            #        +"{} up {:.1%} down {:.1%} (nom {:.1f}, up {:.1f}, do {:.1f})".format(h_nom.GetName(),
            #                                                                           1.0-up_int/nom_int if nom_int else 1.0,
            #                                                                           1.0-dn_int/nom_int if nom_int else 1.0,
            #                                                                           nom_int,
            #                                                                           up_int,
            #                                                                           dn_int))
            print(
                "normalization change: " +
                "{} up {:.1%} +/- {:.1%} down {:.1%} +/- {:.1%} ".format(
                    h_nom.GetName(), 1.0 - rup, rupe, 1.0 - rdn, rdne) +
                "(integral: " +
                "nom {:.2E}  +/- {:.2E}, up {:.2E} +/- {:.2E}, do {:.2E} +/- {:.2E})"
                .format(nom_int, nom_err, up_int, up_err, dn_int, dn_err) +
                " (entries nom {:.2E} up {:.2E} do {:.2E}".format(
                    h_nom.GetEntries(), h_up.GetEntries(), h_dn.GetEntries()))
            print(
                "tex normalization change: " +
                "{} up ${:.1%} \pm {:.1%}$ down ${:.1%} \pm {:.1%}$ ".format(
                    h_nom.GetName(), 1.0 - rup, rupe, 1.0 - rdn, rdne))

            def bc(h):
                return [
                    h.GetBinContent(i) for i in range(1, 1 + h.GetNbinsX())
                ]

            def max_frac_variation(h1, h2):
                "maximum bin-by-bin fractional variation; h1 is denominator, empty bins skipped"
                bc1 = bc(h1)
                bc2 = bc(h2)
                return max(
                    [abs(b2 / b1) for b1, b2 in zip(bc1, bc2) if b1 and b2])

            def max_frac_variation_within10(h1, h2):
                """maximum bin-by-bin fractional variation; h1 is denominator.
                Bins with <0.1*peak are skipped"""
                bc1 = bc(h1)
                bc2 = bc(h2)
                m1 = max(bc1)
                m2 = max(bc2)
                return max([
                    abs(b2 / b1) for b1, b2 in zip(bc1, bc2)
                    if b1 > 0.1 * m1 and b2 > 0.1 * m2
                ])

            # print ("shape change: "
            #        +"{} up {:.1%} down {:.1%} ".format(h_nom.GetName(),
            #                                            1.0-max_frac_variation_within10(h_up, h_nom),
            #                                            1.0-max_frac_variation_within10(h_dn, h_nom)))

        topPad.Update()
        # bottom
        can.cd()
        botPad.SetTopMargin(1.25 * botPad.GetTopMargin())
        botPad.Draw()
        botPad.cd()
        ratio_up = ru.buildRatioHistogram(h_up, h_nom)
        ratio_dn = ru.buildRatioHistogram(h_dn, h_nom)
        yMin, yMax = 0.5, 1.5
        ratioPadMaster = pad_master.Clone(pad_master.GetName() + '_ratio')
        ratioPadMaster.SetMinimum(yMin)
        ratioPadMaster.SetMaximum(yMax)
        ratioPadMaster.SetStats(0)
        ratioPadMaster.Draw('axis')
        x_lo, x_hi = ru.getXrange(ratioPadMaster)
        refLines = [
            ru.referenceLine(x_lo, x_hi, y, y) for y in [0.5, 1.0, 1.5]
            if y > yMin and y < yMax
        ]
        for l in refLines:
            l.Draw()
        ratio_up.Draw('same')
        ratio_dn.Draw('same')
        xA, yA = ratioPadMaster.GetXaxis(), ratioPadMaster.GetYaxis()
        textScaleUp = 0.75 * 1.0 / botPad.GetHNDC()
        yA.SetNdivisions(-102)
        yA.SetTitle('Ratio')
        yA.CenterTitle()
        yA.SetTitleOffset(yA.GetTitleOffset() / textScaleUp)
        xA.SetTitleSize(
            yA.GetTitleSize())  # x- was set to 0 for padmaster, restore it
        xA.SetLabelSize(yA.GetLabelSize())
        xA.SetTitle(prettify_title(xA.GetTitle()))
        for a in [xA, yA]:
            a.SetLabelSize(a.GetLabelSize() * textScaleUp)
            a.SetTitleSize(a.GetTitleSize() * textScaleUp)
        botPad._graphical_objects = [ratio_up, ratio_dn, ratioPadMaster
                                     ] + refLines  # avoid garbage collection
        botPad.Update()
        can.Update()
        first_histo = histogram_name is histogram_names[0]
        last_histo = histogram_name is histogram_names[-1]
        can.SaveAs(outdir + '/' + can.GetName() + '.png')
        can.SaveAs(outdir + '/' + can.GetName() + '.eps')
        can.SaveAs(output_pdf_name + (')' if last_histo else ''))
def plotHistosRatio(histosPairs=[], canvasName="", outdir="./"):
    histosRatio = [buildRatioHistogram(hh["num"], hh["den"]) for hh in histosPairs]
    print histosRatio
    for h in histosRatio:
        h.SetTitle("ratio " + h.GetTitle())
    plotHistos(histosRatio, canvasName, outdir)
def main():
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('-n', '--n_iter', type='int', default=8)
    parser.add_option('-m', '--input_mc')
    parser.add_option('-d', '--input_data')
    parser.add_option('-o', '--output')
    parser.add_option(
        '-p', '--plot',
        help='plot inputs')  # todo: implement sanity plot vs. n_iter
    parser.add_option('-v', '--verbose', action='store_true', default=False)
    (opts, args) = parser.parse_args()
    requiredOptions = ['n_iter', 'input_mc', 'input_data', 'output']
    otherOptions = ['plot', 'verbose']
    allOptions = requiredOptions + otherOptions

    def optIsNotSpecified(o):
        return not hasattr(opts, o) or getattr(opts, o) is None

    if any(optIsNotSpecified(o) for o in requiredOptions):
        parser.error('Missing required option')
    nIter = opts.n_iter
    fnameInputMc = opts.input_mc
    fnameInputDa = opts.input_data
    fnameOutput = opts.output
    plot = opts.plot
    verbose = opts.verbose
    if verbose:
        print(
            '\nUsing the following options:\n' +
            '\n'.join("%s : %s" % (o, str(getattr(opts, o)))
                      for o in allOptions))
    fileData = r.TFile.Open(fnameInputDa)
    fileMc = r.TFile.Open(fnameInputMc)
    assert fileData and fileMc, "Missing input files: data %s, mc %s" % (
        str(fileData), str(fileMc))
    correctionHistos = {}
    for lep in ['muon', 'elec']:
        if verbose: print "Lepton: %s" % lep
        hRealDataCr = getNumDenHistos(fileData, lep + '_realCR_all_l_pt')
        hFakeDataLo = getNumDenHistos(fileData, lep + '_fakeHF_all_l_pt')
        hFakeDataHi = getNumDenHistos(fileData, lep + '_fakeHF_high_all_l_pt')
        hFakeMcLo = getNumDenHistos(fileMc, lep + '_fakeHF_all_l_pt')
        hFakeMcHi = getNumDenHistos(fileMc, lep + '_fakeHF_high_all_l_pt')
        if plot:
            hNumDen = [hFakeDataLo, hFakeDataHi, hFakeMcLo, hFakeMcHi]
            for nd in ['num', 'den']:
                plotHistos([h[nd] for h in hNumDen], 'c_' + lep + '_' + nd)
            plotHistosRatio(hNumDen, 'c_' + lep + '_ratio')

        def missingInputHisto(ndHistos):
            return any(not h for h in ndHistos.values())

        histoCollToBeChecked = [
            'hRealDataCr', 'hFakeDataLo', 'hFakeDataHi', 'hFakeMcLo',
            'hFakeMcHi'
        ]
        missingHistos = dict([(nhc, hp)
                              for nhc, hp in [(hc, eval(hc))
                                              for hc in histoCollToBeChecked]
                              if missingInputHisto(hp)])
        if len(missingHistos):
            print(
                lep + ' : missing histograms: \n' + '\n'.join([
                    "%s: num %s den %s" % (k, v['num'], v['den'])
                    for k, v in missingHistos.iteritems()
                ]))
            continue
        hRealEff = buildRatioHistogram(hRealDataCr['num'], hRealDataCr['den'],
                                       'real_eff')
        corrected = dict([(nd, hFakeDataLo[nd].Clone('corrected_' + nd))
                          for nd in ['num', 'den']])
        for iteration in range(nIter):
            rate = buildRatioHistogram(corrected['num'],
                                       corrected['den'])  # temporary rate (?)
            if verbose:

                def lf2s(l):
                    return ', '.join(["%.3f" % e for e in l])

                print "Iteration %d, corrected values:" % iteration
                print "  num   %s" % lf2s(binContents(corrected['num']))
                print "  den   %s" % lf2s(binContents(corrected['den']))
                print "  ratio %s" % lf2s(binContents(rate))
            dataNum, dataDen = hFakeDataHi['num'], hFakeDataHi['den']
            for nd, tl in [('num', 'tight'), ('den', 'loose')]:
                corr, dataLow = corrected[nd], hFakeDataLo[nd]
                mcLow, mcHi = hFakeMcLo[nd], hFakeMcHi[nd]
                corrFact = getCorrFactors(hRealEff, rate, dataNum, dataDen,
                                          mcHi, tl)
                corr = correctRate(corr, dataLow, mcLow, corrFact)
        ratio = buildRatioHistogram(corrected['num'], corrected['den'],
                                    lep + '_corHFRate')
        correctionHistos[lep] = ratio
    if verbose: print "saving output to ", fnameOutput
    fileOut = r.TFile.Open(fnameOutput, 'recreate')
    fileOut.cd()
    for l, h in correctionHistos.iteritems():
        if verbose:
            print "%s : writing %s\n%s" % (l, h.GetName(), histo1dToTxt(h))
        h.Write()
    fileOut.Close()
def plotIsoComparison(histosPerSource={}, outputDir='', region='', lepton='', verbose=False):
    """
    plot a comparison of eff(T|L) for real and for fake leptons
    vs. pt, where the numerator is one of the tight definitions
    """
    var = 'pt'
    sources = histosPerSource.keys()
    lOrTOrTs = first(first(histosPerSource)).keys()
    histosPtPerSource = dict((s, dict((lt, histosPerSource[s][var][lt]) for lt in lOrTOrTs)) for s in sources)
    def buildTotFakeHistos():
        "add up all the non-real (fake) sources"
        notRealSources = [s for s in sources if s!='real']
        aSource = first(notRealSources)
        totFakeHistos = dict()
        for lt in ['loose', 'tight', 'tight_std', 'tight_minden', 'tight_tight']:
            template = histosPtPerSource[aSource][lt]
            h = template.Clone(template.GetName().replace(aSource, 'fake'))
            h.Reset()
            for s in sources : h.Add(histosPtPerSource[s][lt])
            totFakeHistos[lt] = h
        return totFakeHistos
    histosPtPerSource['fake'] = buildTotFakeHistos()
    effReal_wh     = rootUtils.buildRatioHistogram(histosPtPerSource['real']['tight'       ], histosPtPerSource['real']['loose'])
    effReal_std    = rootUtils.buildRatioHistogram(histosPtPerSource['real']['tight_std'   ], histosPtPerSource['real']['loose'])
    effReal_minden = rootUtils.buildRatioHistogram(histosPtPerSource['real']['tight_minden'], histosPtPerSource['real']['loose'])
    effReal_tight  = rootUtils.buildRatioHistogram(histosPtPerSource['real']['tight_tight' ], histosPtPerSource['real']['loose'])
    effFake_wh     = rootUtils.buildRatioHistogram(histosPtPerSource['fake']['tight'       ], histosPtPerSource['fake']['loose'])
    effFake_std    = rootUtils.buildRatioHistogram(histosPtPerSource['fake']['tight_std'   ], histosPtPerSource['fake']['loose'])
    effFake_minden = rootUtils.buildRatioHistogram(histosPtPerSource['fake']['tight_minden'], histosPtPerSource['fake']['loose'])
    effFake_tight  = rootUtils.buildRatioHistogram(histosPtPerSource['fake']['tight_tight' ], histosPtPerSource['fake']['loose'])
    frameName, frameTitle = region+'_'+lepton, "fake and real efficiencies for %s in %s"%(lepton, region)
    can = r.TCanvas('c_'+frameName, frameTitle, 800, 600)
    can.cd()
    pm = effReal_wh
    pm.SetMinimum(0.0)
    pm.SetMaximum(1.1)
    pm.GetYaxis().SetTitle("#epsilon(T|L)")
    colorReal, colorFake = r.kBlue, r.kRed
    markerWh, markerStd, markerMinden, markerTight = r.kMultiply, r.kCircle, r.kOpenTriangleUp, r.kOpenSquare
    def setAttrs(h, mark, col):
        h.SetLineColor(col)
        h.SetMarkerColor(col)
        h.SetMarkerStyle(mark)
    setAttrs(effReal_wh,     markerWh,     colorReal)
    setAttrs(effReal_std,    markerStd,    colorReal)
    setAttrs(effReal_minden, markerMinden, colorReal)
    setAttrs(effReal_tight,  markerTight,  colorReal)
    setAttrs(effFake_wh,     markerWh,     colorFake)
    setAttrs(effFake_std,    markerStd,    colorFake)
    setAttrs(effFake_minden, markerMinden, colorFake)
    setAttrs(effFake_tight,  markerTight,  colorFake)
    pm.SetStats(0)
    pm.Draw('axis')
    #for h in [effReal_wh, effReal_std, effReal_tight, effFake_wh, effFake_std, effFake_tight]:
    for h in [effReal_wh, effReal_std, effReal_minden, effFake_wh, effFake_std, effFake_minden]:
        h.Draw('same')
    leg = rightLegend(can)
    leg.SetBorderSize(0)
    leg.AddEntry(r.TObject(),   'Real', '')
    leg.AddEntry(effReal_std,   'std iso', 'lp')
    #leg.AddEntry(effReal_tight, 'tight iso', 'lp')
    leg.AddEntry(effReal_minden,'minden iso', 'lp')
    leg.AddEntry(effReal_wh,    'wh iso',  'lp')
    leg.AddEntry(r.TObject(),   'Fake', '')
    leg.AddEntry(effFake_std,   'std iso', 'lp')
    #leg.AddEntry(effFake_tight, 'tight iso', 'lp')
    leg.AddEntry(effFake_minden,'minden iso', 'lp')
    leg.AddEntry(effFake_wh,  '  wh iso',  'lp')
    leg.Draw()
    topRightLabel(can, "#splitline{%s}{%s}"%(lepton, region), xpos=0.125, align=13)
    can.RedrawAxis()
    can._histos = [effReal_wh, effReal_std, effFake_wh, effFake_std]
    can.Update()
    mkdirIfNeeded(outputDir)
    can.SaveAs(os.path.join(outputDir, frameTitle+'.png'))