if obj.InheritsFrom("TH1") and signalMatch in name: if name.split('_')[-2] == "group": if "outliers" in name: drawThisBin = True name2D = 'W{ch}_{flav}_outliers'.format( ch=charge, flav=channel) title2D = 'W{chs}: |#eta| > {etamax}#; p_{{T}} #notin [{ptmin:.0f},{ptmax:.0f})'.format( etamax=genBins.etaBins[-1], ptmin=genBins.ptBins[0], ptmax=genBins.ptBins[-1], chs=chs) else: etabinIndex, ptbinIndex = get_ieta_ipt_from_process_name( name) if options.draw_all_bins: drawThisBin = True elif options.draw_selected_etaPt != '': if etabinIndex != ieta_sel or ptbinIndex != ipt_sel: drawThisBin = False else: drawThisBin = True else: drawThisBin = False name2D = 'W{ch}_{flav}_ieta_{ieta}_ipt_{ipt}'.format( ch=charge, flav=channel, ieta=etabinIndex, ipt=ptbinIndex) title2D = 'W{chs}: |#eta| #in [{etamin},{etamax})#; p_{{T}} #in [{ptmin:.0f},{ptmax:.0f})'.format( etamin=genBins.etaBins[etabinIndex],
pois_indices[impMat.GetXaxis().GetBinLabel(ib)] = ib nuisances = [] nuisances_indices = [] for ib in xrange(1, impMat.GetNbinsY() + 1): for n in nuis_regexps: if re.match(n, impMat.GetYaxis().GetBinLabel(ib)): nuisances.append(impMat.GetYaxis().GetBinLabel(ib)) nuisances_indices.append(ib) mat = {} pois = sorted(pois, key=lambda x: int(x.split('_')[-2]) if '_Ybin_' in x else 0) pois = sorted(pois, key=lambda x: get_ieta_ipt_from_process_name(x) if ('_ieta_' in x and '_ipt_' in x) else 0) # sort by charge if needed pois = sorted(pois, key=lambda x: 0 if "plus" in x else 1 if "minus" in x else 2) for ipoi, poi in enumerate(pois): for inuis, nuis in enumerate(nuisances): mat[(poi, nuis)] = impMat.GetBinContent(pois_indices[poi], nuisances_indices[inuis]) ## sort the pois and nuisances alphabetically, except for pdfs, which are sorted by number if len(options.nuisgroups) == 0: ## for mu* QCD scales, distinguish among muR and muRXX with XX in 1-10 nuisances = sorted(nuisances, key=lambda x: int(x.replace('pdf', ''))
th2_sub.GetZaxis().SetTitle("impact for {p} {units}".format( units='' if options.absolute else '(%)', p=poiName_target[options.target])) th2_sub.GetZaxis().SetTitleOffset(1.4) th2_sub.GetZaxis().SetTitleSize(0.05) th2_sub.GetZaxis().SetLabelSize(0.04) for p in pois: if charge != "allCharges" and not charge in p: continue if '_ieta_' in p and '_ipt_' in p: if options.absolute: val = mat[(p, nuisance[n])] else: val = 100 * mat[(p, nuisance[n])] / valuesAndErrors[p][ 0] if valuesAndErrors[p][0] != 0 else 0.0 ieta, ipt = get_ieta_ipt_from_process_name(p) th2_sub.SetBinContent(ieta + 1, ipt + 1, val) rmax = max(abs(th2_sub.GetMaximum()), abs(th2_sub.GetMinimum())) if not options.absolute: rmax = min(20., rmax) if options.absValue: th2_sub.GetZaxis().SetRangeUser(0, rmax) else: th2_sub.GetZaxis().SetRangeUser(-rmax, rmax) if options.zrange: if options.zrange == "template": rmin = th2_sub.GetBinContent(th2_sub.GetMinimumBin()) rmax = th2_sub.GetBinContent(th2_sub.GetMaximumBin()) else: rmin, rmax = (float(x) for x in options.zrange.split(',')) th2_sub.GetZaxis().SetRangeUser(rmin, rmax)
def niceName(name, genEtaPtBins="", forceLep="", drawRangeInLabel=False): if '_Ybin' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l: ' if 'plus' in name: nn += 'W+ ' elif 'minus' in name: nn += 'W- ' else: nn += 'W ' if 'left' in name: nn += 'left ' elif 'right' in name: nn += 'right ' elif 'long' in name: nn += 'long ' else: nn += 'unpolarized ' idx = -2 if (name.endswith('mu') or any( [x in name for x in ['masked', 'sumxsec', 'charge', 'a0', 'a4']])) else -1 nn += name.split('_')[idx] if 'eff_unc' in name: nn = '#epsilon_{unc}^{' + nn + '}' return nn elif '_ieta_' in name and '_ipt_' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l:' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ieta, ipt = get_ieta_ipt_from_process_name(name) nn += "i#eta, ip_{{T}} = {neta}, {npt} ".format(neta=ieta, npt=ipt) return nn elif '_ieta_' in name: # nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l:' # nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ieta = int((name.split("_ieta_")[1]).split("_")[0]) # nn += "i#eta = {neta}".format(neta=ieta) nn = "" if drawRangeInLabel: etal, etah = 0.0, 0.0 if genEtaPtBins: etal = genEtaPtBins.etaBins[ieta] etah = genEtaPtBins.etaBins[ieta + 1] wch = 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' lep = '#mu' if '_mu_' in name else 'el' if '_el_' in name else 'l' if forceLep: lep = '#mu' if forceLep == "mu" else 'e' if forceLep == "el" else 'l' nn = "{wch} |#eta^{{{lep}}}| #in [{etal:1.1f}, {etah:1.1f}]".format( wch=wch, lep=lep, etal=etal, etah=etah) else: nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l:' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' nn += "i#eta = {neta}".format(neta=ieta) return nn elif '_ipt_' in name: # nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l:' # nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ipt = int((name.split("_ipt_")[1]).split("_")[0]) # nn += "ip_{{T}} = {npt}".format(npt=ipt) nn = "" if drawRangeInLabel: ptl, pth = 0.0, 0.0 if genEtaPtBins: ptl = genEtaPtBins.ptBins[ipt] pth = genEtaPtBins.ptBins[ipt + 1] wch = 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' lep = '#mu' if '_mu_' in name else 'el' if '_el_' in name else 'l' if forceLep: lep = '#mu' if forceLep == "mu" else 'e' if forceLep == "el" else 'l' nn = "{wch} p_{{T}}({lep}) #in [{ptl:3g}, {pth:3g}]".format( wch=wch, lep=lep, ptl=ptl, pth=pth) else: nn = '#mu: ' if '_mu_' in name else 'el: ' if '_el_' in name else 'l:' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' nn += "ip_{{T}} = {npt}".format(npt=ipt) return nn elif "CMS_" in name: # keep Wmu or We now that we do combination, they are different sources # if "CMS_Wmu" in name: # return name.replace("CMS_Wmu_","") # elif "CMS_We" in name: # return name.replace("CMS_We_","") #else: # return name return name.replace("CMS_", "") elif re.match("Fakes(Eta|EtaCharge|PtNorm|PtSlope)Uncorrelated.*", name): num = re.findall(r'\d+', name) # get number pfx = name.split( num[0])[1] # split on number and read what's on the right leptonCharge = "" if len(pfx): leptonCharge = "{lep}{chs}".format( lep="#mu" if "mu" in pfx else "e", chs="+" if "plus" in pfx else "-" if "minus" in pfx else "") tmpvar = "" if "FakesEtaCharge" in name: tmpvar = "#eta-byCharge" elif "FakesEta" in name: tmpvar = "#eta" elif "FakesPtNorm" in name: tmpvar = "p_{T}-norm" elif "FakesPtSlope" in name: tmpvar = "p_{T}-slope" return "Fakes {var}-{n} {lepCh}".format(var=tmpvar, n=num[0], lepCh=leptonCharge) elif re.match(".*EffStat\d+.*", name): num = re.findall( r'\d+', name) # get number (there will be two of them, need the second) pfx = name.split("EffStat" + str(num[1]))[ 1] # split on second number and read what's on the right leptonCharge = "" if len(pfx): leptonCharge = "{lep}{chs}".format( lep="#mu" if "mu" in pfx else "e", chs="+" if "plus" in pfx else "-" if "minus" in pfx else "") return "Eff. uncorr. {n1}-{n2} {lepCh}".format(n1=num[0], n2=num[1], lepCh=leptonCharge) else: return name
reverse=False) elif any(re.match('FakesPtUncorrelated.*', x) for x in params): params = sorted(params, key=lambda x: utilities.getNFromString(x), reverse=False) elif any(re.match('.*EffStat.*', x) for x in params): params = sorted(params, key=lambda x: utilities.getNFromString(x), reverse=False) elif any('masked' in x or x.endswith('mu') for x in params): if any('_ieta_' in x for x in params): # differential xsection: get ieta, ipt index and use them as keys to sort params = sorted( params, key=lambda x: -1 if "_ieta_" not in x else get_ieta_ipt_from_process_name(x), reverse=False) else: params = sorted(params, key=lambda x: int(x.split('_')[-2]) if ('masked' in x or x.endswith('mu')) else -1, reverse=False) nuis_p_i = 0 title = "#theta" #hist_fit_s = ROOT.TH1F("fit_s" ,"S+B fit Nuisances ;;%s "%title,len(params),0,len(params)) hist_fit_s = ROOT.TH1F("fit_s", '', len(params), 0, len(params)) pmin, pmax = -3., 3. hist_fit_1d = ROOT.TH1F("fit_1d ", '', 50, pmin, pmax) hist_fit_1d_e = ROOT.TH1F("fit_1d_e", '', 50, pmin, pmax)
def getXsecs_etaPt(processes, systs, etaPtBins, infile): # in my case here, the histograms already have the cross section in pb, no need to divide by lumi #print "Inside getXsecs_etaPt" # etaPtBins is a list of 4 things: Netabins, etabins, Nptbins,ptbins histo_file = ROOT.TFile(infile, 'READ') hists = [] for process in processes: etabins = etaPtBins[1] ptbins = etaPtBins[3] charge = "plus" if "plus" in process else "minus" # check if gen eta binning starts from negative value (probably we will stick to |eta|, but just in case) etavar = "absetal1" if (float(etabins[0]) >= 0.0) else "etal1" cen_name = 'gen_ptl1_'+etavar+'_W'+charge+'_mu_central' cen_hist = histo_file.Get(cen_name) # this is a TH2 useEleXsec = False if not cen_hist: cen_name = cen_name.replace("_mu_","_el_") cen_hist = histo_file.Get(cen_name) useEleXsec = True #print cen_name if not cen_hist: print "Error in getXsecs_etaPt(): histogram %s not found in file %s. Exit" % (cen_name, infile) quit() # before searching the bin, sum epsilon to the y value being inspected # Root assigns the lower bin edge to the bin, while the upper bin edge is assigned to the adjacent bin. However, depending on the number y being used, # there can be a precision issue which might induce the selection of the wrong bin (since yfirst and yvalue are actually bin boundaries) # It seems odd, but I noticed that with the fake rate graphs (I was getting events migrating between adjacent eta bins) epsilon = 0.00001 # process has the form: Wplus_el_ieta_3_ipt_0_Wplus_el_group_0, where the number after group is not relevant (does not coincide with absolute bin number) # as it can be the same for many processes # one should use ieta and ipt, which identifies the template bin (first index is 0, not 1) # generally, ieta,ipt in name should correspond to 2D histogram X and Y bins (after adding 1) # However, one could use different definition for the gen level binning (used to cut and define a signal process) and the reco level one (defining the TH2) if "outliers" in process: # cross section for outliers = Integral(all) - integral(acceptance) totxsec = cen_hist.Integral(0, cen_hist.GetNbinsX()+1, 0, cen_hist.GetNbinsY()+1) ieta_low_acc = cen_hist.GetXaxis().FindFixBin(etabins[0]+epsilon) ieta_high_acc = cen_hist.GetXaxis().FindFixBin(etabins[-1]+epsilon) -1 ipt_low_acc = cen_hist.GetYaxis().FindFixBin(ptbins[0]+epsilon) ipt_high_acc = cen_hist.GetYaxis().FindFixBin(ptbins[-1]+epsilon) -1 xsecAccept = cen_hist.Integral(ieta_low_acc, ieta_high_acc, ipt_low_acc, ipt_high_acc) ncen = totxsec - xsecAccept else: ieta,ipt = get_ieta_ipt_from_process_name(process) etafirst = etabins[ieta] etalast = etabins[ieta+1] ptfirst = ptbins[ipt] ptlast = ptbins[ipt+1] # caution, we are using TH2, the logic below is slightly different than for TH1 istart_eta = cen_hist.GetXaxis().FindFixBin(etafirst + epsilon) iend_eta = cen_hist.GetXaxis().FindFixBin(etalast + epsilon) istart_pt = cen_hist.GetYaxis().FindFixBin(ptfirst + epsilon) iend_pt = cen_hist.GetYaxis().FindFixBin(ptlast + epsilon) ncen = cen_hist.Integral(istart_eta, iend_eta-1, istart_pt,iend_pt-1) tmp_hist = ROOT.TH1F('x_'+process,'x_'+process, 1, 0., 1.) ## normalize back to cross section tmp_hist.SetBinContent(1, ncen) hists.append(copy.deepcopy(tmp_hist)) for sys in systs: upn = sys+'Up' if not 'pdf' in sys else sys dnn = sys+'Dn' if not 'pdf' in sys else sys if useEleXsec: sys_upname = 'gen_ptl1_'+etavar+'_W'+charge+'_el_'+upn sys_dnname = 'gen_ptl1_'+etavar+'_W'+charge+'_el_'+dnn else: sys_upname = 'gen_ptl1_'+etavar+'_W'+charge+'_mu_'+upn sys_dnname = 'gen_ptl1_'+etavar+'_W'+charge+'_mu_'+dnn sys_up_hist = histo_file.Get(sys_upname) sys_dn_hist = histo_file.Get(sys_dnname) if not sys_up_hist: print "Error in getXsecs_etaPt(): histogram %s not found in file %s" % (sys_upname, infile) quit() if not sys_dn_hist: print "Error in getXsecs_etaPt(): histogram %s not found in file %s" % (sys_dnname, infile) quit() if "outliers" in process: totxsec = sys_up_hist.Integral(0, sys_up_hist.GetNbinsX()+1, 0, sys_up_hist.GetNbinsY()+1) xsecAccept = sys_up_hist.Integral(ieta_low_acc, ieta_high_acc, ipt_low_acc, ipt_high_acc) nup = totxsec - xsecAccept totxsec = sys_dn_hist.Integral(0, sys_dn_hist.GetNbinsX()+1, 0, sys_dn_hist.GetNbinsY()+1) xsecAccept = sys_dn_hist.Integral(ieta_low_acc, ieta_high_acc, ipt_low_acc, ipt_high_acc) ndn = totxsec - xsecAccept else: nup = sys_up_hist.Integral(istart_eta, iend_eta-1, istart_pt,iend_pt-1) ndn = sys_dn_hist.Integral(istart_eta, iend_eta-1, istart_pt,iend_pt-1) if 'pdf' in sys: ndn = 2.*ncen-nup ## or ncen/nup? tmp_hist_up = ROOT.TH1F('x_'+process+'_'+sys+'Up','x_'+process+'_'+sys+'Up', 1, 0., 1.) tmp_hist_up.SetBinContent(1, nup) tmp_hist_dn = ROOT.TH1F('x_'+process+'_'+sys+'Down','x_'+process+'_'+sys+'Dn', 1, 0., 1.) tmp_hist_dn.SetBinContent(1, ndn) hists.append(copy.deepcopy(tmp_hist_up)) hists.append(copy.deepcopy(tmp_hist_dn)) hist_data = ROOT.TH1F('x_data_obs', 'x_data_obs', 1, 0., 1.) hist_data.SetBinContent(1, 1.) hists.append(copy.deepcopy(hist_data)) return hists
def plotPars(inputFile, pois=None, selectString='', maxPullsPerPlot=30, plotdir='./', suffix='', analysis='helicity', excludeName=None, paramFamily=None,plotPull=False, channel='el', charge='plus', selection="",useMedian=False): chs = "+" if charge == "plus" else "-" isSignalStrength = False if paramFamily: if paramFamily == "pdf": pois="pdf.*" all_valuesAndErrors = utilities.getHistosFromToys(inputFile,getPull=plotPull,matchBranch=pois,excludeBranch=excludeName, selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "scale": pois="" for i in range(1,11): pois += "muR{n},muF{n},muRmuF{n},".format(n=str(i)) if pois.endswith(','): pois = pois[:-1] all_valuesAndErrors = utilities.getHistosFromToys(inputFile,getPull=plotPull,matchBranch=pois,excludeBranch=excludeName, selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "shapesyst": if channel == "el": pois = "alphaS,CMS_We_sig_lepeff,CMS_We_elescale,CMS_We_FRe_slope,CMS_We_FRe_continuous" else: pois = "alphaS,CMS_Wmu_sig_lepeff,CMS_Wmu_muscale,CMS_Wmu_FRmu_slope,CMS_Wmu_FR_continuous" all_valuesAndErrors = utilities.getHistosFromToys(inputFile,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "normsyst": if channel == "el": pois = "CMS_We_lepVeto,CMS_We_bkg_lepeff,CMS_VV,CMS_Top,CMS_W,CMS_DY,CMS_lumi_13TeV,CMS_We_flips,CMS_Wbkg" else: pois = "CMS_Wmu_lepVeto,CMS_Wmu_bkg_lepeff,CMS_VV,CMS_Top,CMS_W,CMS_DY,CMS_lumi_13TeV,CMS_Wbkg" all_valuesAndErrors = utilities.getHistosFromToys(inputFile,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "signalStrength": # add some exclusion matches if not already present if excludeName != None: excludeName += ",.*pmaskedexp.*" if "pmaskedexp" not in excludeName else "" else : excludeName = ".*pmaskedexp.*" pois="W{ch}.*_mu".format(ch=charge) isSignalStrength = True all_valuesAndErrors = utilities.getHistosFromToys(inputFile,200,0,2,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "absxsec": pois="W{ch}.*_pmaskedexp".format(ch=charge) if excludeName != None: excludeName += ",.*pmaskedexpnorm.*" if "pmaskedexpnorm" not in excludeName else "" else : excludeName = ".*pmaskedexpnorm.*" all_valuesAndErrors = utilities.getHistosFromToys(inputFile,1000,0,200,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) elif paramFamily == "normxsec": pois="W{ch}.*_pmaskedexpnorm".format(ch=charge) all_valuesAndErrors = utilities.getHistosFromToys(inputFile,1000,0,1,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) else: # warning: utilities.getHistosFromToys is working only for parameters centered around 0 (histogram is defined in -3,3) # this script will not work if using parameters with pmaskedexp if pois: if any (["pmaskedexp" in x for x in pois.split(",")]): print "Warning: current setup cannot work with parameters matching pmaskedexp: they are expected to be centered around 0. Exit" print "You might want to change behaviour of function utilities.getHistosFromToys to use those parameters as well" exit(0) if any ([wc in poi for wc in ["Wplus","Wminus"] for poi in pois.split(",")]): all_valuesAndErrors = utilities.getHistosFromToys(inputFile,200,0,2,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) if not any("pmaskedexp" in poi for poi in pois.split(",")): isSignalStrength = True else: all_valuesAndErrors = utilities.getHistosFromToys(inputFile,getPull=plotPull,matchBranch=pois, excludeBranch=excludeName,selection=selection,setStatOverflow=True,getMedian=useMedian) print "From the list of parameters it seems that you are plotting results for channel ",channel print "pois = ", pois valuesAndErrors = {} excludedNames = excludeName.split(',') if excludeName else [] if pois: poi_patts = pois.split(",") for ppatt in poi_patts: for (k,v) in all_valuesAndErrors.iteritems(): if any([re.match(x,k) for x in excludedNames]): continue if re.match(ppatt,k) and k not in valuesAndErrors: valuesAndErrors[k] = v #print valuesAndErrors.keys() params = valuesAndErrors.keys() print "----------------------------------" print "List of parameters:" print params print "----------------------------------" if any(re.match('pdf.*',x) for x in params): params = sorted(params, key = lambda x: int(x.split('pdf')[-1]), reverse=False) nPulls = 0 pullLabelSize = 0.028 pullSummaryMap = {} c = ROOT.TCanvas("c","",960,800) c.SetTickx(1) c.SetTicky(1) c.cd() c.SetLeftMargin(0.14) c.SetRightMargin(0.06) c.SetTopMargin(0.05) c.SetBottomMargin(0.2) c.cd() if isSignalStrength: chi2title = "#chi^{2} for signal strength parameters" chi2distr = ROOT.TH1D("chi2distr",chi2title,20,0.0,2.0) for name in params: print "Making pull for parameter ",name # look in utilities.py for getHistosFromToys(: if plotting pull, the histogram is defined as (x-x_gen)x_err, # while valuesAndErrors[name][0] and valuesAndErrors[name][1] are defined as mean and rms of original histogram from x # if option getPull of getHistosFromToys is False, then there is no difference mean_p, sigma_p = (valuesAndErrors[name][0],valuesAndErrors[name][1]-valuesAndErrors[name][0]) #mean_p, sigma_p = (valuesAndErrors[name][3].GetMean(),valuesAndErrors[name][3].GetStdDev()) histo = valuesAndErrors[name][3] if isSignalStrength and histo.GetStdDev() > 0.05: histo.Rebin(2) histo.Draw() histo.GetXaxis().SetTitle(histo.GetTitle()); histo.GetYaxis().SetTitle("number of toys (%d total)" % histo.Integral()); histo.SetTitle("") histo.GetYaxis().SetTitleOffset(1.05); histo.GetXaxis().SetTitleOffset(0.9); histo.GetYaxis().SetTitleSize(0.05); histo.GetXaxis().SetTitleSize(0.05); histo.GetXaxis().SetTitle(name) fitPull = histo.Integral()>0 if fitPull: histo.Fit("gaus","Q") fit = histo.GetFunction("gaus") fit.SetLineColor(4) lat.DrawLatex(0.16, 0.8, 'mean: {me:.3f}'.format(me=fit.GetParameter(1))) lat.DrawLatex(0.16, 0.7, 'err : {er:.2f}'.format(er=fit.GetParameter(2))) lat.DrawLatex(0.16, 0.6, 'chi2/ndf: {cn:.2f}'.format(cn=fit.GetChisquare()/fit.GetNDF() if fit.GetNDF()>0 else 999)) lat.DrawLatex(0.65, 0.8, 'mean: {me:.3f}'.format(me=histo.GetMean())) lat.DrawLatex(0.65, 0.7, 'RMS : {er:.2f}'.format(er=histo.GetStdDev())) if useMedian: lat.DrawLatex(0.65, 0.6, 'median: {me:.3f}'.format(me=mean_p)) # in this case mean_p stores the median if isSignalStrength: if fit.GetParameter(2) < 0.1: histo.GetXaxis().SetRangeUser(0.5,1.5) if fit.GetParameter(2) < 0.05: histo.GetXaxis().SetRangeUser(0.8,1.2) if fit.GetNDF()>0: chi2distr.Fill(fit.GetChisquare()/fit.GetNDF()) os.system('cp /afs/cern.ch/user/g/gpetrucc/php/index.php '+plotdir) distrdir = plotdir+'/pulldistr' createPlotDirAndCopyPhp(distrdir) for ext in ['png', 'pdf']: c.SaveAs("{pdir}/{name}_postfit_{ch}_{channel}{suffix}.{ext}".format(pdir=distrdir,name=name,ch=charge, suffix=("_"+suffix) if len(suffix) else "", channel=channel,ext=ext)) if fitPull: # tlatex = ROOT.TLatex(); tlatex.SetNDC(); # tlatex.SetTextSize(0.11) # tlatex.SetTextColor(4); # tlatex.DrawLatex(0.65,0.80,"Mean : %.3f #pm %.3f" % (histo.GetFunction("gaus").GetParameter(1),histo.GetFunction("gaus").GetParError(1))) # tlatex.DrawLatex(0.65,0.66,"Sigma : %.3f #pm %.3f" % (histo.GetFunction("gaus").GetParameter(2),histo.GetFunction("gaus").GetParError(2))) # tlatex.SetTextSize(0.11); # tlatex.SetTextColor(1); # tlatex.DrawLatex(0.65,0.33,"Post-fit #pm #sigma_{#theta}: %.3f #pm %.3f" % (mean_P p), sigma_p)) pullSummaryMap[name]=(histo.GetFunction("gaus").GetParameter(1),histo.GetFunction("gaus").GetParameter(2), histo.GetMean(),utilities.effSigma(histo)) nPulls += 1 if nPulls>0: if isSignalStrength: canvas_Chi2 = ROOT.TCanvas("canvas_Chi2","",700,700) canvas_Chi2.SetTickx(1) canvas_Chi2.SetTicky(1) chi2distr.Draw("Hist") chi2distr.GetXaxis().SetTitle("#chi^{2}") chi2distr.GetXaxis().SetTitleOffset(0.9) chi2distr.GetXaxis().SetTitleSize(0.05) chi2distr.GetYaxis().SetTitle("Events") chi2distr.GetYaxis().SetTitleOffset(1.05) chi2distr.GetYaxis().SetTitleSize(0.05) if chi2distr.GetEntries(): overflow = 100. * chi2distr.GetBinContent(chi2distr.GetNbinsX() + 1) / chi2distr.GetEntries() # use as XX %, multiplying by 100 else: overflow = 0 lat.DrawLatex(0.12, 0.8, 'W{ch} #rightarrow {lep}#nu'.format(ch=chs,lep="e" if channel == "el" else "#mu")) lat.DrawLatex(0.12, 0.7, 'mean: {me:.3f}'.format(me=chi2distr.GetMean())) lat.DrawLatex(0.12, 0.6, 'RMS : {er:.2f}'.format(er=chi2distr.GetStdDev())) lat.DrawLatex(0.12, 0.5, 'Overflow : {of:.1f}%'.format(of=overflow)) # following does not work, the stat box is disabled # chi2distr.SetStats(1) # canvas_Chi2.Update() # statBox = chi2distr.FindObject("stats") # if statBox: # statBox.SetX1NDC(0.62) # statBox.SetX2NDC(0.92) # statBox.SetY1NDC(0.59) # statBox.SetY2NDC(0.91) # statBox.SetFillColor(0) # statBox.SetFillStyle(0) # statBox.SetBorderSize(0) # statBox.Draw() # else: # print "Warning: no stat box found for Chi^2" for ext in ['png', 'pdf']: canvas_Chi2.SaveAs("{pdir}/chi2_{ch}_{channel}{suffix}.{ext}".format(pdir=distrdir,ch=charge, suffix=("_"+suffix) if len(suffix) else "", channel=channel,ext=ext)) print "Generating Pull Summaries...\n" nRemainingPulls = nPulls hc = ROOT.TCanvas("hc","",3000,2000); hc.SetTopMargin(0.05) hc.SetBottomMargin(0.2) hc.SetRightMargin(0.05) hc.SetGrid(0); pullPlots = 1; while nRemainingPulls > 0: nThisPulls = min(maxPullsPerPlot,nRemainingPulls) pull_rms = ROOT.TH1F("pull_rms_{pp}".format(pp=str(pullPlots)) ,"", 3*nThisPulls+1,0,nThisPulls*3+1); pull_effsigma = ROOT.TH1F("pull_effsigma_{pp}".format(pp=str(pullPlots)) ,"", 3*nThisPulls+1,0,nThisPulls*3+1); pull_median = ROOT.TH1F("pull_median_{pp}".format(pp=str(pullPlots)) ,"", 3*nThisPulls+1,0,nThisPulls*3+1); pi=1 sortedpulls = [] if 'pdf' in pois: sortedpulls = sorted(pullSummaryMap.keys(), key=lambda k: int(k.split('pdf')[-1])) # following condition should not happen (parameter is not centered around 0) #elif 'masked' in pois: #elif any([x in pois for x in ["Wplus","Wminus"]]) and not "masked" in pois: elif isSignalStrength or paramFamily == "absxsec" or paramFamily == "normxsec": keys = pullSummaryMap.keys() if analysis == "helicity": keys_l = list(k for k in keys if 'left' in k) keys_r = list(k for k in keys if 'right' in k) norms_l = sorted(keys_l, key=lambda k: int(k.split('_')[-1]), reverse=False) norms_r = sorted(keys_r, key=lambda k: int(k.split('_')[-1]), reverse=True) sortedpulls = norms_r + norms_l else: # differential xsection: get ieta, ipt index and use them as keys to sort tmpkeys = [x for x in keys if "outliers" not in x] sortedpulls = sorted(tmpkeys, key = lambda x: get_ieta_ipt_from_process_name(x)) for x in keys: if "outliers" in x: sortedpulls.append(x) else: sortedpulls = sorted(pullSummaryMap.keys(), key=lambda k: k[0]) # alphabetic order if len(sortedpulls)==0: break maxErr = 0; for name in sortedpulls: if pi>nThisPulls: break pull = pullSummaryMap[name] pull_rms.SetBinContent(3*pi+0,pull[0]) pull_rms.SetBinError(3*pi+0,pull[1]) pull_effsigma.SetBinContent(3*pi+1,pull[2]) pull_effsigma.SetBinError(3*pi+1,pull[3]) if useMedian: pull_median.SetBinContent(3*pi+1,mean_p) pull_median.SetBinError(3*pi+1,0) newname = name # if isSignalStrength: # newname = "_".join(name.split("_")[:6]) # elif paramFamily == "absxsec": # newname = "_".join(name.split("_")[:6]) + "_xsec" # elif paramFamily == "normxsec": # newname = "_".join(name.split("_")[:6]) + "_xsecNorm" if isSignalStrength or paramFamily == "absxsec" or paramFamily == "normxsec": newname = niceName(name) pull_rms.GetXaxis().SetBinLabel(3*pi,newname) pull_rms.GetXaxis().SetTickSize(0.) max2=max(pull[1],pull[3]) if max2>maxErr: maxErr=max2 del pullSummaryMap[name] pi += 1 nRemainingPulls -= 1 #pull_rms .SetMarkerColor(46); pull_rms .SetLineColor(45); pull_rms .SetLineWidth(2); pull_rms .SetMarkerSize(1.0); pull_rms .SetMarkerStyle(21); #pull_effsigma .SetMarkerColor(21); pull_effsigma .SetLineColor(24); pull_effsigma .SetLineWidth(2); pull_effsigma .SetMarkerSize(1.0); pull_effsigma .SetMarkerStyle(22) # NmaxChar = 1 # for name in sortedpulls: # NmaxChar = max(NmaxChar,len(name)) # if NmaxChar >= 8: # hc.SetBottomMargin(0.2) # hardcoded and not optimized I just looked at a random pull plot with the CMS_We_elescale and QCD scales parameters pull_rms .SetMarkerColor(ROOT.kRed+2); pull_rms .SetLineColor(ROOT.kRed); pull_rms .SetLineWidth(3); pull_rms .SetMarkerSize(1.0); pull_rms .SetMarkerStyle(21); pull_effsigma .SetMarkerColor(ROOT.kBlack); pull_effsigma .SetLineColor(ROOT.kGray+2); pull_effsigma .SetLineWidth(3); pull_effsigma .SetMarkerSize(1.0); pull_effsigma .SetMarkerStyle(22) pull_median.SetMarkerColor(ROOT.kBlack); pull_median.SetLineColor(ROOT.kBlack); pull_median.SetLineWidth(3); pull_median.SetMarkerSize(1.5); pull_median.SetMarkerStyle(20) pull_rms.SetLabelSize(pullLabelSize) pull_rms.LabelsOption("v") yrange = 3. if isSignalStrength and not plotPull: pull_rms.GetYaxis().SetRangeUser(0.9,1.1) else: pull_rms.GetYaxis().SetRangeUser(-yrange,yrange) if plotPull: pull_rms.GetYaxis().SetTitle("pull summary (n#sigma)") else: pull_rms.GetYaxis().SetTitle("POI summary") pull_rms.Draw("p") pull_effsigma.Draw("p same") line0 = ROOT.TLine(pull_rms.GetXaxis().GetXmin(), 0., pull_rms.GetXaxis().GetXmax(), 0.); line0.SetLineStyle(7) line0.Draw('same') leg = ROOT.TLegend(0.60, 0.75, 0.85, 0.95) leg.SetFillStyle(0) leg.SetBorderSize(0) leg.AddEntry(pull_rms,"Gaussian #sigma") leg.AddEntry(pull_effsigma,"Effective #sigma") leg.Draw("same") legMedian = ROOT.TLegend(0.15, 0.85, 0.4, 0.95) if useMedian: legMedian.SetFillStyle(0) legMedian.SetBorderSize(0) legMedian.AddEntry(pull_median,"Median","PL") legMedian.Draw("same") param_group=pois.replace('.*','').replace(',','_') for ext in ['png', 'pdf']: hc.SaveAs("{pdir}/pullSummaryToys_{params}_{igroup}_{ch}_{c}.{ext}".format(pdir=plotdir,ch=charge,suffix="_"+suffix,params=param_group,igroup=pullPlots,c=channel,ext=ext)) pullPlots += 1
def niceName(name): if '_Ybin' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' if 'plus' in name: nn += 'W+ ' elif 'minus' in name: nn += 'W- ' else: nn += 'W ' if 'left' in name: nn += 'left ' elif 'right' in name: nn += 'right ' elif 'long' in name: nn += 'long ' else: nn += 'unpolarized ' idx = -2 if (name.endswith('mu') or any([x in name for x in ['masked','sumxsec','charge','a0','a4']])) else -1 nn += name.split('_')[idx] if 'eff_unc' in name: nn = '#epsilon_{unc}^{'+nn+'}' return nn elif '_ieta_' and '_ipt_' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ieta,ipt = get_ieta_ipt_from_process_name(name) nn += "i#eta, ip_{{T}} = {neta}, {npt} ".format(neta=ieta,npt=ipt) return nn elif '_ieta_' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ieta = int((name.split("_ieta_")[1]).split("_")[0]) nn += "i#eta = {neta}".format(neta=ieta) return nn elif '_ipt_' in name: nn = '#mu: ' if '_mu_' in name else 'el: ' nn += 'W+ ' if 'plus' in name else 'W- ' if 'minus' in name else 'W ' ipt = int((name.split("_ipt_")[1]).split("_")[0]) nn += "i#p_{{T}} = {npt}".format(npt=ipt) return nn elif "CMS_" in name: if "CMS_Wmu" in name: return name.replace("CMS_Wmu_","") elif "CMS_We" in name: return name.replace("CMS_We_","") else: return name elif re.match( "Fakes(Eta|Pt)Uncorrelated.*",name): num = re.findall(r'\d+', name) # get number pfx = name.split(num[0])[1] # split on number and read what's on the right leptonCharge = "" if len(pfx): leptonCharge = "{lep}{chs}".format(lep="#mu" if "mu" in pfx else "e", chs = "+" if "plus" in pfx else "-" if "minus" in pfx else "") return "Fakes {var}-uncorr.{n} {lepCh}".format(var="#eta" if "FakesEta" in name else "p_{T}", n=num[0], lepCh=leptonCharge) elif re.match(".*EffStat\d+.*",name): num = re.findall(r'\d+', name) # get number (there will be two of them, need the second) pfx = name.split("EffStat"+str(num[1]))[1] # split on second number and read what's on the right leptonCharge = "" if len(pfx): leptonCharge = "{lep}{chs}".format(lep="#mu" if "mu" in pfx else "e", chs = "+" if "plus" in pfx else "-" if "minus" in pfx else "") return "Eff. uncorr. {n1}-{n2} {lepCh}".format(n1=num[0],n2=num[1],lepCh=leptonCharge) else: return name
# to help sorting with helicity # if using more helicity and Y bins, sort by hel,Ybin helSorted = { "left" : 1, "right" : 2, "long" : 3} chargeSorted = { "Wplus" : 1, "Wminus" : 2} ## sort the floatParams. alphabetically, except for pdfs, which are sorted by number ## for mu* QCD scales, distinguish among muR and muRXX with XX in 1-10 # why is this commented? Isn't it nice that different charge and polarizations are grouped together? # see old example here: # http://mciprian.web.cern.ch/mciprian/wmass/13TeV/helicityAnalysis/electron/fromEmanuele/13-12-18/fitresults_poim1_exp1_bbb1/subMatrix/smallCorrelation_Wplusminus_leftrightYbin_2-5.png #params = sorted(params, key= lambda x: (int(chargeSorted[x.split('_')[0]]),int(helSorted[x.split('_')[1]]),int(x.split('_')[-1])) if '_Ybin_' in x else 0) # one might want to invert the order of charge and helicity for the sorting params = sorted(params, key= lambda x: utilities.getNFromString(x) if '_Ybin_' in x else 0) params = sorted(params, key= lambda x: get_ieta_ipt_from_process_name(x) if ('_ieta_' in x and '_ipt_' in x) else 0) params = sorted(params, key= lambda x: int(x.replace('pdf','')) if 'pdf' in x else 0) params = sorted(params, key= lambda x: int(x.replace('muRmuF','')) if ('muRmuF' in x and x != "muRmuF") else 0) params = sorted(params, key= lambda x: int(x.replace('muR','')) if (''.join([j for j in x if not j.isdigit()]) == 'muR' and x != "muR") else 0) params = sorted(params, key= lambda x: int(x.replace('muF','')) if (''.join([j for j in x if not j.isdigit()]) == 'muF' and x != "muF") else 0) params = sorted(params, key= lambda x: utilities.getNFromString(x) if 'EffStat' in x else 0) params = sorted(params, key= lambda x: utilities.getNFromString(x) if 'FakesEtaUncorrelated' in x else 0) params = sorted(params, key= lambda x: utilities.getNFromString(x) if 'FakesPtUncorrelated' in x else 0) # sort by charge if needed # I think it is useful that different charges are separated in the matrix, it is easier to read it params = sorted(params, key = lambda x: 0 if "plus" in x else 1 if "minus" in x else 2) print "sorted params = ", params c = ROOT.TCanvas("c","",1200,800) c.SetGridx()