def Create_Legend(x1, y1, x2, y2, font=42, text_size=0.045, fill=0, border_size=1, entry_sep=0, margin=0.3, show_stats=False, ncols=1): leg = TLegend(x1, y1, x2, y2) leg.SetTextFont(font) leg.SetTextSize(text_size) leg.SetFillColor(fill) leg.SetBorderSize(border_size) leg.SetEntrySeparation(entry_sep) leg.SetMargin(margin) leg.SetNColumns(ncols) if show_stats: leg.SetNColumns(2) ## Creating headers for the legend leg.AddEntry(0, "", "") # leg.AddEntry( 0, "#bf{Mean}", "") leg.AddEntry(0, "#bf{RMSE}", "") return leg
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"]: legend.AddEntry(pdfs[func, "obj"], fitFuncs[func, "name"], "l")
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 drawROIGeometry(geometry, is2016): outfn = "ROILayout%s.pdf" % ("2016" if is2016 else "2015") global box, c, h, leg gROOT.Reset() gStyle.SetOptStat(0) c = TCanvas('c', "MuCTPi Geometry %s" % "2016" if is2016 else "2015", 1400, 950) c.Draw() #h = TH2F("h","Muon Geometry",10,-2.6,2.6,10,-6.4,6.4) h = TH2F("h", "Muon Geometry %s" % "2016" if is2016 else "2015", 10, -2.6, 2.6, 10, -0.15, 6.4) h.SetXTitle("#eta") h.SetYTitle("#phi") h.Draw() box = TBox() box.SetFillStyle(0) box.SetLineColor(3) box.SetLineColor(3) text = TLatex() text.SetTextSize(0.005) text.SetTextFont(42) text.SetTextAlign(22) secLabel = TLatex() secLabel.SetTextSize(0.008) secLabel.SetTextFont(42) secLabel.SetTextAlign(22) leg = TLegend(0.7, 0.1, 0.9, 0.4) leg.SetEntrySeparation(0.05) leg.SetNColumns(2) #for MIOCT in geometry.getMIOCTs(): for colorIndex, MioctID in enumerate(drawOrder): MIOCT = geometry.getMIOCT(MioctID) firstBox = True color = colorMap[colorIndex % len(colorMap)] #print "Using color ",color box.SetLineColor(color) box.SetLineWidth(1) for Sector in MIOCT.Sectors: ymin = 10 ymax = -10 for ROI in Sector.ROIs: c1_x = float(ROI["etamin"]) c1_y = float(ROI["phimin"]) c2_x = float(ROI["etamax"]) c2_y = float(ROI["phimax"]) ymin = min(ymin, c1_y) ymax = max(ymax, c2_y) #print "eta [%f - %f], phi [%f - %f]" % (c1_x,c2_x,c1_y,c2_y) b = box.DrawBox(c1_x, c1_y, c2_x, c2_y) text.DrawText((c1_x + c2_x) / 2, (c1_y + c2_y) / 2, ROI["roiid"]) if firstBox: firstBox = False leg.AddEntry(b, "Slot %s" % MIOCT["slot"], "l") if Sector["name"].startswith("B"): if int(Sector["name"][1:]) < 32: xpos = -0.02 ypos = (ymin + ymax) / 2 - 0.05 else: xpos = 0.02 ypos = (ymin + ymax) / 2 + 0.03 secLabel.DrawText(xpos, ypos, Sector["name"]) leg.Draw() c.Update() c.SaveAs(outfn)