width = 0.4 textsize = 0.04 xstart = 0.4 ystart = 0.75 latex = TLatex() latex.SetNDC() latex.SetTextAlign(12) latex.SetTextSize(textsize) latex.DrawLatex(xstart, 0.85, "CMS Preliminary") latex.DrawLatex(xstart, 0.80, "4.96 fb^{-1}, #sqrt{s}=7TeV") legend = TLegend(xstart, ystart - 7.7 * textsize, xstart + width, ystart) legend.SetFillColor(0) legend.SetTextSize(textsize) legend.SetColumnSeparation(0.0) legend.SetEntrySeparation(0.1) legend.SetMargin(0.2) # Add data to legend if nj == "2": legend.AddEntry(rooplot.findObject("h_dataset"), "2-jet Data", "p") elif nj == "3": legend.AddEntry(rooplot.findObject("h_dataset"), "3-jet Data", "p") elif nj == "4": legend.AddEntry(rooplot.findObject("h_dataset"), "#geq4-jet Data", "p") elif nj == "23": legend.AddEntry(rooplot.findObject("h_dataset"), "2+3-jet Data", "p") # Add functions to legend for func in ["f1", "f2", "f3"]:
def plotter(plotdir, plot, xTitle, logY, channels=['VV'], includeData=False, scaleSignal=0, UserRange=[None, None, None, None], initPath=''): # channelTex={'WPWP':'W^{+}W^{+}', 'WPWM':'W^{+}W^{-}','WMWM':'W^{-}W^{-}','WPZ':'W^{+}Z','WMZ':'W^{-}Z','ZZ':'ZZ'} channelTex = {'ZZ': 'ZZ'} # plotstyle=[(1,1),(1,2),(2,1),(2,2),(4,1),(4,2)] plotstyle = [(1, 1)] # 0 1 2 3 4 5 6 # Backgrounds=['QCD', 'WJetsToQQ_HT600ToInf', 'ZJetsToQQ_HT600ToInf', 'TT', 'WW', 'WZ', 'ZZ'] # BGColors= [rt.kAzure+7, rt.kRed-4, rt.kOrange-2, rt.kGreen+2, rt.kOrange+7, rt.kBlue+1, rt.kMagenta+2] # BGTeX= ['QCD', 'W+JetsToQQ', 'Z+JetsToQQ', 'TTbar'], 'WW', 'WZ', 'ZZ'] #stackOrder= [4,5,6,2,1,3,0] Backgrounds = [ 'QCD' ] # 'WJetsToQQ_HT600ToInf', 'ZJetsToQQ_HT600ToInf', 'ZZ'] 'TT', 'WW', 'WZ', 'ZZ'] BGColors = [ rt.kAzure + 7 ] #, rt.kOrange-2, rt.kGreen+2], rt.kOrange+7, rt.kBlue+1, rt.kMagenta+2] BGTeX = ['QCD'] # stackOrder = [0] PreSelection = [ 'nocuts', 'common', 'corrections', 'cleaner', 'softdropmassCorr', 'AK4pfidfilter', 'AK8pfidfilter', 'invMAk8sel', 'detaAk8sel', 'AK8N2sel' ] Selection = [ 'preselection', 'softdropAK8sel', 'tau21sel', 'deltaR48', 'VVRegion', 'AK4N2sel', 'OpSignsel', 'detaAk4sel', 'invMAk4sel_1p0', 'Kin_AK8' ] cutnames = [ 'cleaner', 'AK8N2sel', 'invMAk8sel', 'detaAk8sel', 'softdropAK8sel', 'tau21sel', 'AK4cleaner', 'AK4N2sel', 'OpSignsel', 'detaAk4sel', 'invMAk4sel_1p0' ] cuts = { 'cleaner': '#splitline{p_{T-AK8} > 200 GeV, |#eta_{AK8}| < 2.5}{p_{T-AK4} > 30 GeV, |#eta_{AK4}| < 5.0}', 'AK8N2sel': 'N_{AK8} #geq 2', 'invMAk8sel': 'M_{jj-AK8} > 1050 GeV', 'detaAk8sel': '|#Delta#eta_{jj-AK8}|<1.3', 'softdropAK8sel': '65 GeV <M_{SD}< 105 GeV', 'tau21sel': '0 #leq #tau_{2}/#tau_{1}<0.45', # 'AK4cleaner':'p_{T-AK4} > 30 GeV, |#eta_{AK4}| < 5.0', 'AK4cleaner': '', 'AK4N2sel': 'N_{AK4} #geq 2', 'OpSignsel': '#eta_{1-AK4} #eta_{2-AK4} < 0', 'detaAk4sel': '|#Delta#eta_{jj-AK4}| > 3.0', 'invMAk4sel_1p0': 'M_{jj-AK4} > 1.0 TeV' } VV = ('VV' in channels) seperate = (not VV) if VV: #channels=["WPWP","WPWM","WMWM","WPZ","WMZ","ZZ"] channels = ["ZZ"] plottitle = plotdir + '_' + plot lumi = 36.814 xLabelSize = 18. yLabelSize = 18. xTitleSize = 20. yTitleSize = 22. xTitleOffset = 4. yTitleOffset = 1.3 printout = False Portrait = True cutname = False ratio = includeData if ('highbin' in plot): binning = 'dijetbinning' else: binning = 'default' if (Portrait): canvX = 600 canvY = 600 else: canvX = 900 canvY = 675 if (plotdir in PreSelection): region = 'PreSelection' initPath = '' referenceHistPath = 'detaAk8sel/N_pv' if (PreSelection.index(plotdir) < 4 and ('softdrop' in plot)): return 'SofdropMass not filled yet!' else: region = 'SignalRegion' referenceHistPath = 'tau21sel/N_pv' referenceHistPath = plotdir + '/' + plot if (initPath == ''): path = '/nfs/dust/cms/user/loemkerj/bachelor/CMSSW_10_2_16/src/UHH2/aQGCVVjjhadronic/%s' % region else: path = initPath outputPath = path.replace( '/nfs/dust/cms/user/loemkerj/bachelor/CMSSW_10_2_16/src/UHH2/aQGCVVjjhadronic/SignalRegion', 'plots/') if (plotdir in PreSelection): CutNumber = PreSelection.index(plotdir) else: CutNumber = Selection.index(plotdir) outputPath = outputPath + '/%02i_%s' % (CutNumber, plotdir) + '/' if (printout): print('InputPath:', path) print('OutputPath:', outputPath) #check if OutputPath exists - and if not create it! if not os.path.exists(outputPath): os.makedirs(outputPath) # path='/home/albrec/Master/signal/' scaleVV = (scaleSignal != 0) VVScale = scaleSignal if (UserRange[2] == None or UserRange[3] == None): YRangeUser = False Ymin = UserRange[2] Ymax = UserRange[3] else: YRangeUser = True Ymin = UserRange[2] Ymax = UserRange[3] if (UserRange[0] == None or UserRange[1] == None): XRangeUser = False Xmin = UserRange[0] Xmax = UserRange[1] else: XRangeUser = True Xmin = UserRange[0] Xmax = UserRange[1] # YRangeUser=False # Ymin=0.11 # Ymax=9*10**3 # XRangeUser=False # Xmin=0 # Xmax=6000. gROOT.ProcessLine("gErrorIgnoreLevel = 2001;") SFiles = [] for i in range(len(channels)): SFiles.append( TFile( path + "/uhh2.AnalysisModuleRunner.MC.MC_aQGC_%sjj_hadronic_2016v3.root" % channels[i])) #uhh2.AnalysisModuleRunner.MC.MC_aQGC_ZZjj_hadronic_2016v3.root ##Open Files to get BackgroundHist: BFiles = [] for i in range(len(Backgrounds)): BFiles.append( TFile(path + "/uhh2.AnalysisModuleRunner.MC.MC_%s.root" % Backgrounds[i])) #Open File to get DataHist: # DataFile = TFile(path+"/uhh2.AnalysisModuleRunner.Data.DATA.root") #gROOT.ProcessLine( "gErrorIgnoreLevel = 0;") if (includeData == True): #calculate QCDscale with Integrals from the following Histogram: # referenceHistPath = 'tau21sel/N_AK4' # referenceHistPath = 'detaAk8sel/N_pv' # referenceHistPath = 'tau21sel/met_pt_over_sumptAK8_2' QCDscale = float(DataFile.Get(referenceHistPath).Integral()) QCDNorm = 1 for i in range(len(BFiles)): if ('QCD' in BFiles[i].GetName()): QCDNorm = float(BFiles[i].Get(referenceHistPath).Integral()) else: QCDscale -= float(BFiles[i].Get(referenceHistPath).Integral()) QCDscale /= QCDNorm else: QCDscale = 1.0 if (printout): print('using QCDscale:', QCDscale) SHists = [] for i in range(len(channels)): SHists.append(SFiles[i].Get(plotdir + '/' + plot)) BHists = [] for i in range(len(BFiles)): BHists.append(BFiles[i].Get(plotdir + '/' + plot)) if ('QCD' in BFiles[i].GetName()): BHists[-1].Scale(QCDscale) #if(includeData): # DataHist=DataFile.Get(plotdir+'/'+plot) if (binning == 'dijetbinning'): fitbinning = array('d') binwidth = 200 NBins = (14000 / binwidth) - ((1040 / binwidth) + 1) NBins = int(NBins) for i in range(NBins + 1): fitbinning.append(1050 + i * binwidth) for i in range(len(channels)): SHists[i] = SHists[i].Rebin(NBins, "new binning", fitbinning) for i in range(len(Backgrounds)): BHists[i] = BHists[i].Rebin(NBins, "new binning", fitbinning) if (includeData): DataHist = DataHist.Rebin(NBins, "new binning", fitbinning) canv = TCanvas(plottitle, plottitle, canvX, canvY) yplot = 0.7 yratio = 0.3 ymax = 1.0 xmax = 1.0 xmin = 0.0 if (ratio): plotpad = TPad("plotpad", "Plot", xmin, ymax - yplot, xmax, ymax) ratiopad = TPad("ratiopad", "Ratio", xmin, ymax - yplot - yratio, xmax, ymax - yplot) else: plotpad = TPad("plotpad", "Plot", xmin, ymax - yplot - yratio, xmax, ymax) plotpad.SetTopMargin(0.08) plotpad.SetLeftMargin(0.1) plotpad.SetRightMargin(0.05) plotpad.SetTicks() plotpad.Draw() if (ratio): plotpad.SetBottomMargin(0.016) ratiopad.SetTopMargin(0.016) ratiopad.SetBottomMargin(0.35) ratiopad.SetLeftMargin(0.1) ratiopad.SetRightMargin(0.05) ratiopad.SetTicks() ratiopad.Draw() else: plotpad.SetBottomMargin(0.1) if (logY): plotpad.SetLogy() canv.SetLogy() if ('-logX' in xTitle): plotpad.SetLogx() if (ratio): ratiopad.SetLogx() canv.SetLogx() drawOptions = "HE" stack = THStack(plottitle, plottitle) BHist = THStack(plottitle, plottitle) # for i in range(len(Backgrounds)): for i in stackOrder: # BHists[i].SetFillColor(BGColors[i]) BHists[i].SetLineColor(BGColors[i]) BHist.Add(BHists[i], 'Hist') BHist.SetTitle(plottitle) BHistErr = BHists[0].Clone() for i in range(1, len(Backgrounds)): BHistErr.Add(BHists[i]) BHistErr.SetFillStyle(3204) BHistErr.SetFillColor(rt.kGray + 2) BHistErr.SetLineColor(1) BGMax = BHist.GetMaximum() SIGMax = 0 #if(VV): #new # SIGMax=VVsum.GetMaximum() #else: for i in range(len(channels)): tmpmax = SHists[i].GetMaximum() if (tmpmax > SIGMax): SIGMax = tmpmax if (scaleVV): SIGMax = SIGMax * VVScale if (logY): MAX = 0.9 * float(10**(magnitude(max(BGMax, SIGMax)) + 1)) MIN = float(10**(magnitude(max(BGMax, SIGMax)) - 5)) MIN += float(10**(magnitude(MIN))) legendMIN = math.log(max(BGMax, SIGMax)) / math.log(MAX) else: MAX = (1.0 / 0.8) * max(BGMax, SIGMax) legendMIN = 0.7 MIN = 0. legendMIN = (legendMIN * 0.7) + 0.3 - 0.016 legend = TLegend(0.5, 0.75, 0.85, 0.89) legend.SetFillStyle(0) legend.SetTextSize(0.02) legend.SetMargin(0.4) legend.SetNColumns(2) legend.SetColumnSeparation(0.3) if (includeData): DataHist.SetMarkerStyle(8) DataHist.SetLineColor(1) DataHist.SetTitle(plottitle) if VV: for i in range(len(channels)): if (i == 0): VVsum = SHists[i].Clone() else: VVsum.Add(SHists[i]) legentry = 'VVjj' if (scaleVV): VVsum.Scale(VVScale) legentry += ' *%0.f' % VVScale VVsum.SetLineColor(1) VVsum.SetLineStyle(plotstyle[0][1]) VVsum.SetLineWidth(2) legend.AddEntry(VVsum, legentry) else: for i in range(len(channels)): SHists[i].SetLineColor(plotstyle[i][0]) SHists[i].SetLineStyle(plotstyle[i][1]) SHists[i].SetLineWidth(2) legentry = "%sjj" % channelTex[channels[i]] if (scaleVV): SHists[i].Scale(VVScale) legentry += ' *%.2E' % VVScale legend.AddEntry(SHists[i], legentry) for i in stackOrder: legend.AddEntry(BHists[i], BGTeX[i], "f") legend.AddEntry(BHistErr, "stat. Uncertainty", "f") if (includeData): legend.AddEntry(DataHist, "Data", "lep") canv.SetTitle(plottitle) BHistErr.GetYaxis().SetTitle('Events') BHistErr.GetYaxis().SetRangeUser(MIN, MAX) BHistErr.GetYaxis().SetTitleFont(43) BHistErr.GetYaxis().SetTitleSize(yTitleSize) BHistErr.GetYaxis().SetTitleOffset(yTitleOffset) BHistErr.GetYaxis().SetLabelFont(43) BHistErr.GetYaxis().SetLabelSize(yLabelSize) if (ratio): BHistErr.GetXaxis().SetTitleSize(0.0) BHistErr.GetXaxis().SetLabelSize(0.0) else: BHistErr.GetXaxis().SetTitle(xTitle) BHistErr.GetXaxis().SetTitleFont(43) BHistErr.GetXaxis().SetTitleSize(xTitleSize) # BHistErr.GetXaxis().SetTitleOffset(xTitleOffset) BHistErr.GetXaxis().SetTitleOffset(1.2) BHistErr.GetXaxis().SetLabelFont(43) BHistErr.GetXaxis().SetLabelSize(xLabelSize) # BHistErr.GetXaxis().SetTickLength(0.08) # BHistErr.GetXaxis().SetNdivisions(506) if (YRangeUser): BHistErr.GetYaxis().SetRangeUser(Ymin, Ymax) if (XRangeUser): BHistErr.GetXaxis().SetRangeUser(Xmin, Xmax) plotpad.cd() BHistErr.Draw("E2") BHist.Draw("HistSAME") BHistErr.Draw("E2SAME") if (VV): VVsum.Draw("SAME" + drawOptions) elif ('-noSig' not in xTitle): for i in range(len(channels)): SHists[i].Draw("SAME" + drawOptions) if (includeData): DataHist.Draw("APE1SAME") plotpad.RedrawAxis() if (ratio): ratiopad.cd() if (includeData): ratioHist = DataHist.Clone() else: ratioHist = BHistErr.Clone() ratioHist.SetLineColor(rt.kBlack) # ratioHist.Sumw2() ratioHist.SetStats(0) ratioHist.Divide(BHistErr) ratioHist.SetMarkerStyle(21) ratioHist.SetMarkerSize(0.7) #Yaxis ratioHist.GetYaxis().SetRangeUser(0.3, 1.7) ratioHist.GetYaxis().SetTitle("Data/BG") ratioHist.GetYaxis().CenterTitle() ratioHist.GetYaxis().SetTitleFont(43) ratioHist.GetYaxis().SetTitleSize(yTitleSize) ratioHist.GetYaxis().SetTitleOffset(yTitleOffset) ratioHist.GetYaxis().SetLabelFont(43) ratioHist.GetYaxis().SetLabelSize(yLabelSize) ratioHist.GetYaxis().SetNdivisions(506) #Xaxis ratioHist.GetXaxis().SetTitle(xTitle) ratioHist.GetXaxis().SetTitleFont(43) ratioHist.GetXaxis().SetTitleSize(xTitleSize) ratioHist.GetXaxis().SetTitleOffset(xTitleOffset) ratioHist.GetXaxis().SetLabelFont(43) ratioHist.GetXaxis().SetLabelSize(xLabelSize) ratioHist.GetXaxis().SetTickLength(0.08) ratioHist.GetXaxis().SetNdivisions(506) # if(YRangeUser): # ratioHist.GetYaxis().SetRangeUser(Ymin,Ymax) if (XRangeUser): ratioHist.GetXaxis().SetRangeUser(Xmin, Xmax) ratioXMin = Xmin ratioXMax = Xmax else: ratioXMin = ratioHist.GetXaxis().GetXmin() ratioXMax = ratioHist.GetXaxis().GetXmax() ratioHist.Draw("ep") zeropercent = TLine(ratioXMin, 1, ratioXMax, 1) zeropercent.Draw() plus10percent = TLine(ratioXMin, 1.1, ratioXMax, 1.1) plus10percent.SetLineStyle(rt.kDashed) plus10percent.Draw() minus10percent = TLine(ratioXMin, 0.9, ratioXMax, 0.9) minus10percent.SetLineStyle(rt.kDashed) minus10percent.Draw() canv.cd() gPad.RedrawAxis() legend.Draw() latex = TLatex() latex.SetNDC(kTRUE) latex.SetTextSize(20) latex.DrawLatex(0.69, 0.953, "%.2f fb^{-1} (13 TeV)" % lumi) latex.DrawLatex(0.1, 0.953, "private work") lastcut = 'nocuts' for cut in cutnames: if cut in plotdir: lastcut = cut if (not (lastcut == 'nocuts') and cutname): # latex.SetTextSize(0.03) latex.SetTextSize(15) for l in range(cutnames.index(lastcut) + 1): latex.DrawLatex(0.12, 0.8 - l * 0.04, cuts[cutnames[l]]) canv.Update() canv.Print(outputPath + '/%s_%s.pdf' % (plotdir, plot)) #prevents memory leak in Canvas Creation/Deletion #see: https://root.cern.ch/root/roottalk/roottalk04/2484.html gSystem.ProcessEvents() if (ratio): del ratiopad del plotpad, canv # gc.collect() return 'done!'
class DataMCPlot(object): '''Handles a Data vs MC plot. Features a list of histograms (some of them being stacked), and several Drawing functions. ''' _f_keeper = {} _t_keeper = {} def __init__(self, name): self.histosDict = {} self.histos = [] self.supportHist = None self.name = name self.stack = None self.legendOn = True self.legend = None # self.legendBorders = 0.20, 0.46, 0.44, 0.89 # self.legendPos = 'left' self.legendBorders = 0.20, 0.78, 0.80, 0.88 self.legendPos = 'top' # self.lastDraw = None # self.lastDrawArgs = None self.nostack = None self.blindminx = None self.blindmaxx = None self.groups = {} self.axisWasSet = False self.histPref = histPref def __contains__(self, name): return name in self.histosDict def __getitem__(self, name): return self.histosDict[name] def readTree(self, file_name, tree_name='tree', verbose=False, friend_func=None): '''Cache files/trees''' if file_name in self.__class__._t_keeper: ttree = self.__class__._t_keeper[file_name] if verbose: print 'got cached tree', ttree else: tfile = self.__class__._f_keeper[file_name] = TFile.Open(file_name) ttree = self.__class__._t_keeper[file_name] = tfile.Get(tree_name) if verbose: print 'read tree', ttree, 'from file', file_name if friend_func: file_name = friend_func(file_name) friend_tree = self.readTree(file_name, tree_name, verbose) ttree.AddFriend(friend_tree) gROOT.cd() return ttree def Blind(self, minx, maxx, blindStack): self.blindminx = minx self.blindmaxx = maxx if self.stack and blindStack: self.stack.Blind(minx, maxx) if self.nostack: for hist in self.nostack: if hist.style.drawAsData: hist.Blind(minx, maxx) def AddHistogram(self, name, histo, layer=0, legendLine=None, stack=True): '''Add a ROOT histogram, with a given name. Histograms will be drawn by increasing layer.''' tmp = Histogram(name, histo, layer, legendLine, stack=stack) self.histos.append(tmp) self.histosDict[name] = tmp return tmp def Group(self, groupName, namesToGroup, layer=None, style=None, silent=False): '''Group all histos with names in namesToGroup into a single histo with name groupName. All histogram properties are taken from the first histogram in namesToGroup. See UnGroup as well ''' groupHist = None realNames = [] actualNamesInGroup = [] for name in namesToGroup: hist = self.histosDict.get(name, None) if hist is None: if not silent: print 'warning, no histo with name', name continue if groupHist is None: groupHist = hist.Clone(groupName) self.histos.append(groupHist) self.histosDict[groupName] = groupHist else: groupHist.Add(hist) actualNamesInGroup.append(name) realNames.append(hist.realName) hist.on = False if groupHist: self.groups[groupName] = actualNamesInGroup groupHist.realName = ','.join(realNames) if style is not None: groupHist.SetStyle(style) self._ApplyPrefs() def UnGroup(self, groupName): '''Ungroup groupName, recover the histograms in the group''' group = self.groups.get(groupName, None) if group is None: print groupName, 'is not a group in this plot.' return for name in group: self.histosDict[name].on = True self.histosDict[groupName].on = False def Replace(self, name, pyhist): '''Not very elegant... should have a clone function in Histogram...''' oldh = self.histosDict.get(name, None) if oldh is None: print 'histogram', name, 'does not exist, cannot replace it.' return pythist = copy.deepcopy(pyhist) pythist.layer = oldh.layer pythist.stack = oldh.stack pythist.name = oldh.name pythist.legendLine = oldh.legendLine pythist.SetStyle(oldh.style) pythist.weighted.SetFillStyle(oldh.weighted.GetFillStyle()) index = self.histos.index(oldh) self.histosDict[name] = pythist self.histos[index] = pythist def _SortedHistograms(self, reverse=False): '''Returns the histogram dictionary, sorted by increasing layer, excluding histograms which are not "on". This function is used in all the Draw functions.''' byLayer = sorted(self.histos, key=attrgetter('layer')) byLayerOn = [hist for hist in byLayer if (hist.on is True)] if reverse: byLayerOn.reverse() return byLayerOn def Hist(self, histName): '''Returns a histogram. Print the DataMCPlot object to see which histograms are available.''' return self.histosDict[histName] def DrawNormalized(self, opt=''): '''All histograms are drawn as PDFs, even the stacked ones''' same = '' for hist in self._SortedHistograms(): hist.obj.DrawNormalized(same + opt) if same == '': same = 'same' self.DrawLegend() if TPad.Pad(): TPad.Pad().Update() # self.lastDraw = 'DrawNormalized' # self.lastDrawArgs = [ opt ] def Draw(self, opt=''): '''All histograms are drawn.''' same = '' self.supportHist = None for hist in self._SortedHistograms(): if self.supportHist is None: self.supportHist = hist hist.Draw(same + opt) if same == '': same = 'same' # set_trace() yaxis = self.supportHist.GetYaxis() yaxis.SetRangeUser(0.01, 1.5 * ymax(self._SortedHistograms())) self.DrawLegend() if TPad.Pad(): TPad.Pad().Update() # self.lastDraw = 'Draw' # self.lastDrawArgs = [ opt ] def CreateLegend(self, ratio=False, print_norm=False): if self.legend is None: self.legend = TLegend(*self.legendBorders) self.legend.SetFillColor(0) self.legend.SetFillStyle(0) self.legend.SetLineColor(0) self.legend.SetLineWidth(1) self.legend.SetNColumns(5) # number of comps / 2 (or 3) + 1 self.legend.SetEntrySeparation(0.2) self.legend.SetColumnSeparation(0.2) self.legend.SetBorderSize(0) self.legend.SetMargin(0.25) else: self.legend.Clear() hists = self._SortedHistograms(reverse=True) if ratio: hists = hists[:-1] # removing the last histo. for index, hist in enumerate(hists): if print_norm: if not hist.legendLine: hist.legendLine = hist.name hist.legendLine += ' ({norm:.1f})'.format(norm=hist.Yield()) hist.AddEntry(self.legend) def DrawLegend(self, ratio=False, print_norm=False): '''Draw the legend.''' if self.legendOn: self.CreateLegend(ratio=ratio, print_norm=print_norm) self.legend.Draw('same') def DrawRatio(self, opt=''): '''Draw ratios : h_i / h_0. h_0 is the histogram with the smallest layer, and h_i, i>0 are the other histograms. if the DataMCPlot object contains N histograms, N-1 ratio plots will be drawn. To take another histogram as the denominator, change the layer of this histogram by doing: dataMCPlot.Hist("histName").layer = -99 ''' same = '' denom = None self.ratios = [] for hist in self._SortedHistograms(): if denom == None: denom = hist continue ratio = copy.deepcopy(hist) ratio.obj.Divide(denom.obj) ratio.obj.Draw(same) self.ratios.append(ratio) if same == '': same = 'same' self.DrawLegend(ratio=True) if TPad.Pad(): TPad.Pad().Update() # self.lastDraw = 'DrawRatio' # self.lastDrawArgs = [ opt ] def DrawDataOverMCMinus1(self, ymin=-0.5, ymax=0.5): stackedHists = [] dataHist = None for hist in self._SortedHistograms(): if hist.stack is False: dataHist = hist continue stackedHists.append(hist) self._BuildStack(stackedHists, ytitle='Data/MC') mcHist = self.BGHist() if dataHist == None: dataHist = mcHist # this was added to avoid crashes for SR plots (without data) self.dataOverMCHist = copy.deepcopy(dataHist) # self.dataOverMCHist.Add(mcHist, -1) self.dataOverMCHist.Divide(mcHist) self.dataOverMCHist.Draw() yaxis = self.dataOverMCHist.GetYaxis() yaxis.SetRangeUser(ymin + 1., ymax + 1.) yaxis.SetTitle('Data/MC') yaxis.SetNdivisions(5) fraclines = 0.2 if ymax <= 0.2 or ymin >= -0.2: fraclines = 0.1 self.DrawRatioLines(self.dataOverMCHist, fraclines, 1.) if TPad.Pad(): TPad.Pad().Update() def DrawRatioStack(self, opt='', xmin=None, xmax=None, ymin=None, ymax=None): '''Draw ratios. The stack is considered as a single histogram.''' denom = None # import pdb; pdb.set_trace() histForRatios = [] denom = None for hist in self._SortedHistograms(): if hist.stack is False: # if several unstacked histograms, the highest layer is used denom = hist continue histForRatios.append(hist) self._BuildStack(histForRatios, ytitle='MC/Data') self.stack.Divide(denom.obj) if self.blindminx and self.blindmaxx: self.stack.Blind(self.blindminx, self.blindmaxx) self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) self.ratios = [] for hist in self.nostack: if hist is denom: continue ratio = copy.deepcopy(hist) ratio.obj.Divide(denom.obj) ratio.obj.Draw('same') self.ratios.append(ratio) self.DrawLegend(ratio=True) self.DrawRatioLines(denom, 0.2, 1) if TPad.Pad(): TPad.Pad().Update() def DrawNormalizedRatioStack(self, opt='', xmin=None, xmax=None, ymin=None, ymax=None): '''Draw ratios. The stack is considered as a single histogram. All histograms are normalized before computing the ratio''' denom = None histForRatios = [] for hist in self._SortedHistograms(): # taking the first histogram (lowest layer) # as the denominator histogram. if denom == None: denom = copy.deepcopy(hist) continue # other histograms will be divided by the denominator histForRatios.append(hist) self._BuildStack(histForRatios, ytitle='MC p.d.f. / Data p.d.f.') self.stack.Normalize() denom.Normalize() self.stack.Divide(denom.weighted) self.stack.Draw(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) self.ratios = [] for hist in self.nostack: # print 'nostack ', hist ratio = copy.deepcopy(hist) ratio.Normalize() ratio.obj.Divide(denom.weighted) ratio.obj.Draw('same') self.ratios.append(ratio) self.DrawLegend(ratio=True) self.DrawRatioLines(denom, 0.2, 1) if TPad.Pad(): TPad.Pad().Update() # self.lastDraw = 'DrawNormalizedRatioStack' # self.lastDrawArgs = [ opt ] def DrawRatioLines(self, hist, frac=0.2, y0=1.): '''Draw a line at y = 1, at 1+frac, and at 1-frac. hist is used to get the x axis range.''' xmin = hist.obj.GetXaxis().GetXmin() xmax = hist.obj.GetXaxis().GetXmax() line = TLine() line.DrawLine(xmin, y0, xmax, y0) line.SetLineStyle(2) line.DrawLine(xmin, y0 + frac, xmax, y0 + frac) line.DrawLine(xmin, y0 - frac, xmax, y0 - frac) def GetStack(self): '''Returns stack; builds stack if not there yet''' if not self.stack: self._BuildStack(self._SortedHistograms(), ytitle='Events') return self.stack def BGHist(self): return self.GetStack().totalHist def SignalHists(self): return [h for h in self.nostack if not h.style.drawAsData] def DrawStack(self, opt='', xmin=None, xmax=None, ymin=None, ymax=None, print_norm=False, scale_signal=''): '''Draw all histograms, some of them in a stack. if Histogram.stack is True, the histogram is put in the stack. scale_signal: mc_int -> scale to stack integral''' self._BuildStack(self._SortedHistograms(), ytitle='Events') same = 'same' if len(self.nostack) == 0: same = '' self.supportHist = None for hist in self.nostack: if hist.style.drawAsData: hist.Draw('SAME' if self.supportHist else '') else: if scale_signal == 'mc_int': hist.Scale(hist.Yield(weighted=True) / self.stack.integral) hist.Draw('SAME HIST' if self.supportHist else 'HIST') if not self.supportHist: self.supportHist = hist self.stack.Draw(opt + same, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) if self.supportHist is None: self.supportHist = self.BGHist() if not self.axisWasSet: mxsup = self.supportHist.weighted.GetBinContent( self.supportHist.weighted.GetMaximumBin()) mxstack = self.BGHist().weighted.GetBinContent( self.BGHist().weighted.GetMaximumBin()) mx = max(mxsup, mxstack) if ymin is None: ymin = 0.01 if ymax is None: ymax = mx * 2 centrality = self.supportHist.weighted.GetRMS() / ( self.supportHist.weighted.GetXaxis().GetXmax() - self.supportHist.weighted.GetXaxis().GetXmin()) if centrality > 0.15: ymax = mx * 2.2 self.supportHist.GetYaxis().SetRangeUser(ymin, ymax) self.axisWasSet = True for hist in self.nostack: if self.blindminx and hist.style.drawAsData: hist.Blind(self.blindminx, self.blindmaxx) if hist.style.drawAsData: hist.Draw('SAME') else: hist.Draw('SAME HIST') if self.supportHist.weighted.GetMaximumBin( ) < self.supportHist.weighted.GetNbinsX() / 2: # self.legendBorders = 0.62, 0.46, 0.88, 0.89 self.legendBorders = 0.20, 0.78, 0.80, 0.88 # self.legendPos = 'right' self.legendPos = 'top' self.DrawLegend(print_norm=print_norm) if TPad.Pad(): TPad.Pad().Update() # set_trace() def DrawNormalizedStack(self, opt='', xmin=None, xmax=None, ymin=0.001, ymax=None): '''Draw all histograms, some of them in a stack. if Histogram.stack is True, the histogram is put in the stack. all histograms out of the stack, and the stack itself, are shown as PDFs.''' self._BuildStack(self._SortedHistograms(), ytitle='p.d.f.') self.stack.DrawNormalized(opt, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) for hist in self.nostack: hist.obj.DrawNormalized('same') self.DrawLegend() if TPad.Pad(): TPad.Pad().Update() # self.lastDraw = 'DrawNormalizedStack' # self.lastDrawArgs = [ opt ] def Rebin(self, factor): '''Rebin, and redraw.''' # the dispatching technique is not too pretty, # but keeping a self.lastDraw function initialized to one of the Draw functions # when calling it creates a problem in deepcopy. for hist in self.histos: hist.Rebin(factor) self.axisWasSet = False def NormalizeToBinWidth(self): '''Normalize each Histograms bin to the bin width.''' for hist in self.histos: hist.NormalizeToBinWidth() def WriteDataCard(self, filename=None, verbose=True, mode='RECREATE', dir=None, postfix=''): '''Export current plot to datacard''' if not filename: filename = self.name + '.root' outf = TFile(filename, mode) if dir and outf.Get(dir): print 'Directory', dir, 'already present in output file' if any( outf.Get(dir + '/' + hist.name + postfix) for hist in self._SortedHistograms()): print 'Recreating file because histograms already present' outf = TFile(filename, 'RECREATE') if dir: outf_dir = outf.Get(dir) if not outf_dir: outf_dir = outf.mkdir(dir) outf_dir.cd() for hist in self._SortedHistograms(): 'Writing', hist, 'as', hist.name hist.weighted.Write(hist.name + postfix) outf.Write() def _BuildStack(self, hists, ytitle=None): '''build a stack from a list of Histograms. The histograms for which Histogram.stack is False are put in self.nostack''' self.stack = None self.stack = Stack(self.name + '_stack', ytitle=ytitle) self.nostack = [] for hist in hists: if hist.stack: self.stack.Add(hist) else: self.nostack.append(hist) def _GetHistPref(self, name): '''Return the preference dictionary for a given component''' thePref = None for prefpat, pref in self.histPref.iteritems(): if fnmatch.fnmatch(name, prefpat): if thePref is not None: print 'several matching preferences for', name thePref = pref if thePref is None: print 'cannot find preference for hist', name thePref = {'style': Style(), 'layer': 999, 'legend': name} return thePref def _ApplyPrefs(self): for hist in self.histos: pref = self._GetHistPref(hist.name) hist.layer = pref['layer'] hist.SetStyle(pref['style']) hist.legendLine = pref['legend'] def __str__(self): if self.stack is None: self._BuildStack(self._SortedHistograms(), ytitle='Events') tmp = [' '.join(['DataMCPlot: ', self.name])] tmp.append('Histograms:') for hist in self._SortedHistograms(reverse=True): tmp.append(' '.join(['\t', str(hist)])) tmp.append( 'Stack yield = {integ:7.1f}'.format(integ=self.stack.integral)) return '\n'.join(tmp)
def drawlegend(self, position=None, **kwargs): """Create and draw legend. Legend position can be controlled in several ways drawlegend(position) drawlegend(position=position) where position is a string which can contain the horizontal position, e.g. 'left', 'center', 'right', 'L', 'C', 'R', 'x=0.3', ... where 'x' is the position (between 0 an 1) of the left side in the frame. The position string can also contain the vertical position as e.g. 'top', 'middle', 'bottom', 'T', 'M', 'B', 'y=0.3', ... where 'y' is the position (between 0 an 1) of the top side in the frame. Instead of the strings, the exact legend coordinates can be controlled with the keywords x1, x2, y1 and y2, or, x1, y1, width and height: drawlegend(x1=0.2,width=0.4) drawlegend(x1=0.2,width=0.4,y1=0.9,height=0.4) drawlegend(x1=0.2,x2=0.6,y1=0.9,y2=0.4) These floats are normalized to the axis frame, ignoring the canvas margins: x=0 is the left, x=1 is the right, y=0 is the bottom and y=1 is the top side. Values less than 0, or larger than 1, will put the legend outside the frame. """ #if not ratio: # tsize *= 0.80 # signaltsize *= 0.80 verbosity = LOG.getverbosity(self, kwargs) hists = self.hists errstyle = 'lep' if gStyle.GetErrorX() else 'ep' entries = kwargs.get('entries', []) bands = kwargs.get('band', [self.errband]) # error bands bands = ensurelist(bands, nonzero=True) bandentries = kwargs.get('bandentries', []) title = kwargs.get('header', None) title = kwargs.get('title', title) # legend header/title latex = kwargs.get('latex', True) # automatically format strings as LaTeX style = kwargs.get('style', None) style0 = kwargs.get('style0', None) # style of first histogram errstyle = kwargs.get('errstyle', errstyle) # style for an error point styles = kwargs.get('styles', []) position = kwargs.get('pos', position) # legend position position = kwargs.get('position', position) or self.position option = kwargs.get('option', '') border = kwargs.get('border', False) transparent = kwargs.get('transparent', True) x1_user = kwargs.get('x1', None) # legend left side x2_user = kwargs.get('x2', None) # legend right side y1_user = kwargs.get('y1', None) # legend top side y2_user = kwargs.get('y2', None) # legend bottom side width = kwargs.get('width', -1) # legend width height = kwargs.get('height', -1) # legend height tsize = kwargs.get('tsize', _lsize) # text size twidth = kwargs.get('twidth', None) or 1 # scalefactor for legend width theight = kwargs.get('theight', None) or 1 # scalefactor for legend height texts = kwargs.get('text', []) # extra text below legend ncols = kwargs.get('ncols', self.ncols) or 1 # number of legend columns colsep = kwargs.get('colsep', 0.06) # seperation between legend columns bold = kwargs.get('bold', True) # bold legend header panel = kwargs.get('panel', 1) # panel (top=1, bottom=2) texts = ensurelist(texts, nonzero=True) entries = ensurelist(entries, nonzero=False) bandentries = ensurelist(bandentries, nonzero=True) headerfont = 62 if bold else 42 # CHECK LOG.insist(self.canvas, "Canvas does not exist!") self.canvas.cd(panel) scale = 485. / min(gPad.GetWh() * gPad.GetHNDC(), gPad.GetWw() * gPad.GetWNDC()) tsize *= scale # text size # ENTRIES #if len(bandentries)==len(bands) and len(entries)>len(hists): # for band, bandtitle in zip(band,bandentries): # entries.insert(hists.index(band),bandtitle) while len(entries) < len(hists): entries.append(hists[len(entries)].GetTitle()) while len(bandentries) < len(bands): bandentries.append(bands[len(bandentries)].GetTitle()) hists = hists + bands entries = entries + bandentries if latex: title = maketitle(title) entries = [maketitle(e) for e in entries] texts = [maketitle(t) for t in texts] maxlen = estimatelen([title] + entries + texts) # STYLES if style0: styles[0] = style0 while len(styles) < len(hists): hist = hists[len(styles)] if hist in bands: styles.append('f') elif style != None: styles.append(style) elif hasattr(hist, 'GetFillStyle') and hist.GetFillStyle() > 0: styles.append('f') elif 'E0' in hist.GetOption() or 'E1' in hist.GetOption(): styles.append(errstyle) else: styles.append('lp') # NUMBER of LINES nlines = sum([1 + e.count('\n') for e in entries]) #else: nlines += 0.80 if texts: nlines += sum([1 + t.count('\n') for t in texts]) if ncols > 1: nlines /= float(ncols) if title: nlines += 1 + title.count('\n') # DIMENSIONS if width < 0: width = twidth * max(0.22, min(0.60, 0.036 + 0.016 * maxlen)) if height < 0: height = theight * 1.34 * tsize * nlines if ncols > 1: width *= ncols / (1 - colsep) x2 = 0.90 x1 = x2 - width y1 = 0.92 y2 = y1 - height # POSITION if position == None: position = "" position = position.replace('left', 'L').replace( 'center', 'C').replace('right', 'R').replace( #.lower() 'top', 'T').replace('middle', 'M').replace('bottom', 'B') if not any(c in position for c in 'TMBy'): # set default vertical position += 'T' if not any(c in position for c in 'LCRx'): # set default horizontal position += 'RR' if ncols > 1 else 'R' # if title else 'L' if 'C' in position: if 'R' in position: center = 0.57 elif 'L' in position: center = 0.43 else: center = 0.50 x1 = center - width / 2 x2 = center + width / 2 elif 'LL' in position: x1 = 0.03 x2 = x1 + width elif 'L' in position: x1 = 0.08 x2 = x1 + width elif 'RR' in position: x2 = 0.97 x1 = x2 - width elif 'R' in position: x2 = 0.92 x1 = x2 - width elif 'x=' in position: x1 = float(re.findall(r"x=(\d\.\d+)", position)[0]) x2 = x1 + width if 'M' in position: if 'T' in position: middle = 0.57 elif 'B' in position: middle = 0.43 else: middle = 0.50 y1 = middle - height / 2 y2 = middle + height / 2 elif 'TT' in position: y2 = 0.97 y1 = y2 - height elif 'T' in position: y2 = 0.92 y1 = y2 - height elif 'BB' in position: y1 = 0.03 y2 = y1 + height elif 'B' in position: y1 = 0.08 y2 = y1 + height elif 'y=' in position: y2 = float(re.findall(r"y=(\d\.\d+)", position)[0]) y1 = y2 - height if x1_user != None: x1 = x1_user x2 = x1 + width if x2_user == None else x2_user if y1_user != None: y1 = y1_user y2 = y1 - height if y2_user == None else y2_user L, R = gPad.GetLeftMargin(), gPad.GetRightMargin() T, B = gPad.GetTopMargin(), gPad.GetBottomMargin() X1, X2 = L + (1 - L - R) * x1, L + ( 1 - L - R) * x2 # convert frame to canvas coordinates Y1, Y2 = B + (1 - T - B) * y1, B + ( 1 - T - B) * y2 # convert frame to canvas coordinates legend = TLegend(X1, Y1, X2, Y2) LOG.verb( "Plot.drawlegend: position=%r, height=%.3f, width=%.3f, (x1,y1,x2,y2)=(%.2f,%.2f,%.2f,%.2f), (X1,Y1,X2,Y2)=(%.2f,%.2f,%.2f,%.2f)" % (position, height, width, x1, y1, x2, y2, X1, Y1, X2, Y2), verbosity, 1) # MARGIN if ncols >= 2: margin = 0.090 / width else: margin = 0.044 / width legend.SetMargin(margin) # STYLE if transparent: legend.SetFillStyle(0) # 0 = transparent else: legend.SetFillColor(0) legend.SetBorderSize(border) legend.SetTextSize(tsize) legend.SetTextFont(headerfont) # bold for title if ncols > 1: legend.SetNColumns(ncols) legend.SetColumnSeparation(colsep) # HEADER if title: legend.SetHeader(title) legend.SetTextFont(42) # no bold for entries # ENTRIES if hists: for hist1, entry1, style1 in columnize(zip(hists, entries, styles), ncols): for entry in entry1.split('\n'): legend.AddEntry(hist1, entry, style1) hist1, style1 = 0, '' for line in texts: legend.AddEntry(0, line, '') if verbosity >= 2: print ">>> Plot.drawlegend: title=%r, texts=%s, latex=%s" % ( title, texts, latex) print ">>> Plot.drawlegend: hists=%s" % (hists) print ">>> Plot.drawlegend: entries=%s" % (entries) print ">>> Plot.drawlegend: styles=%s" % (styles) print ">>> Plot.drawlegend: nlines=%s, len(hists)=%s, len(texts)=%s, ncols=%s, margin=%s" % ( nlines, len(hists), len(texts), ncols, margin) legend.Draw(option) self.legends.append(legend) return legend
def plotPostFitValues(channel,var,DM,year,*parameters,**kwargs): """Draw post-fit values for parameter using MultiDimFit and FitDiagnostics output.""" if DM=='DM0' and 'm_2' in var: return print green("\n>>> plotPostFitValues %s, %s"%(DM, var)) if len(parameters)==1 and isinstance(parameters[0],list): parameters = parameters[0] parameters = [p.replace('$CAT',DM).replace('$CHANNEL',channel) for p in list(parameters)] title = kwargs.get('title', "" ) name = kwargs.get('name', "" ) indir = kwargs.get('indir', "output_%d"%year ) outdir = kwargs.get('outdir', "postfit_%d"%year ) tag = kwargs.get('tag', "" ) plotlabel = kwargs.get('plotlabel', "" ) compareFD = kwargs.get('compareFD', False ) and N==1 era = "%d-13TeV"%year isBBB = any("_bin_" in p for p in parameters) filename = '%s/higgsCombine.%s_%s-%s%s-%s.MultiDimFit.mH90.root'%(indir,channel,var,DM,tag,era) filenamesFD = '%s/fitDiagnostics.%s_%s-%s%s-%s_TES*p*.root'%(indir,channel,var,DM,tag,era) ensureDirectory(outdir) if not name: name = formatParameter(parameters[0]).replace('_'+DM,'') if len(parameters)>1: name = "comparison_%s"%(name) #re.sub(r"bin_\d+","bin",name) canvasname = "%s/postfit-%s_%s_%s%s%s"%(outdir,name,var,DM,tag,plotlabel) print '>>> file "%s"'%(filename) graphs = [ ] graphsFD = [ ] tvals = [ ] pvals = [ -2.2, +2.2 ] tes = measureTES(filename) for parameter in parameters[:]: graph = getTGraphOfParameter(filename,'tes',parameter,xvals=tvals,yvals=pvals) if graph: graphs.append(graph) else: parameters.remove(parameter) if compareFD: graphFD = getTGraphOfParameter_FD(filenamesFD,parameter,xvals=tvals,yvals=pvals) if graphFD: graphsFD.append(graphFD) if len(parameters)!=len(graphs): warning("plotPostFitValues: len(parameters) = %d != %d = len(graphs)"%(len(parameters),len(graphs))) exit(1) N = len(parameters) compareFD = compareFD and len(graphsFD)>0 parameters = [formatParameter(p).replace('_'+DM,'') for p in parameters] graphsleg = columnize(graphs,3) if N>6 else columnize(graphs,2) if N>3 else graphs # reordered for two columns paramsleg = columnize(parameters,3) if N>6 else columnize(parameters,2) if N>3 else parameters # reordered for two columns xtitle = 'tau energy scale' ytitle = "%s post-fit value"%(parameters[0] if N==1 else "MultiDimFit") xmin, xmax = min(tvals), max(tvals) ymin, ymax = min(pvals), max(pvals) colors = [ kBlack, kBlue, kRed, kGreen, kMagenta, kOrange, kTeal, kAzure+2, kYellow-3 ] canvas = TCanvas("canvas","canvas",100,100,800,650) canvas.SetFillColor(0) canvas.SetBorderMode(0) canvas.SetFrameFillStyle(0) canvas.SetFrameBorderMode(0) canvas.SetTopMargin( 0.07 ); canvas.SetBottomMargin( 0.13 ) canvas.SetLeftMargin( 0.11 ); canvas.SetRightMargin( 0.05 ) canvas.SetTickx(0) canvas.SetTicky(0) canvas.SetGrid() canvas.cd() maxmargin = 0.18*abs(ymax-ymin) minmargin = 0.20*abs(ymax-ymin) #(0.20 if N<5 else 0.10*ceil(N/2)) ymin = 0.0 if ymin>=0 else ymin-minmargin ymax = ymax + minmargin textsize = 0.045 if N==1 else 0.036 lineheight = 0.055 if N==1 else 0.045 x1, width = 0.42, 0.25 y1, height = 0.15, lineheight*(ceil(N/3.) if N>6 else ceil(N/2.) if N>3 else N) if title: height += lineheight if compareFD: height += lineheight if N>6: if isBBB: width = 0.62; x1 = 0.25 else: width = 0.70; x1 = 0.20 elif N>3: width = 0.52; x1 = 0.36 legend = TLegend(x1,y1,x1+width,y1+height) legend.SetTextSize(textsize) legend.SetBorderSize(0) legend.SetFillStyle(0) legend.SetFillColor(0) if title: legend.SetTextFont(62) legend.SetHeader(title) legend.SetTextFont(42) if N>6: legend.SetNColumns(3) legend.SetColumnSeparation(0.02) elif N>3: legend.SetNColumns(2) legend.SetColumnSeparation(0.03) frame = canvas.DrawFrame(xmin,ymin,xmax,ymax) frame.GetYaxis().SetTitleSize(0.060) frame.GetXaxis().SetTitleSize(0.060) frame.GetXaxis().SetLabelSize(0.050) frame.GetYaxis().SetLabelSize(0.050) frame.GetXaxis().SetLabelOffset(0.01) frame.GetYaxis().SetLabelOffset(0.01) frame.GetXaxis().SetTitleOffset(0.98) frame.GetYaxis().SetTitleOffset(0.84) frame.GetXaxis().SetNdivisions(508) frame.GetYaxis().SetTitle(ytitle) frame.GetXaxis().SetTitle(xtitle) for i, (parameter,graph) in enumerate(zip(parameters,graphs)): graph.SetLineColor(colors[i%len(colors)]) graph.SetLineWidth(2) graph.SetLineStyle(1) graph.Draw('LSAME') for parameter,graph in zip(paramsleg,graphsleg): if N==1: legend.AddEntry(graph, "MultiDimFit", 'l') else: legend.AddEntry(graph, parameter, 'l') line = TLine(tes, ymin, tes, ymax) #line.SetLineWidth(1) line.SetLineStyle(7) line.Draw('SAME') latex = TLatex() latex.SetTextSize(0.045) latex.SetTextAlign(13) latex.SetTextFont(42) latex.DrawLatex(tes+0.02*(xmax-xmin),ymax-0.04*(ymax-ymin),"tes = %.3f"%tes) if compareFD: graphFD.SetLineColor(kBlue) graphFD.SetLineWidth(2) graphFD.SetLineStyle(2) graphFD.Draw('LSAME') legend.AddEntry(graphFD, "FitDiagnostics", 'l') legend.Draw() CMS_lumi.relPosX = 0.12 CMS_lumi.CMS_lumi(canvas,13,0) gPad.SetTicks(1,1) gPad.Modified() frame.Draw('SAMEAXIS') canvas.SaveAs(canvasname+".png") if args.pdf: canvas.SaveAs(canvasname+".pdf") canvas.Close()