def plotHistRatioAndFit(histos, ratio, fitfunc, outfname, graphics_attributes={}) : can = r.TCanvas('can_'+outfname, outfname, 800, 600) botPad, topPad = buildBotTopPads(can, splitFraction=0.35) can.cd() topPad.Draw() topPad.cd() pm = first(histos) pm.Draw('axis') xAx, yAx = pm.GetXaxis(), pm.GetYaxis() xAx.SetTitle('') xAx.SetLabelSize(0) yAx.SetRangeUser(0.0, 1.2) yAx.SetNdivisions(505) # for some reason some of the inputs are already ratios with -201 textScaleUp = 1.0/topPad.GetHNDC() yAx.SetLabelSize(textScaleUp*0.04) yAx.SetTitleSize(textScaleUp*0.04) yAx.SetTitle(graphics_attributes['ytitle']) yAx.SetTitleOffset(yAx.GetTitleOffset()/textScaleUp) for k, h in histos.iteritems() : h.SetMarkerStyle(graphics_attributes['markers'][k]) h.SetMarkerColor(graphics_attributes['colors'][k]) h.SetLineColor(graphics_attributes['colors'][k]) h.Draw('same') labels = graphics_attributes['labels'] drawLegendWithDictKeys(topPad, dict([(labels[s], histos[s]) for s in histos.keys()]), legWidth=0.4) can.cd() botPad.Draw() botPad.cd() ratio.Draw('axis') textScaleUp = 1.0/botPad.GetHNDC() xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis() yAx.SetRangeUser(0.0, 2.0) xAx.SetTitle(graphics_attributes['xtitle']) yAx.SetNdivisions(-202) yAx.SetTitle('Data/Sim') yAx.CenterTitle() for a in [xAx, yAx] : if a.GetLabelSize()>0.04 : continue a.SetLabelSize(a.GetLabelSize()*textScaleUp) a.SetTitleSize(a.GetTitleSize()*textScaleUp) a.SetTitleOffset(a.GetTitleOffset()/textScaleUp) ratio.SetMarkerStyle(graphics_attributes['markers']['data']) fitfunc.SetLineWidth(2) fitfunc.SetLineStyle(2) fitfunc.Draw('same') ratio.Draw('same') tex = r.TLatex() tex.SetNDC(True) p0, p0Err, chi2, ndf = fitResults(fitfunc) fitParLabel = "Const. fit : %s #pm %s"%(pdgRound(p0, p0Err)) fitGoodLabel = "#chi^{2}/DOF : %.2f / %d"%(chi2, ndf) tex.SetTextSize(yAx.GetTitleSize()) tex.SetTextFont(yAx.GetTitleFont()) tex.DrawLatex(0.15, 0.40, "#splitline{%s}{%s}"%(fitParLabel, fitGoodLabel)) can.Update() outfname = outfname+'.png' rmIfExists(outfname) # avoid root warnings can.SaveAs(outfname)
def plot_emu_mue_with_ratio(canvas=None, h_mue=None, h_emu=None, h_ratio=None, filename='', figformats=['png', 'eps']): c = canvas c.Clear() c._graphics = [] c.cd() botPad, topPad = buildBotTopPads(c, splitFraction=0.375) topPad.Draw() topPad.cd() h_mue.SetStats(0) h_emu.SetStats(0) h_emu.SetLineColor(r.kBlue) h_mue.SetLineColor(r.kRed) h_mue.Draw() h_emu.Draw('same') pm = h_mue yMin, yMax = getMinMax([h_mue, h_emu]) pm.SetMinimum(0.0) pm.SetMaximum(1.1 * yMax) xax = pm.GetXaxis() yax = pm.GetYaxis() xax.SetLabelSize(0) xax.SetTitleSize(0) leg = topLeftLegend(c, 0.275, 0.275, shift=0.05) leg.SetBorderSize(0) leg.SetHeader('non-prompt prediction') leg.AddEntry(h_mue, '#mue', 'L') leg.AddEntry(h_emu, 'e#mu', 'L') leg.Draw() c._graphics.append(leg) c.cd() botPad.Draw() botPad.cd() h_ratio.SetLineColor(r.kBlack) h_ratio.SetTitle('') xax = h_ratio.GetXaxis() yax = h_ratio.GetYaxis() yax.SetRangeUser(0.0, 2.0) yax.SetTitle('e#mu / #mue ratio') xA, yA = h_ratio.GetXaxis(), h_ratio.GetYaxis() textScaleUp = 1.0 / botPad.GetHNDC() yA.SetNdivisions(-104) yA.CenterTitle() yA.SetTitleOffset(yA.GetTitleOffset() / textScaleUp) for a in [xA, yA]: a.SetLabelSize(a.GetLabelSize() * textScaleUp) a.SetTitleSize(a.GetTitleSize() * textScaleUp) h_ratio.Draw() refline = referenceLine(xA.GetXmin(), xA.GetXmax()) refline.Draw() c._graphics.append(refline) c.Update() for ext in figformats: c.SaveAs("{0}.{1}".format(filename, ext))
def plot_emu_mue_with_ratio(canvas=None, h_mue=None, h_emu=None, h_ratio=None, filename='', figformats=['png', 'eps'], label=''): c = canvas c.Clear() c._graphics = [] c.cd() botPad, topPad = buildBotTopPads(c, splitFraction=0.375) topPad.Draw() topPad.cd() h_mue.SetStats(0) h_emu.SetStats(0) h_emu.SetLineColor(r.kBlue) h_mue.SetLineColor(r.kRed) h_mue.Draw() h_emu.Draw('same') pm = h_mue yMin, yMax = getMinMax([h_mue, h_emu]) pm.SetMinimum(0.0) pm.SetMaximum(1.1 * yMax) xax = pm.GetXaxis() yax = pm.GetYaxis() xax.SetLabelSize(0) xax.SetTitleSize(0) leg = topRightLegend(c, 0.275, 0.275, shift=0.05) leg.SetBorderSize(0) leg.SetHeader(label) leg.AddEntry(h_mue, '#mue', 'L') leg.AddEntry(h_emu, 'e#mu', 'L') leg.Draw() c._graphics.append(leg) c.cd() botPad.Draw() botPad.cd() h_ratio.SetLineColor(r.kBlack) h_ratio.SetTitle('') xax = h_ratio.GetXaxis() yax = h_ratio.GetYaxis() yax.SetRangeUser(0.0, 2.0) yax.SetTitle('e#mu / #mue ratio') xA, yA = h_ratio.GetXaxis(), h_ratio.GetYaxis() textScaleUp = 1.0/botPad.GetHNDC() yA.SetNdivisions(-104) yA.CenterTitle() yA.SetTitleOffset(yA.GetTitleOffset()/textScaleUp) for a in [xA, yA] : a.SetLabelSize(a.GetLabelSize()*textScaleUp) a.SetTitleSize(a.GetTitleSize()*textScaleUp) h_ratio.Draw() refline = referenceLine(xA.GetXmin(), xA.GetXmax()) refline.Draw() c._graphics.append(refline) c.Update() for ext in figformats: c.SaveAs("{0}.{1}".format(filename, ext)) print "{0}.{1}".format(filename, ext)
def main() : parser = optparse.OptionParser(usage=usage) parser.add_option('-t', '--tag') parser.add_option('-f', '--input_fake') parser.add_option('-i', '--input_dir') parser.add_option('-o', '--output_dir') parser.add_option('-v','--verbose', action='store_true', default=False) (opts, args) = parser.parse_args() requiredOptions = ['tag', 'input_fake', 'input_dir', 'output_dir',] otherOptions = ['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') tag = opts.tag.strip('_') inputFakeFile = opts.input_fake inputDirname = opts.input_dir outputDir = opts.output_dir outputDir = outputDir if outputDir.endswith('/') else outputDir+'/' verbose = opts.verbose if verbose : print '\nUsing the following options:\n'+'\n'.join("%s : %s"%(o, str(getattr(opts, o))) for o in allOptions) inputFiles = getInputFiles(inputDirname, tag, verbose) inputFiles[fakeSample()] = r.TFile.Open(inputFakeFile) assert all(f for f in inputFiles.values()), ("missing inputs: \n%s"%'\n'.join(["%s : %s"%kv for kv in inputFiles.iteritems()])) mkdirIfNeeded(outputDir) for region in ['cr8lptee', 'cr8lptmm', 'cr9lpt', 'sr8', 'sr9', 'srSsEwk', 'crSsEwkLoose'] : for channel in ['ee', 'em', 'mm'] : for varname in ['l0_pt', 'l1_pt', 'll_M', 'metrel', 'met', 'njets', 'nbjets'] : histo_basename = region+'_'+channel+'_'+varname hists, err2s = buildHists(inputFiles, histo_basename) if not hists[dataSample()].GetEntries() : continue err_band = buildErrBandGraph(hists['sm'], err2s) err_band_r = buildErrBandRatioGraph(err_band) can = r.TCanvas('can_'+histo_basename, histo_basename, 800, 600) botPad, topPad = buildBotTopPads(can) can.cd() topPad.Draw() drawTop(topPad, hists, err_band, (channel, region)) can.cd() botPad.Draw() drawBot(botPad, hists[dataSample()], hists['sm'], err_band_r, xaxisLabel(varname)) can.Update() outFilename = outputDir+histo_basename+'.png' rmIfExists(outFilename) # avoid root warnings can.SaveAs(outFilename) if verbose : print "output saved to \n%s"%outputDir
def plotVar(bkgHistos, sigHistos, llnjvar, plotdir='./') : def preferredSignal(signals): pref = 'Herwigpp_sM_wA_noslep_notauhad_WH_2Lep_1' return pref if pref in signals else first(sorted(signals)) signalSample = preferredSignal(sigHistos.keys()) allHistos = bkgHistos.values() + [sigHistos[signalSample],] allHistosEmpty = all([h.GetEntries()==0 for h in allHistos]) if allHistosEmpty : return can = r.TCanvas('can_'+llnjvar, llnjvar, 800, 800) botPad, topPad = buildBotTopPads(can, splitFraction=0.75, squeezeMargins=False) totBkg = summedHisto(bkgHistos.values()) totBkg.SetDirectory(0) can._totBkg = totBkg can._histos = [bkgHistos, sigHistos] can.cd() botPad.Draw() drawBottom(botPad, totBkg, bkgHistos, sigHistos[signalSample], llnjvar) can.cd() topPad.Draw() drawTop(topPad, totBkg, sigHistos[signalSample]) mkdirIfNeeded(plotdir) outFilename = plotdir+'/'+llnjvar+'.png' rmIfExists(outFilename) # avoid root warnings can.SaveAs(outFilename)
def plotHistos(histoData=None, histoSignal=None, histoTotBkg=None, histosBkg={}, statErrBand=None, systErrBand=None, # these are TGraphAsymmErrors canvasName='canvas', outdir='./', options=None, stack_order=[], topLabel='', drawStatErr=False, drawSystErr=False, drawYieldAndError=False, printYieldSummary=False) : "Note: blinding can be required for only a subrange of the histo, so it is taken care of when filling" verbose = options.verbose formatAuxMaterial = options.format_aux setAtlasStyle() padMaster = histoData if verbose : print "plotting ",padMaster.GetName(),' (',padMaster.GetEntries(),' entries)' can = r.TCanvas(canvasName, padMaster.GetTitle(), 800, 800) botPad, topPad = buildBotTopPads(can, squeezeMargins=False) can.cd() topPad.Draw() # draw top topPad.cd() topPad._hists = [padMaster] if formatAuxMaterial: for ax in [padMaster.GetXaxis(), padMaster.GetYaxis()]: ax.SetTitle(prettify(ax.GetTitle())) padMaster.Draw('axis') topPad.Update() # necessary to fool root's dumb object ownership of the stack stack = r.THStack('stack_'+padMaster.GetName(), '') r.SetOwnership(stack, False) topPad._hists.append(stack) leg = topRightLegend(can, 0.300 if formatAuxMaterial else 0.225, 0.400 if formatAuxMaterial else 0.325, shiftX=-0.1 if formatAuxMaterial else 0.0, shiftY=-0.1 if formatAuxMaterial else 0.0) topPad._leg = leg leg.SetBorderSize(0) leg.SetTextFont(padMaster.GetTitleFont()) leg._reversedEntries = [] def integralWou(h): "Integral with underflow and overflow" return h.Integral(0, h.GetNbinsX()+1) for group, histo in sortedAs(histosBkg, stack_order) : histo.SetFillColor(getGroupColor(group)) histo.SetLineWidth(2) histo.SetLineColor(r.kBlack) stack.Add(histo) topPad._hists.append(histo) leg._reversedEntries.append((histo, prettify(group) if formatAuxMaterial else "{0}: {1:.2f}".format(group, integralWou(histo)), 'F')) if not formatAuxMaterial: leg._reversedEntries.append((dummyHisto(), "{0}, {1:.2f}".format('bkg', sum([integralWou(h) for h in stack.GetHists()])), 'l')) if statErrBand and drawStatErr : statErrBand.SetFillStyle(3006) leg._reversedEntries.append((statErrBand, 'stat', 'f')) if systErrBand and drawSystErr : systErrBand.SetFillStyle(3007) leg._reversedEntries.append((systErrBand, 'syst', 'f')) leg._reversedEntries.append((histoData, "Data {0}".format(prettify(topLabel)) if formatAuxMaterial else "{0}, {1:.2f}".format('data', integralWou(histoData)), 'p')) for h, g, o in leg._reversedEntries[::-1] : leg.AddEntry(h, g, o) # stack goes b-t, legend goes t-b stack.Draw('hist same') histoData.SetMarkerStyle(r.kFullCircle) histoData.SetLineWidth(2) dataGraph = graphWithPoissonError(histoData) dataGraph.Draw('same p') isRightSignal = False if histoSignal: histoSignal.SetFillStyle(0) histoSignal.SetFillColor(0) histoSignal.SetLineColor(getGroupColor('signal')) histoSignal.SetLineWidth(3) isRightSignal = (all(s in histoSignal.GetName() for s in ['signaltaumu', 'sr_mue']) or all(s in histoSignal.GetName() for s in ['signaltaue', 'sr_emu'])) if isRightSignal: histoSignal.Draw('histo same') if statErrBand and drawStatErr : statErrBand.SetFillStyle(3006) statErrBand.Draw('E2 same') leg.AddEntry(statErrBand, 'stat', 'f') if systErrBand and drawSystErr : systErrBand.SetFillStyle(3007) systErrBand.Draw('E2 same') leg.AddEntry(systErrBand, 'syst', 'f') totErrBand = systUtils.addErrorBandsInQuadrature(statErrBand, systErrBand) if totErrBand : totErrBand.Draw('E2 same') totErrBand.SetFillStyle(3005) leg.AddEntry(totErrBand, 'stat+syst', 'f') if printYieldSummary: print_total_error = False print ("{}:".format(topLabel if topLabel else dataGraph.GetName())+ (" expected {:.2f}^{{+{:.2f}}}_{{{:.2f}}}".format(totErrBand.GetY()[0], totErrBand.GetErrorYhigh(0), totErrBand.GetErrorYlow(0)) if print_total_error else " expected {:.2f}^{{+{:.2f}}}_{{{:.2f}}} (stat) ^{{+{:.2f}}}_{{{:.2f}}} (syst)" .format(totErrBand.GetY()[0], statErrBand.GetErrorYhigh(0) if statErrBand else 0.0, statErrBand.GetErrorYlow(0) if statErrBand else 0.0, systErrBand.GetErrorYhigh(0) if systErrBand else 0.0, systErrBand.GetErrorYlow(0) if systErrBand else 0.0) )+ " obs {:.2f}".format(dataGraph.GetY()[0])) if histoSignal and isRightSignal: hname = histoSignal.GetName() label = ('H#rightarrow#mu#tau' if 'signaltaumu' in hname else 'H#rightarrowe#tau' if 'signaltaue' in hname else 'signal') label = ("{}, BR=1%".format(label) if formatAuxMaterial else "{}, BR=1%, {:.2f}".format(label, integralWou(histoSignal))) leg.AddEntry(histoSignal, label, 'l') leg.Draw('same') topPad.Update() tex = r.TLatex() tex.SetTextSize(0.5 * tex.GetTextSize()) tex.SetNDC(True) label = "%s tot bkg : "%(can.GetName()) label += "%.3f #pm %.3f (stat)"%(integralAndError(histoTotBkg)) if systErrBand : sysUp, sysDo = systUtils.totalUpDownVariation(systErrBand) label += "#pm #splitline{%.3f}{%.3f} (syst)"%(sysUp, sysDo) if drawYieldAndError : tex.DrawLatex(0.10, 0.95, label) topPad.SetTopMargin(2.0*topPad.GetTopMargin()) atlasLabel = drawAtlasLabel(can, align=13, scale=0.75, xpos=0.175 if formatAuxMaterial else 0.125, ypos=0.945 if formatAuxMaterial else None, public=True) if topLabel and not formatAuxMaterial : topRightLabel(can, topLabel, ypos=1.0) yMin, yMax = getMinMax([histoData, dataGraph, histoTotBkg, histoSignal, totErrBand]) padMaster.SetMinimum(0.0) padMaster.SetMaximum(1.1 * yMax) padMaster.GetXaxis().SetLabelSize(0) padMaster.GetXaxis().SetTitleSize(0) increaseAxisFont(padMaster.GetXaxis()) increaseAxisFont(padMaster.GetYaxis()) topPad.RedrawAxis() topPad.Update() # force stack to create padMaster # draw bot (ratio) can.cd() botPad.Draw() botPad.cd() ratio = systUtils.buildAsymmRatioGraph(dataGraph, totErrBand) ratioPadMaster = padMaster.Clone(padMaster.GetName()+'_ratio') ratioPadMaster.SetDirectory(0) ratioPadMaster.Clear() yMin, yMax = 0.0, 2.0 ratioPadMaster.SetMinimum(yMin) ratioPadMaster.SetMaximum(yMax) ratioPadMaster.SetStats(0) ratioPadMaster.Draw('axis') x_lo, x_hi = getXrange(ratioPadMaster) 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 = systUtils.buildErrBandRatioGraph(totErrBand) err_band_r.Draw('E2 same') ratio.Draw('ep') xA, yA = ratioPadMaster.GetXaxis(), ratioPadMaster.GetYaxis() textScaleUp = 0.75*1.0/botPad.GetHNDC() # if xaxis_title : xA.SetTitle(xaxis_title) yA.SetNdivisions(-104) yA.SetTitle('Data/Bkg') yA.CenterTitle() yA.SetTitleOffset(yA.GetTitleOffset()/textScaleUp) xA.SetTitleSize(yA.GetTitleSize()) # was set to 0 for padmaster, restore it xA.SetLabelSize(yA.GetLabelSize()) for a in [xA, yA] : a.SetLabelSize(a.GetLabelSize()*textScaleUp) a.SetTitleSize(a.GetTitleSize()*textScaleUp) botPad._graphical_objects = [ratio, ratioPadMaster, err_band_r] + refLines # avoid garbage collection botPad.Update() can.Update() for ext in ['png','eps'] : can.SaveAs(outdir+'/'+can.GetName()+'.'+ext)
def plotHistos( histoData=None, histoSignal=None, histoTotBkg=None, histosBkg={}, statErrBand=None, systErrBand=None, # these are TGraphAsymmErrors canvasName='canvas', outdir='./', options=None, stack_order=[], topLabel='', drawStatErr=False, drawSystErr=False, drawYieldAndError=False, printYieldSummary=False): "Note: blinding can be required for only a subrange of the histo, so it is taken care of when filling" verbose = options.verbose formatAuxMaterial = options.format_aux setAtlasStyle() padMaster = histoData if verbose: print "plotting ", padMaster.GetName(), ' (', padMaster.GetEntries( ), ' entries)' can = r.TCanvas(canvasName, padMaster.GetTitle(), 800, 800) botPad, topPad = buildBotTopPads(can, squeezeMargins=False) can.cd() topPad.Draw() # draw top topPad.cd() topPad._hists = [padMaster] if formatAuxMaterial: for ax in [padMaster.GetXaxis(), padMaster.GetYaxis()]: ax.SetTitle(prettify(ax.GetTitle())) padMaster.Draw('axis') topPad.Update( ) # necessary to fool root's dumb object ownership of the stack stack = r.THStack('stack_' + padMaster.GetName(), '') r.SetOwnership(stack, False) topPad._hists.append(stack) leg = topRightLegend(can, 0.300 if formatAuxMaterial else 0.225, 0.400 if formatAuxMaterial else 0.325, shiftX=-0.1 if formatAuxMaterial else 0.0, shiftY=-0.1 if formatAuxMaterial else 0.0) topPad._leg = leg leg.SetBorderSize(0) leg.SetTextFont(padMaster.GetTitleFont()) leg._reversedEntries = [] def integralWou(h): "Integral with underflow and overflow" return h.Integral(0, h.GetNbinsX() + 1) for group, histo in sortedAs(histosBkg, stack_order): histo.SetFillColor(getGroupColor(group)) histo.SetLineWidth(2) histo.SetLineColor(r.kBlack) stack.Add(histo) topPad._hists.append(histo) leg._reversedEntries.append( (histo, prettify(group) if formatAuxMaterial else "{0}: {1:.2f}".format(group, integralWou(histo)), 'F')) if not formatAuxMaterial: leg._reversedEntries.append((dummyHisto(), "{0}, {1:.2f}".format( 'bkg', sum([integralWou(h) for h in stack.GetHists()])), 'l')) if statErrBand and drawStatErr: statErrBand.SetFillStyle(3006) leg._reversedEntries.append((statErrBand, 'stat', 'f')) if systErrBand and drawSystErr: systErrBand.SetFillStyle(3007) leg._reversedEntries.append((systErrBand, 'syst', 'f')) leg._reversedEntries.append( (histoData, "Data {0}".format(prettify(topLabel)) if formatAuxMaterial else "{0}, {1:.2f}".format('data', integralWou(histoData)), 'p')) for h, g, o in leg._reversedEntries[::-1]: leg.AddEntry(h, g, o) # stack goes b-t, legend goes t-b stack.Draw('hist same') histoData.SetMarkerStyle(r.kFullCircle) histoData.SetLineWidth(2) dataGraph = graphWithPoissonError(histoData) dataGraph.Draw('same p') isRightSignal = False if histoSignal: histoSignal.SetFillStyle(0) histoSignal.SetFillColor(0) histoSignal.SetLineColor(getGroupColor('signal')) histoSignal.SetLineWidth(3) isRightSignal = (all(s in histoSignal.GetName() for s in ['signaltaumu', 'sr_mue']) or all(s in histoSignal.GetName() for s in ['signaltaue', 'sr_emu'])) if isRightSignal: histoSignal.Draw('histo same') if statErrBand and drawStatErr: statErrBand.SetFillStyle(3006) statErrBand.Draw('E2 same') leg.AddEntry(statErrBand, 'stat', 'f') if systErrBand and drawSystErr: systErrBand.SetFillStyle(3007) systErrBand.Draw('E2 same') leg.AddEntry(systErrBand, 'syst', 'f') totErrBand = systUtils.addErrorBandsInQuadrature(statErrBand, systErrBand) if totErrBand: totErrBand.Draw('E2 same') totErrBand.SetFillStyle(3005) leg.AddEntry(totErrBand, 'stat+syst', 'f') if printYieldSummary: print_total_error = False print( "{}:".format(topLabel if topLabel else dataGraph.GetName()) + (" expected {:.2f}^{{+{:.2f}}}_{{{:.2f}}}".format( totErrBand.GetY()[0], totErrBand.GetErrorYhigh(0), totErrBand.GetErrorYlow(0)) if print_total_error else " expected {:.2f}^{{+{:.2f}}}_{{{:.2f}}} (stat) ^{{+{:.2f}}}_{{{:.2f}}} (syst)" .format(totErrBand.GetY()[0], statErrBand.GetErrorYhigh(0) if statErrBand else 0.0, statErrBand.GetErrorYlow(0) if statErrBand else 0.0, systErrBand.GetErrorYhigh(0) if systErrBand else 0.0, systErrBand.GetErrorYlow(0) if systErrBand else 0.0)) + " obs {:.2f}".format(dataGraph.GetY()[0])) if histoSignal and isRightSignal: hname = histoSignal.GetName() label = ('H#rightarrow#mu#tau' if 'signaltaumu' in hname else 'H#rightarrowe#tau' if 'signaltaue' in hname else 'signal') label = ("{}, BR=1%".format(label) if formatAuxMaterial else "{}, BR=1%, {:.2f}".format(label, integralWou(histoSignal))) leg.AddEntry(histoSignal, label, 'l') leg.Draw('same') topPad.Update() tex = r.TLatex() tex.SetTextSize(0.5 * tex.GetTextSize()) tex.SetNDC(True) label = "%s tot bkg : " % (can.GetName()) label += "%.3f #pm %.3f (stat)" % (integralAndError(histoTotBkg)) if systErrBand: sysUp, sysDo = systUtils.totalUpDownVariation(systErrBand) label += "#pm #splitline{%.3f}{%.3f} (syst)" % (sysUp, sysDo) if drawYieldAndError: tex.DrawLatex(0.10, 0.95, label) topPad.SetTopMargin(2.0 * topPad.GetTopMargin()) atlasLabel = drawAtlasLabel(can, align=13, scale=0.75, xpos=0.175 if formatAuxMaterial else 0.125, ypos=0.945 if formatAuxMaterial else None, public=True) if topLabel and not formatAuxMaterial: topRightLabel(can, topLabel, ypos=1.0) yMin, yMax = getMinMax( [histoData, dataGraph, histoTotBkg, histoSignal, totErrBand]) padMaster.SetMinimum(0.0) padMaster.SetMaximum(1.1 * yMax) padMaster.GetXaxis().SetLabelSize(0) padMaster.GetXaxis().SetTitleSize(0) increaseAxisFont(padMaster.GetXaxis()) increaseAxisFont(padMaster.GetYaxis()) topPad.RedrawAxis() topPad.Update() # force stack to create padMaster # draw bot (ratio) can.cd() botPad.Draw() botPad.cd() ratio = systUtils.buildAsymmRatioGraph(dataGraph, totErrBand) ratioPadMaster = padMaster.Clone(padMaster.GetName() + '_ratio') ratioPadMaster.SetDirectory(0) ratioPadMaster.Clear() yMin, yMax = 0.0, 2.0 ratioPadMaster.SetMinimum(yMin) ratioPadMaster.SetMaximum(yMax) ratioPadMaster.SetStats(0) ratioPadMaster.Draw('axis') x_lo, x_hi = getXrange(ratioPadMaster) 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 = systUtils.buildErrBandRatioGraph(totErrBand) err_band_r.Draw('E2 same') ratio.Draw('ep') xA, yA = ratioPadMaster.GetXaxis(), ratioPadMaster.GetYaxis() textScaleUp = 0.75 * 1.0 / botPad.GetHNDC() # if xaxis_title : xA.SetTitle(xaxis_title) yA.SetNdivisions(-104) yA.SetTitle('Data/Bkg') yA.CenterTitle() yA.SetTitleOffset(yA.GetTitleOffset() / textScaleUp) xA.SetTitleSize( yA.GetTitleSize()) # was set to 0 for padmaster, restore it xA.SetLabelSize(yA.GetLabelSize()) for a in [xA, yA]: a.SetLabelSize(a.GetLabelSize() * textScaleUp) a.SetTitleSize(a.GetTitleSize() * textScaleUp) botPad._graphical_objects = [ratio, ratioPadMaster, err_band_r ] + refLines # avoid garbage collection botPad.Update() can.Update() for ext in ['png', 'eps']: can.SaveAs(outdir + '/' + can.GetName() + '.' + ext)
def subtractRealAndComputeScaleFactor(histosPerGroup={}, variable='', outRatiohistoname='',outDataeffhistoname='', outputDir='./', region='', subtractReal=True, verbose=False): "efficiency scale factor" groups = histosPerGroup.keys() mkdirIfNeeded(outputDir) histosPerType = dict([(lt, dict([(g, histosPerGroup[g][variable][lt]) for g in groups])) for lt in leptonTypes]) for lt in leptonTypes : histosPerType[lt]['totSimBkg'] = summedHisto([histo for group,histo in histosPerType[lt].iteritems() if group not in ['data', 'signal']]) simuTight = histosPerType['fake_tight']['totSimBkg'] simuLoose = histosPerType['fake_loose']['totSimBkg'] dataTight = histosPerType['tight' ]['data' ] dataLoose = histosPerType['loose' ]['data' ] # subtract real contribution from data # _Note to self_: currently estimating the real contr from MC; in # the past also used iterative corr, which might be more # appropriate in cases like here, where the normalization is # so-so. Todo: investigate the normalization. dataSubTight = dataTight.Clone(dataTight.GetName().replace('data_tight','data_minus_prompt_tight')) dataSubLoose = dataLoose.Clone(dataLoose.GetName().replace('data_loose','data_minus_prompt_loose')) dataSubTight.SetDirectory(0) dataSubLoose.SetDirectory(0) dataSubTight.Add(histosPerType['real_tight']['totSimBkg'], -1.0 if subtractReal else 0.0) dataSubLoose.Add(histosPerType['real_loose']['totSimBkg'], -1.0 if subtractReal else 0.0) effData = dataSubTight.Clone(outDataeffhistoname) effData.SetDirectory(0) effData.Divide(dataSubLoose) effSimu = simuTight.Clone(simuTight.GetName().replace('fake_tight','fake_eff')) effSimu.SetDirectory(0) effSimu.Divide(simuLoose) print "eff(T|L) vs. ",variable def formatFloat(floats): return ["%.4f"%f for f in floats] print "efficiency data : ",formatFloat(getBinContents(effData)) print "efficiency simu : ",formatFloat(getBinContents(effSimu)) ratio = effData.Clone(outRatiohistoname) ratio.SetDirectory(0) ratio.Divide(effSimu) print "sf data/simu : ",formatFloat(getBinContents(ratio)) print " +/- : ",formatFloat(getBinErrors(ratio)) can = r.TCanvas('c_'+outRatiohistoname, outRatiohistoname, 800, 600) botPad, topPad = rootUtils.buildBotTopPads(can) can.cd() topPad.Draw() topPad.cd() pm = effData pm.SetStats(0) pm.Draw('axis') xAx, yAx = pm.GetXaxis(), pm.GetYaxis() xAx.SetTitle('') xAx.SetLabelSize(0) yAx.SetRangeUser(0.0, 0.25) textScaleUp = 1.0/topPad.GetHNDC() yAx.SetLabelSize(textScaleUp*0.04) yAx.SetTitleSize(textScaleUp*0.04) yAx.SetTitle('#epsilon(T|L)') yAx.SetTitleOffset(yAx.GetTitleOffset()/textScaleUp) effSimu.SetLineColor(r.kRed) effSimu.SetMarkerStyle(r.kOpenCross) effSimu.SetMarkerColor(effSimu.GetLineColor()) effData.Draw('same') effSimu.Draw('same') leg = drawLegendWithDictKeys(topPad, {'data':effData, 'simulation':simuTight}, legWidth=0.4) leg.SetHeader('scale factor '+region+' '+('electron' if '_el_'in outRatiohistoname else 'muon' if '_mu_' in outRatiohistoname else '')) can.cd() botPad.Draw() botPad.cd() ratio.SetStats(0) ratio.Draw() textScaleUp = 1.0/botPad.GetHNDC() xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis() yAx.SetRangeUser(0.0, 2.0) xAx.SetTitle({'pt1':'p_{T}', 'eta1':'|#eta|', 'pt1_eta1':'p_{T}'}[variable]) yAx.SetNdivisions(-202) yAx.SetTitle('Data/Sim') yAx.CenterTitle() xAx.SetLabelSize(textScaleUp*0.04) xAx.SetTitleSize(textScaleUp*0.04) yAx.SetLabelSize(textScaleUp*0.04) yAx.SetTitleSize(textScaleUp*0.04) refLine = rootUtils.referenceLine(xAx.GetXmin(), xAx.GetXmax()) refLine.Draw() can.Update() outFname = os.path.join(outputDir, region+'_'+outRatiohistoname) for ext in ['.eps','.png']: utils.rmIfExists(outFname+ext) can.SaveAs(outFname+ext) return {outRatiohistoname : ratio, outDataeffhistoname : effData, outDataeffhistoname.replace('_fake_rate_data_', '_tight_data_minus_prompt') : dataSubTight, outDataeffhistoname.replace('_fake_rate_data_', '_loose_data_minus_prompt') : dataSubLoose }
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 subtractRealAndComputeScaleFactor(histosPerGroup={}, variable='', outRatiohistoname='', outDataeffhistoname='', outputDir='./', region='', subtractReal=True, verbose=False): "efficiency scale factor" groups = histosPerGroup.keys() mkdirIfNeeded(outputDir) histosPerType = dict([(lt, dict([(g, histosPerGroup[g][variable][lt]) for g in groups])) for lt in leptonTypes]) for lt in leptonTypes: histosPerType[lt]['totSimBkg'] = summedHisto([ histo for group, histo in histosPerType[lt].iteritems() if group not in ['data', 'signal'] ]) simuTight = histosPerType['fake_tight']['totSimBkg'] simuLoose = histosPerType['fake_loose']['totSimBkg'] dataTight = histosPerType['tight']['data'] dataLoose = histosPerType['loose']['data'] # subtract real contribution from data # _Note to self_: currently estimating the real contr from MC; in # the past also used iterative corr, which might be more # appropriate in cases like here, where the normalization is # so-so. Todo: investigate the normalization. dataSubTight = dataTight.Clone(dataTight.GetName().replace( 'data_tight', 'data_minus_prompt_tight')) dataSubLoose = dataLoose.Clone(dataLoose.GetName().replace( 'data_loose', 'data_minus_prompt_loose')) dataSubTight.SetDirectory(0) dataSubLoose.SetDirectory(0) dataSubTight.Add(histosPerType['real_tight']['totSimBkg'], -1.0 if subtractReal else 0.0) dataSubLoose.Add(histosPerType['real_loose']['totSimBkg'], -1.0 if subtractReal else 0.0) effData = dataSubTight.Clone(outDataeffhistoname) effData.SetDirectory(0) effData.Divide(dataSubLoose) effSimu = simuTight.Clone(simuTight.GetName().replace( 'fake_tight', 'fake_eff')) effSimu.SetDirectory(0) effSimu.Divide(simuLoose) print "eff(T|L) vs. ", variable def formatFloat(floats): return ["%.4f" % f for f in floats] print "efficiency data : ", formatFloat(getBinContents(effData)) print "efficiency simu : ", formatFloat(getBinContents(effSimu)) ratio = effData.Clone(outRatiohistoname) ratio.SetDirectory(0) ratio.Divide(effSimu) print "sf data/simu : ", formatFloat(getBinContents(ratio)) print " +/- : ", formatFloat(getBinErrors(ratio)) can = r.TCanvas('c_' + outRatiohistoname, outRatiohistoname, 800, 600) botPad, topPad = rootUtils.buildBotTopPads(can) can.cd() topPad.Draw() topPad.cd() pm = effData pm.SetStats(0) pm.Draw('axis') xAx, yAx = pm.GetXaxis(), pm.GetYaxis() xAx.SetTitle('') xAx.SetLabelSize(0) yAx.SetRangeUser(0.0, 0.25) textScaleUp = 1.0 / topPad.GetHNDC() yAx.SetLabelSize(textScaleUp * 0.04) yAx.SetTitleSize(textScaleUp * 0.04) yAx.SetTitle('#epsilon(T|L)') yAx.SetTitleOffset(yAx.GetTitleOffset() / textScaleUp) effSimu.SetLineColor(r.kRed) effSimu.SetMarkerStyle(r.kOpenCross) effSimu.SetMarkerColor(effSimu.GetLineColor()) effData.Draw('same') effSimu.Draw('same') leg = drawLegendWithDictKeys(topPad, { 'data': effData, 'simulation': simuTight }, legWidth=0.4) leg.SetHeader('scale factor ' + region + ' ' + ('electron' if '_el_' in outRatiohistoname else 'muon' if '_mu_' in outRatiohistoname else '')) can.cd() botPad.Draw() botPad.cd() ratio.SetStats(0) ratio.Draw() textScaleUp = 1.0 / botPad.GetHNDC() xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis() yAx.SetRangeUser(0.0, 2.0) xAx.SetTitle({ 'pt1': 'p_{T}', 'eta1': '|#eta|', 'pt1_eta1': 'p_{T}' }[variable]) yAx.SetNdivisions(-202) yAx.SetTitle('Data/Sim') yAx.CenterTitle() xAx.SetLabelSize(textScaleUp * 0.04) xAx.SetTitleSize(textScaleUp * 0.04) yAx.SetLabelSize(textScaleUp * 0.04) yAx.SetTitleSize(textScaleUp * 0.04) refLine = rootUtils.referenceLine(xAx.GetXmin(), xAx.GetXmax()) refLine.Draw() can.Update() outFname = os.path.join(outputDir, region + '_' + outRatiohistoname) for ext in ['.eps', '.png']: utils.rmIfExists(outFname + ext) can.SaveAs(outFname + ext) return { outRatiohistoname: ratio, outDataeffhistoname: effData, outDataeffhistoname.replace('_fake_rate_data_', '_tight_data_minus_prompt'): dataSubTight, outDataeffhistoname.replace('_fake_rate_data_', '_loose_data_minus_prompt'): dataSubLoose }
def plotHistRatioAndFit(histos, ratio, fitfunc, outfname, graphics_attributes={}): can = r.TCanvas('can_' + outfname, outfname, 800, 600) botPad, topPad = buildBotTopPads(can, splitFraction=0.35) can.cd() topPad.Draw() topPad.cd() pm = first(histos) pm.Draw('axis') xAx, yAx = pm.GetXaxis(), pm.GetYaxis() xAx.SetTitle('') xAx.SetLabelSize(0) yAx.SetRangeUser(0.0, 1.2) yAx.SetNdivisions( 505) # for some reason some of the inputs are already ratios with -201 textScaleUp = 1.0 / topPad.GetHNDC() yAx.SetLabelSize(textScaleUp * 0.04) yAx.SetTitleSize(textScaleUp * 0.04) yAx.SetTitle(graphics_attributes['ytitle']) yAx.SetTitleOffset(yAx.GetTitleOffset() / textScaleUp) for k, h in histos.iteritems(): h.SetMarkerStyle(graphics_attributes['markers'][k]) h.SetMarkerColor(graphics_attributes['colors'][k]) h.SetLineColor(graphics_attributes['colors'][k]) h.Draw('same') labels = graphics_attributes['labels'] drawLegendWithDictKeys(topPad, dict([(labels[s], histos[s]) for s in histos.keys()]), legWidth=0.4) can.cd() botPad.Draw() botPad.cd() ratio.Draw('axis') textScaleUp = 1.0 / botPad.GetHNDC() xAx, yAx = ratio.GetXaxis(), ratio.GetYaxis() yAx.SetRangeUser(0.0, 2.0) xAx.SetTitle(graphics_attributes['xtitle']) yAx.SetNdivisions(-202) yAx.SetTitle('Data/Sim') yAx.CenterTitle() for a in [xAx, yAx]: if a.GetLabelSize() > 0.04: continue a.SetLabelSize(a.GetLabelSize() * textScaleUp) a.SetTitleSize(a.GetTitleSize() * textScaleUp) a.SetTitleOffset(a.GetTitleOffset() / textScaleUp) ratio.SetMarkerStyle(graphics_attributes['markers']['data']) fitfunc.SetLineWidth(2) fitfunc.SetLineStyle(2) fitfunc.Draw('same') ratio.Draw('same') tex = r.TLatex() tex.SetNDC(True) p0, p0Err, chi2, ndf = fitResults(fitfunc) fitParLabel = "Const. fit : %s #pm %s" % (pdgRound(p0, p0Err)) fitGoodLabel = "#chi^{2}/DOF : %.2f / %d" % (chi2, ndf) tex.SetTextSize(yAx.GetTitleSize()) tex.SetTextFont(yAx.GetTitleFont()) tex.DrawLatex(0.15, 0.40, "#splitline{%s}{%s}" % (fitParLabel, fitGoodLabel)) can.Update() outfname = outfname + '.png' rmIfExists(outfname) # avoid root warnings can.SaveAs(outfname)
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 ''))