def plotStackedHistos(histos={}, datakey=None, stackkeys=[], outputDir='', region='', colors={}, verbose=False): "input: a dictionary of histos[group]" mkdirIfNeeded(outputDir) bkg_histos = dict([(k, h) for k, h in histos.iteritems() if k in stackkeys]) tot_bkg = summedHisto(bkg_histos.values(), label='') err_band = None # tmp disable # err_band = buildErrBandGraph(tot_bkg, computeStatErr2(tot_bkg)) empty_bkg = tot_bkg.Integral() == 0 if empty_bkg: if verbose: print "empty backgrounds, skip %s" % tot_bkg.GetName() return histoname = tot_bkg.GetName() can = r.TCanvas('c_' + histoname, histoname, 800, 600) can.cd() pm = tot_bkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_' + tot_bkg.GetName(), '') can.Update() r.SetOwnership(stack, False) for s, h in bkg_histos.iteritems(): h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') data = histos[datakey] if datakey and datakey in histos else None if data and data.GetEntries(): data.SetMarkerStyle(r.kFullDotLarge) data.Draw('p same') if verbose: print "data : nEntries {:.1f} totWeight {:.1f} ".format( data.GetEntries(), data.Integral()) yMin, yMax = getMinMax([h for h in [tot_bkg, data, err_band] if h]) # pm.SetMinimum(0.5) pm.SetMaximum(1.1 * yMax) can.Update() # can.SetLogy() topRightLabel(can, "#splitline{%s}{%s}" % (histoname, region), xpos=0.125, align=13) drawLegendWithDictKeys(can, dictSum(bkg_histos, {'stat err': err_band}), opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()] + [data] can.Update() if verbose: print os.path.join(outputDir, histoname + '.png') can.SaveAs(os.path.join(outputDir, histoname + '.png'))
def drawBottom(pad, totBkg, bkgHistos, sigHisto, llnjvar) : pad.cd() totBkg.SetStats(False) totBkg.SetMinimum(0.) # force this to avoid negative fluct due to fake totBkg.Draw('axis') pad.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_'+llnjvar,'') pad.Update() r.SetOwnership(stack, False) for s, h in bkgHistos.iteritems() : h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') pad.Update() sigHisto.SetLineColor(r.kRed) sigHisto.SetLineWidth(2*sigHisto.GetLineWidth()) sigHisto.Draw('same') pad.Update() topRightLabel(pad, llnjvar, xpos=0.125, align=13) drawLegendWithDictKeys(pad, dictSum(bkgHistos, {'signal' : sigHisto}), opt='f') pad.RedrawAxis() pad._stack = stack pad._histos = [h for h in stack.GetHists()] pad.Update()
def plotPerSourceEff(histosPerVar={}, outputDir='', lepton='', region='', sample='', verbose=False, zoomIn=True): "plot efficiency for each source (and 'anysource') as a function of each var; expect histos[var][source][loose,tight]" variables = histosPerVar.keys() sources = [s for s in first(histosPerVar).keys() if s!='real'] # only fake eff really need a scale factor colors = colorsLineSources mkdirIfNeeded(outputDir) for var in filter(lambda x : x in ['pt1', 'eta1'], histosPerVar.keys()): histosPerSource = dict((s, histosPerVar[var][s]) for s in sources) canvasBasename = region+'_efficiency_'+lepton+'_'+var+("_%s"%sample if sample else '') missingSources = [s for s, h in histosPerSource.iteritems() if not h['loose'] or not h['tight']] if missingSources: if verbose : print "skip %s, missing histos for %s"%(var, str(missingSources)) continue anySourceLoose = summedHisto([h['loose'] for h in histosPerSource.values()]) anySourceTight = summedHisto([h['tight'] for h in histosPerSource.values()]) anySourceLoose.SetName(histoNamePerSource(var, 'any', 'loose', region)) anySourceTight.SetName(histoNamePerSource(var, 'any', 'tight', region)) histosPerSource['any'] = { 'loose' : anySourceLoose, 'tight' : anySourceTight } emptyBkg = anySourceLoose.Integral()==0 or anySourceTight.Integral()==0 if emptyBkg: if verbose : print "empty backgrounds, skip %s"%canvasBasename continue def computeEfficiencies(histosPerSource={}) : sources = histosPerSource.keys() num = dict((s, histosPerSource[s]['tight']) for s in sources) den = dict((s, histosPerSource[s]['loose']) for s in sources) eff = dict((s, h.Clone(h.GetName().replace('tight', 'tight_over_loose'))) for s, h in num.iteritems()) [eff[s].Divide(den[s]) for s in sources] return eff effs = computeEfficiencies(histosPerSource) can = r.TCanvas('c_'+canvasBasename, canvasBasename, 800, 600) can.cd() pm = first(effs) # pad master pm.SetStats(False) pm.Draw('axis') can.Update() for s, h in effs.iteritems() : h.SetMarkerColor(colors[s] if s in colors else r.kBlack) h.SetLineColor(h.GetMarkerColor()) h.SetLineWidth(2*h.GetLineWidth()) h.SetMarkerStyle(markersSources[s] if s in markersSources else r.kDot) h.Draw('ep same') h.SetDirectory(0) #pprint.pprint(effs) yMin, yMax = getMinMax(effs.values()) pm.SetMinimum(0.0) pm.SetMaximum(0.25 if yMax < 0.5 and zoomIn else 1.1) can.Update() topRightLabel(can, canvasBasename, xpos=0.125, align=13) drawLegendWithDictKeys(can, effs, opt='lp') can.RedrawAxis() can._histos = effs can.Update() outFname = os.path.join(outputDir, canvasBasename+'.png') utils.rmIfExists(outFname) can.SaveAs(outFname)
def plotStackedHistos(histosPerGroup={}, outputDir='', region='', verbose=False): groups = histosPerGroup.keys() variables = first(histosPerGroup).keys() leptonTypes = first(first(histosPerGroup)).keys() colors = getGroupColor() mkdirIfNeeded(outputDir) histosPerName = dict([(region+'_'+var+'_'+lt, # one canvas for each histo, so key with histoname w/out group dict([(g, histosPerGroup[g][var][lt]) for g in groups])) for var in variables for lt in leptonTypes]) for histoname, histosPerGroup in histosPerName.iteritems(): missingGroups = [g for g, h in histosPerGroup.iteritems() if not h] if missingGroups: if verbose : print "skip %s, missing histos for %s"%(histoname, str(missingGroups)) continue bkgHistos = dict([(g, h) for g, h in histosPerGroup.iteritems() if g not in ['data', 'signal']]) totBkg = summedHisto(bkgHistos.values()) err_band = None # buildErrBandGraph(totBkg, computeStatErr2(totBkg)) emptyBkg = totBkg.Integral()==0 if emptyBkg: if verbose : print "empty backgrounds, skip %s"%histoname continue can = r.TCanvas('c_'+histoname, histoname, 800, 600) can.cd() pm = totBkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_'+histoname,'') can.Update() r.SetOwnership(stack, False) for s, h in bkgHistos.iteritems() : h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') data = histosPerGroup['data'] if data and data.GetEntries(): data.SetMarkerStyle(r.kFullDotLarge) data.Draw('p same') # yMin, yMax = getMinMax([h for h in [totBkg, data, err_band] if h]) # fixme with err_band yMin, yMax = 0.0, data.GetMaximum() pm.SetMinimum(0.0) pm.SetMaximum(1.1*yMax) can.Update() topRightLabel(can, histoname, xpos=0.125, align=13) # drawLegendWithDictKeys(can, dictSum(bkgHistos, {'stat err':err_band}), opt='f') drawLegendWithDictKeys(can, bkgHistos, opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()]+[data] can.Update() outFname = os.path.join(outputDir, histoname+'.png') utils.rmIfExists(outFname) can.SaveAs(outFname)
def plotStackedHistosSources(histosPerVar={}, outputDir='', region='', verbose=False): variables = histosPerVar.keys() sources = first(histosPerVar).keys() colors = colorsFillSources mkdirIfNeeded(outputDir) for var in variables: for lOrT in ['loose', 'tight']: histos = dict((s, histosPerVar[var][s][lOrT]) for s in sources) canvasBasename = region + '_region_' + var + '_' + lOrT missingSources = [s for s, h in histos.iteritems() if not h] if missingSources: if verbose: print "skip %s, missing histos for %s" % ( var, str(missingSources)) continue totBkg = summedHisto(histos.values()) err_band = None # buildErrBandGraph(totBkg, computeStatErr2(totBkg)) emptyBkg = totBkg.Integral() == 0 if emptyBkg: if verbose: print "empty backgrounds, skip %s" % canvasBasename continue can = r.TCanvas('c_' + canvasBasename, canvasBasename, 800, 600) can.cd() pm = totBkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_' + canvasBasename, '') can.Update() r.SetOwnership(stack, False) for s, h in histos.iteritems(): h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') yMin, yMax = getMinMax( [h for h in [totBkg, err_band] if h is not None]) pm.SetMinimum(0.0) pm.SetMaximum(1.1 * yMax) can.Update() topRightLabel(can, canvasBasename, xpos=0.125, align=13) # drawLegendWithDictKeys(can, dictSum(histos, {'stat err':err_band}), opt='f') drawLegendWithDictKeys(can, histos, opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()] can.Update() outFname = os.path.join(outputDir, canvasBasename + '.png') utils.rmIfExists(outFname) can.SaveAs(outFname)
def plotStackedHistosWithData(histosPerGroup={}, outputDir='', canvasname='', canvastitle='', colors={}, verbose=False): "histosPerGroup[group], where group=data is treated as special" groups = histosPerGroup.keys() mkdirIfNeeded(outputDir) missingGroups = [g for g, h in histosPerGroup.iteritems() if not h] if missingGroups: if verbose : print "skip %s, missing histos for %s"%(histoname, str(missingGroups)) return bkgHistos = dict([(g, h) for g, h in histosPerGroup.iteritems() if not isDataSample(g)]) totBkg = summedHisto(bkgHistos.values()) err_band = buildErrBandGraph(totBkg, computeStatErr2(totBkg)) emptyBkg = totBkg.Integral()==0 histoname, region = totBkg.GetName(), 'emu' # tmp replacement vars, to be fixed if emptyBkg: if verbose : print "empty backgrounds, skip %s"%histoname return can = r.TCanvas(canvasname, canvastitle, 800, 600) can.cd() pm = totBkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_'+histoname,'') can.Update() r.SetOwnership(stack, False) for s, h in bkgHistos.iteritems() : h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') err_band.Draw('E2 same') data = histosPerGroup['data'] if 'data' in histosPerGroup else None if data and data.GetEntries(): data.SetMarkerStyle(r.kFullDotLarge) data.Draw('p same') if verbose : print "integrals : {0} tot.bkg.: {1}, data: {2}".format(histoname, totBkg.Integral(), data.Integral()) else: print "no data" yMin, yMax = getMinMax([h for h in [totBkg, data, err_band] if h]) pm.SetMinimum(0.0) pm.SetMaximum(1.1*yMax) can.Update() topRightLabel(can, "#splitline{%s}{%s}"%(histoname, region), xpos=0.15, ypos=(1.0-0.5*can.GetTopMargin()), align=13) drawLegendWithDictKeys(can, dictSum(bkgHistos, {'stat err':err_band}), opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()]+[data] can.Update() filename=os.path.join(outputDir, histoname+'.png') rmIfExists(filename) can.SaveAs(filename)
def plotStackedHistosSources(histosPerVar={}, outputDir='', region='', verbose=False): variables = histosPerVar.keys() sources = first(histosPerVar).keys() colors = colorsFillSources mkdirIfNeeded(outputDir) for var in variables: for lOrT in ['loose', 'tight']: histos = dict((s, histosPerVar[var][s][lOrT]) for s in sources) canvasBasename = region+'_region_'+var+'_'+lOrT missingSources = [s for s, h in histos.iteritems() if not h] if missingSources: if verbose : print "skip %s, missing histos for %s"%(var, str(missingSources)) continue totBkg = summedHisto(histos.values()) err_band = None # buildErrBandGraph(totBkg, computeStatErr2(totBkg)) emptyBkg = totBkg.Integral()==0 if emptyBkg: if verbose : print "empty backgrounds, skip %s"%canvasBasename continue can = r.TCanvas('c_'+canvasBasename, canvasBasename, 800, 600) can.cd() pm = totBkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_'+canvasBasename,'') can.Update() r.SetOwnership(stack, False) for s, h in histos.iteritems() : h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') yMin, yMax = getMinMax([h for h in [totBkg, err_band] if h is not None]) pm.SetMinimum(0.0) pm.SetMaximum(1.1*yMax) can.Update() topRightLabel(can, canvasBasename, xpos=0.125, align=13) # drawLegendWithDictKeys(can, dictSum(histos, {'stat err':err_band}), opt='f') drawLegendWithDictKeys(can, histos, opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()] can.Update() outFname = os.path.join(outputDir, canvasBasename+'.png') utils.rmIfExists(outFname) can.SaveAs(outFname)
def plotStackedHistos(histos={}, datakey=None, stackkeys=[], outputDir='', region='', colors={}, verbose=False): "input: a dictionary of histos[group]" mkdirIfNeeded(outputDir) bkg_histos = dict([(k,h) for k,h in histos.iteritems() if k in stackkeys]) tot_bkg = summedHisto(bkg_histos.values(), label='') err_band = None # tmp disable # err_band = buildErrBandGraph(tot_bkg, computeStatErr2(tot_bkg)) empty_bkg = tot_bkg.Integral()==0 if empty_bkg: if verbose : print "empty backgrounds, skip %s"%tot_bkg.GetName() return histoname = tot_bkg.GetName() can = r.TCanvas('c_'+histoname, histoname, 800, 600) can.cd() pm = tot_bkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_'+tot_bkg.GetName(),'') can.Update() r.SetOwnership(stack, False) for s, h in bkg_histos.iteritems() : h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') data = histos[datakey] if datakey and datakey in histos else None if data and data.GetEntries(): data.SetMarkerStyle(r.kFullDotLarge) data.Draw('p same') if verbose: print "data : nEntries {:.1f} totWeight {:.1f} ".format(data.GetEntries(), data.Integral()) yMin, yMax = getMinMax([h for h in [tot_bkg, data, err_band] if h]) # pm.SetMinimum(0.5) pm.SetMaximum(1.1*yMax) can.Update() # can.SetLogy() topRightLabel(can, "#splitline{%s}{%s}"%(histoname, region), xpos=0.125, align=13) drawLegendWithDictKeys(can, dictSum(bkg_histos, {'stat err':err_band}), opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()]+[data] can.Update() if verbose : print os.path.join(outputDir, histoname+'.png') can.SaveAs(os.path.join(outputDir, histoname+'.png'))
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 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 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'))
def plotPerSourceEff(histosPerVar={}, outputDir='', lepton='', region='', sample='', verbose=False, zoomIn=True): "plot efficiency for each source (and 'anysource') as a function of each var; expect histos[var][source][loose,tight]" variables = histosPerVar.keys() sources = [s for s in first(histosPerVar).keys() if s != 'real'] # only fake eff really need a scale factor colors = colorsLineSources mkdirIfNeeded(outputDir) for var in filter(lambda x: x in ['pt1', 'eta1'], histosPerVar.keys()): histosPerSource = dict((s, histosPerVar[var][s]) for s in sources) canvasBasename = region + '_efficiency_' + lepton + '_' + var + ( "_%s" % sample if sample else '') missingSources = [ s for s, h in histosPerSource.iteritems() if not h['loose'] or not h['tight'] ] if missingSources: if verbose: print "skip %s, missing histos for %s" % (var, str(missingSources)) continue anySourceLoose = summedHisto( [h['loose'] for h in histosPerSource.values()]) anySourceTight = summedHisto( [h['tight'] for h in histosPerSource.values()]) anySourceLoose.SetName(histoNamePerSource(var, 'any', 'loose', region)) anySourceTight.SetName(histoNamePerSource(var, 'any', 'tight', region)) histosPerSource['any'] = { 'loose': anySourceLoose, 'tight': anySourceTight } emptyBkg = anySourceLoose.Integral() == 0 or anySourceTight.Integral( ) == 0 if emptyBkg: if verbose: print "empty backgrounds, skip %s" % canvasBasename continue def computeEfficiencies(histosPerSource={}): sources = histosPerSource.keys() num = dict((s, histosPerSource[s]['tight']) for s in sources) den = dict((s, histosPerSource[s]['loose']) for s in sources) eff = dict( (s, h.Clone(h.GetName().replace('tight', 'tight_over_loose'))) for s, h in num.iteritems()) [eff[s].Divide(den[s]) for s in sources] return eff effs = computeEfficiencies(histosPerSource) can = r.TCanvas('c_' + canvasBasename, canvasBasename, 800, 600) can.cd() pm = first(effs) # pad master pm.SetStats(False) pm.Draw('axis') can.Update() for s, h in effs.iteritems(): h.SetMarkerColor(colors[s] if s in colors else r.kBlack) h.SetLineColor(h.GetMarkerColor()) h.SetLineWidth(2 * h.GetLineWidth()) h.SetMarkerStyle(markersSources[s] if s in markersSources else r.kDot) h.Draw('ep same') h.SetDirectory(0) #pprint.pprint(effs) yMin, yMax = getMinMax(effs.values()) pm.SetMinimum(0.0) pm.SetMaximum(0.25 if yMax < 0.5 and zoomIn else 1.1) can.Update() topRightLabel(can, canvasBasename, xpos=0.125, align=13) drawLegendWithDictKeys(can, effs, opt='lp') can.RedrawAxis() can._histos = effs can.Update() outFname = os.path.join(outputDir, canvasBasename + '.png') utils.rmIfExists(outFname) can.SaveAs(outFname)
def plotStackedHistos(histosPerGroup={}, outputDir='', region='', verbose=False): groups = histosPerGroup.keys() variables = first(histosPerGroup).keys() leptonTypes = first(first(histosPerGroup)).keys() colors = getGroupColor() mkdirIfNeeded(outputDir) histosPerName = dict([ ( region + '_' + var + '_' + lt, # one canvas for each histo, so key with histoname w/out group dict([(g, histosPerGroup[g][var][lt]) for g in groups])) for var in variables for lt in leptonTypes ]) for histoname, histosPerGroup in histosPerName.iteritems(): missingGroups = [g for g, h in histosPerGroup.iteritems() if not h] if missingGroups: if verbose: print "skip %s, missing histos for %s" % (histoname, str(missingGroups)) continue bkgHistos = dict([(g, h) for g, h in histosPerGroup.iteritems() if g not in ['data', 'signal']]) totBkg = summedHisto(bkgHistos.values()) err_band = None # buildErrBandGraph(totBkg, computeStatErr2(totBkg)) emptyBkg = totBkg.Integral() == 0 if emptyBkg: if verbose: print "empty backgrounds, skip %s" % histoname continue can = r.TCanvas('c_' + histoname, histoname, 800, 600) can.cd() pm = totBkg # pad master pm.SetStats(False) pm.Draw('axis') can.Update() # necessary to fool root's dumb object ownership stack = r.THStack('stack_' + histoname, '') can.Update() r.SetOwnership(stack, False) for s, h in bkgHistos.iteritems(): h.SetFillColor(colors[s] if s in colors else r.kOrange) h.SetDrawOption('bar') h.SetDirectory(0) stack.Add(h) stack.Draw('hist same') # err_band.Draw('E2 same') data = histosPerGroup['data'] if data and data.GetEntries(): data.SetMarkerStyle(r.kFullDotLarge) data.Draw('p same') # yMin, yMax = getMinMax([h for h in [totBkg, data, err_band] if h]) # fixme with err_band yMin, yMax = 0.0, data.GetMaximum() pm.SetMinimum(0.0) pm.SetMaximum(1.1 * yMax) can.Update() topRightLabel(can, histoname, xpos=0.125, align=13) # drawLegendWithDictKeys(can, dictSum(bkgHistos, {'stat err':err_band}), opt='f') drawLegendWithDictKeys(can, bkgHistos, opt='f') can.RedrawAxis() can._stack = stack can._histos = [h for h in stack.GetHists()] + [data] can.Update() outFname = os.path.join(outputDir, histoname + '.png') utils.rmIfExists(outFname) 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 ''))