def plot_distributions(input_file, distribution_plots): with root_open(input_file) as file: for name,histo_configs in distribution_plots.items(): canvas = Canvas(700,700) legend = Legend(len(histo_configs), leftmargin=0.4, margin=0.3) histograms = [] for histo_config in histo_configs: histogram = file.Get(histo_config.name) if len(histo_config.range)==2: histogram.SetAxisRange(histo_config.range[0], histo_config.range[1], 'X') histogram.color = histo_config.color histogram.markerstyle = histo_config.marker legend.AddEntry(histogram, label=histo_config.legend, style='ep') histograms.append(histogram) xmin, xmax, ymin, ymax = get_limits(histograms, logy=False) option = '' for histogram in histograms: histogram.SetAxisRange(ymin,ymax,'Y') histogram.Draw(option) option += ' same' legend.Draw() canvas.Print('plots/{}.png'.format(name))
h1.linewidth = 0 h2.fillstyle = 'solid' h2.fillcolor = 'red' h2.linecolor = 'red' h2.linewidth = 0 stack = HistStack() stack.Add(h1) stack.Add(h2) # plot with ROOT canvas = Canvas(width=700, height=500) # try setting logy=True and uncommenting the two lines below xmin, xmax, ymin, ymax = get_limits([stack, h3], logy=False) stack.SetMaximum(ymax) #stack.SetMinimum(ymin) #canvas.SetLogy() stack.Draw('HIST E1 X0') h3.Draw('SAME E1 X0') stack.xaxis.SetTitle('Mass') stack.yaxis.SetTitle('Events') # set the number of expected legend entries legend = Legend(3, leftmargin=0.45, margin=0.3) legend.AddEntry(h1, style='F') legend.AddEntry(h2, style='F') legend.AddEntry(h3, style='LEP') legend.Draw() label = ROOT.TText(0.3, 0.8, 'ROOT')
def draw_to_canvas(self): """ Draw this figure to a canvas, which is then returned. """ if len(self._plottables) == 0: raise IndexError("No plottables defined") c = Canvas(width=self.style.canvasWidth, height=self.style.canvasHeight, size_includes_decorations=True) if self.legend.position == 'seperate': legend_width = .2 pad_legend = Pad(1 - legend_width, 0, 1., 1., name="legend") pad_legend.SetLeftMargin(0.0) pad_legend.SetFillStyle(0) # make this pad transparent pad_legend.Draw() else: legend_width = 0 pad_plot = Pad( 0., 0., 1 - legend_width, 1., name="plot", ) pad_plot.SetMargin(*self.style.plot_margins) pad_plot.Draw() pad_plot.cd() # awkward hack around a bug in get limits where everything fails if one plottable is shitty... xmin, xmax, ymin, ymax = None, None, None, None for pdic in self._plottables: try: limits = get_limits(pdic['p'], logx=self.plot.logx, logy=self.plot.logy) # Beware: Python 2 evaluates min/max of None in an undefined way with no error! Wow... xmin = min([xmin, limits[0] ]) if xmin is not None else limits[0] xmax = max([xmax, limits[1] ]) if xmax is not None else limits[1] ymin = min([ymin, limits[2] ]) if ymin is not None else limits[2] ymax = max([ymax, limits[3] ]) if ymax is not None else limits[3] except (TypeError, ValueError): # some plottables do not work with this rootpy function (eg. graph without points, tf1) # TODO: should be fixed upstream pass # overwrite these ranges if defaults are given if self.plot.xmin is not None: xmin = self.plot.xmin if self.plot.xmax is not None: xmax = self.plot.xmax if self.plot.ymax is not None: ymax = self.plot.ymax if self.plot.ymin is not None: ymin = self.plot.ymin if not all([val is not None for val in [xmin, xmax, ymin, ymax]]): raise TypeError( "unable to determine plot axes ranges from the given plottables" ) colors = get_color_generator(self.plot.palette, self.plot.palette_ncolors) # draw an empty frame within the given ranges; frame_from_plottable = [ p for p in self._plottables if p.get('use_as_frame') ] if len(frame_from_plottable) > 0: frame = frame_from_plottable[0]['p'].Clone('__frame') frame.Reset() frame.SetStats(0) frame.xaxis.SetRangeUser(xmin, xmax) frame.yaxis.SetRangeUser(ymin, ymax) frame.GetXaxis().SetTitle(self.xtitle) frame.GetYaxis().SetTitle(self.ytitle) self._theme_plottable(frame) frame.Draw() else: frame = Graph() frame.SetName("__frame") # add a silly point in order to have root draw this frame... frame.SetPoint(0, 0, 0) frame.GetXaxis().SetLimits(xmin, xmax) frame.GetYaxis().SetLimits(ymin, ymax) frame.SetMinimum(ymin) frame.SetMaximum(ymax) frame.GetXaxis().SetTitle(self.xtitle) frame.GetYaxis().SetTitle(self.ytitle) self._theme_plottable(frame) # Draw this frame: 'A' should draw the axis, but does not work if nothing else is drawn. # L would draw a line between the points but is seems to do nothing if only one point is present # P would also draw that silly point but we don't want that! frame.Draw("AL") xtick_length = frame.GetXaxis().GetTickLength() ytick_length = frame.GetYaxis().GetTickLength() for i, pdic in enumerate(self._plottables): obj = pdic['p'] if isinstance(obj, ROOT.TLegendEntry): _root_color = Color(pdic['color']) _root_markerstyle = MarkerStyle(pdic['markerstyle']) obj.SetMarkerStyle(_root_markerstyle('root')) obj.SetMarkerColor(_root_color('root')) elif isinstance(obj, (ROOT.TH1, ROOT.TGraph, ROOT.TF1)): self._theme_plottable(obj) obj.SetMarkerStyle(pdic.get('markerstyle', 'circle')) if pdic.get('color', None): obj.color = pdic['color'] else: try: color = next(colors) except StopIteration: log.warning("Ran out of colors; defaulting to black") color = 1 obj.color = color xaxis = obj.GetXaxis() yaxis = obj.GetYaxis() # Set the title to the given title: obj.title = self.title # the xaxis depends on the type of the plottable :P if isinstance(obj, ROOT.TGraph): # SetLimit on a TH1 is simply messing up the # lables of the axis to screw over the user, presumably... xaxis.SetLimits(xmin, xmax) yaxis.SetLimits(ymin, ymax) # for unbinned data # 'P' plots the current marker, 'L' would connect the dots with a simple line # see: https://root.cern.ch/doc/master/classTGraphPainter.html for more draw options drawoption = 'Psame' elif isinstance(obj, ROOT.TH1): obj.SetStats(0) xaxis.SetRangeUser(xmin, xmax) yaxis.SetRangeUser(ymin, ymax) drawoption = 'same' elif isinstance(obj, ROOT.TF1): # xaxis.SetLimits(xmin, xmax) # yaxis.SetLimits(ymin, ymax) # for unbinned data drawoption = 'same' obj.Draw(drawoption) # Its ok if obj is non; then we just add it to the legend. else: raise TypeError("Un-plottable type given.") pad_plot.SetTicks() pad_plot.SetLogx(self.plot.logx) pad_plot.SetLogy(self.plot.logy) pad_plot.SetGridx(self.plot.gridx) pad_plot.SetGridy(self.plot.gridy) # do we have legend titles? if any([pdic.get('legend_title') for pdic in self._plottables]): leg = self._create_legend() longest_label = 0 for pdic in self._plottables: if not pdic.get('legend_title', False): continue leg.AddEntry(pdic['p'], pdic['legend_title']) if len(pdic['legend_title']) > longest_label: longest_label = len(pdic['legend_title']) # Set the legend position # vertical: if self.legend.position.startswith('t'): leg_hight = leg.y2 - leg.y1 leg.y2 = 1 - pad_plot.GetTopMargin() - ytick_length leg.y1 = leg.y2 - leg_hight elif self.legend.position.startswith('b'): leg_hight = leg.y2 - leg.y1 leg.y1 = pad_plot.GetBottomMargin() + ytick_length leg.y2 = leg.y1 + leg_hight # horizontal: if self.legend.position[1:].startswith('l'): leg_width = 0.3 leg.x1 = pad_plot.GetLeftMargin() + xtick_length leg.x2 = leg.x1 + leg_width elif self.legend.position[1:].startswith('r'): leg_width = 0.3 leg.x2 = 1 - pad_plot.GetRightMargin() - xtick_length leg.x1 = leg.x2 - leg_width if self.legend.position == 'seperate': with pad_legend: leg.Draw() else: leg.Draw() if self.plot.logx: pad_plot.SetLogx(True) if self.plot.logy: pad_plot.SetLogy(True) pad_plot.Update( ) # needed sometimes with import of canvas. maybe because other "plot" pads exist... return c
def stack(x, *args, **kwargs): ## parse arguments _data = kwargs.pop('data', None) _bkgs = kwargs.pop('bkgs', None) _sigs = kwargs.pop('sigs', None) _treename = kwargs.pop('treename', None) _datasearchpath = kwargs.pop('datasearchpath', None) _datadrivensearchpath = kwargs.pop('datadrivensearchpath', None) _bkgsearchpath = kwargs.pop('bkgsearchpath', None) _sigsearchpath = kwargs.pop('sigsearchpath', None) _lumi = kwargs.pop('lumi', None) global data global bkgs global sigs global treename global datasearchpath global datadrivensearchpath global bkgsearchpath global sigsearchpath global lumi data = _data or data bkgs = _bkgs or bkgs sigs = _sigs or sigs treename = _treename or treename datasearchpath = _datasearchpath or datasearchpath datadrivensearchpath = _datadrivensearchpath or datadrivensearchpath bkgsearchpath = _bkgsearchpath or bkgsearchpath sigsearchpath = _sigsearchpath or sigsearchpath if _lumi: lumi = float(_lumi) xtitle = kwargs.pop('xtitle', '') ytitle = kwargs.pop('ytitle', '') logx = bool(kwargs.pop('logx', False)) logy = bool(kwargs.pop('logy', False)) blind = kwargs.pop('blind', None) has_blinded_data = False ## save stuff to bookkeep and return stuff = dict() stuff['x'] = x ## get data histogram h_data = None if data: sp = datasearchpath # HACK: just data to True! newx = '%s::%s::%s' % (sp, treename, x) h_data = ipyhep.tree.project(newx, *args, **kwargs) if h_data: stuff['h_data'] = h_data ## blind the data? if h_data and not blind is None: if isinstance(blind, tuple): blind1, blind2 = blind nbins = h_data.GetNbinsX() for i_bin in xrange(1, nbins + 2): # skip underflow (but not overflow) xval1 = h_data.GetXaxis().GetBinLowEdge(i_bin) xval2 = h_data.GetXaxis().GetBinUpEdge(i_bin) if xval1 >= blind1 and xval2 <= blind2: h_data.SetBinContent(i_bin, 0.0) h_data.SetBinError(i_bin, 0.0) has_blinded_data = True else: nbins = h_data.GetNbinsX() for i_bin in xrange(1, nbins + 2): # skip underflow (but not overflow) xval = h_data.GetXaxis().GetBinLowEdge(i_bin) if xval >= blind: h_data.SetBinContent(i_bin, 0.0) h_data.SetBinError(i_bin, 0.0) has_blinded_data = True ## get background histograms h_bkgs = list() n_bkgs = list() if bkgs: for bkg in bkgs: if isinstance(bkg, list): h_subtotal = None for dsid in bkg: assert isinstance(dsid, str) h_bkg = None if dsid.isdigit(): ## mc backgrounds sp = bkgsearchpath % int(dsid) newx = '%s::%s::%s' % (sp, treename, x) h_bkg = ipyhep.tree.project(newx, *args, **kwargs) else: ## data-driven backgrounds assert dsid == 'fakes' or dsid == 'efakes' sp = datadrivensearchpath % dsid newx = '%s::%s::%s' % (sp, treename, x) h_bkg = ipyhep.tree.project(newx, *args, **kwargs) if h_bkg: if h_subtotal: h_subtotal.Add(h_bkg) else: h_subtotal = h_bkg.Clone() if h_subtotal: h_bkgs.append(h_subtotal) dsid = bkg[0] n_bkgs.append(dsid) else: dsid = bkg assert isinstance(dsid, str) h_bkg = None if dsid.isdigit(): ## mc backgrounds sp = bkgsearchpath % int(dsid) newx = '%s::%s::%s' % (sp, treename, x) h_bkg = ipyhep.tree.project(newx, *args, **kwargs) else: ## data-driven backgrounds assert dsid == 'fakes' or dsid == 'efakes' sp = datadrivensearchpath % dsid newx = '%s::%s::%s' % (sp, treename, x) h_bkg = ipyhep.tree.project(newx, *args, **kwargs) if h_bkg: h_bkgs.append(h_bkg) n_bkgs.append(dsid) if h_bkgs: stuff['h_bkgs'] = h_bkgs ## get signal histograms h_sigs = list() n_sigs = list() if sigs: for dsid in sigs: sp = sigsearchpath % int(dsid) newx = '%s::%s::%s' % (sp, treename, x) h_sig = ipyhep.tree.project(newx, *args, **kwargs) if h_sig: h_sigs.append(h_sig) n_sigs.append(dsid) if h_sigs: stuff['h_sigs'] = h_sigs assert h_sigs ## style data if h_data: h_data.title = 'Data' h_data.linecolor = ipyhep.style.black h_data.linewidth = 2 h_data.markercolor = ipyhep.style.black h_data.markerstyle = 20 h_data.markersize = 1.2 h_data.fillstyle = ipyhep.style.fill_hollow h_data.drawstyle = 'PE' h_data.legendstyle = 'LP' ## scale and style background histograms if h_bkgs: assert len(h_bkgs) == len(n_bkgs), '%s\n%s' % (h_bkgs, n_bkgs) for h, dsid in zip(h_bkgs, n_bkgs): sf = ipyhep.sampleops.get_sf(dsid) if dsid.isdigit(): sf *= lumi / __ntuple_lumi h.Scale(sf) h.title = ipyhep.sampleops.get_label(dsid) h.linecolor = ipyhep.style.black h.linewidth = 1 h.markercolor = ipyhep.sampleops.get_color(dsid) h.fillcolor = ipyhep.sampleops.get_color(dsid) h.fillstyle = ipyhep.style.fill_solid h.legendstyle = 'F' ## calculate stat error on total background h_bkg_total = None if h_bkgs: for h_bkg in h_bkgs: if h_bkg_total: h_bkg_total.Add(h_bkg) else: h_bkg_total = h_bkg.Clone() stuff['h_bkg_total'] = h_bkg_total ## style h_bkg_total if h_bkg_total: h_bkg_total.title = 'stat. uncert.' h_bkg_total.linecolor = ipyhep.style.black h_bkg_total.linewidth = 1 h_bkg_total.markerstyle = 0 h_bkg_total.fillcolor = ipyhep.style.dark_gray h_bkg_total.fillstyle = ipyhep.style.fill_lines h_bkg_total.drawstyle = 'E2' h_bkg_total.legendstyle = 'LF' ## scale and style signal histograms if h_sigs: assert len(h_sigs) == len(n_sigs) for h, dsid in zip(h_sigs, n_sigs): sf = ipyhep.sampleops.get_sf(dsid) sf *= lumi / __ntuple_lumi h.Scale(sf) h.title = ipyhep.sampleops.get_label(dsid) h.linecolor = ipyhep.sampleops.get_color(dsid) h.linewidth = 3 h.fillstyle = ipyhep.style.fill_hollow h.markerstyle = 0 h.drawstyle = 'HIST' h.legendstyle = 'L' ## build list of all_hists all_hists = list() main_hists = list() if h_data: all_hists.append(h_data) main_hists.append(h_data) if h_bkgs: all_hists.extend(h_bkgs) main_hists.extend(h_bkgs) if h_bkg_total: all_hists.append(h_bkg_total) main_hists.append(h_bkg_total) if h_sigs: all_hists.extend(h_sigs) ## get statistics if all_hists: stats_list = list() for h in all_hists: stats_list.extend(get_stats(h)) html = convert_table_to_html(convert_stats_to_table(stats_list)) stuff['html'] = html ## renormalize for bin widths bins = kwargs.pop('bins', None) if bins and isinstance(bins, list): for h in all_hists: renormalize_for_bin_widths(h, bins) ## stack background histograms if h_bkgs: assert len(h_bkgs) == len(n_bkgs), '%s\n%s' % (h_bkgs, n_bkgs) h_bkgs.reverse() n_bkgs.reverse() hstack = HistStack() for h in h_bkgs: hstack.Add(h) hstack.title = 'stack sum' hstack.drawstyle = 'HIST' stuff['stack'] = hstack h_bkgs.reverse() n_bkgs.reverse() # ## convert data to TGraphAsymmErrors # g_data = None # if h_data: # if __use_poissonize: # g_data = poissonize.GetPoissonizedGraph(h_data) # else: # g_data = ROOT.TGraphAsymmErrors() # i_g = 0 # nbins = h_data.GetNbinsX() # for i_bin in xrange(1, nbins+1): # skip underflow/overflow # c = h_data.GetBinContent(i_bin) # e = h_data.GetBinError(i_bin) # if c != 0.0: # g_data.SetPoint(i_g, h_data.GetBinCenter(i_bin), c) # g_ratio.SetPointError(i_g, # h_data.GetBinWidth(i_bin)/2., # h_data.GetBinWidth(i_bin)/2., # e, # e) # i_g += 1 ## build list of objects to draw objects = list() if h_bkgs: objects.append(stuff['stack']) objects.append(stuff['h_bkg_total']) if h_sigs: objects.extend(h_sigs) if h_data: objects.append(h_data) ## set xlimits and ylimits ypadding = 0.21 logy_crop_value = 7e-3 xmin, xmax, ymin, ymax = 0.0, 1.0, 0.0, 1.0 if objects: xmin, xmax, ymin, ymax = get_limits(objects, logx=logx, logy=logy, ypadding=ypadding, logy_crop_value=logy_crop_value) if logy: ymin = 7e-3 else: ymin = 0.0 xlimits = (xmin, xmax) ylimits = (ymin, ymax) stuff['xlimits'] = xlimits stuff['ylimits'] = ylimits ## remove xtitle for do_ratio _xtitle = xtitle if h_data and h_bkg_total and kwargs.get('do_ratio'): _xtitle = '' ## make canvas canvas = Canvas(800, 600) stuff['canvas'] = canvas ## draw the objects if objects: canvas.cd() draw(objects, pad=canvas, xtitle=_xtitle, ytitle=ytitle, xlimits=xlimits, ylimits=ylimits) ## set log x/y, for some reason doesn't work before draw if logx or logy: if logx: canvas.SetLogx() if logy: canvas.SetLogy() canvas.Update() ## draw blind_line if has_blinded_data: if isinstance(blind, tuple): blind_list = list(blind) else: blind_list = [blind] blind_lines = list() for bl in blind_list: line_y1 = ymin line_y2 = ymax blind_line = ROOT.TLine(bl, line_y1, bl, line_y2) blind_line.SetLineColor(ROOT.kGray + 2) blind_line.SetLineStyle(7) blind_line.SetLineWidth(2) blind_line.Draw() blind_lines.append(blind_line) stuff['blind_lines'] = blind_lines canvas.Update() ## legend lefty = True if h_bkg_total: lefty = is_left_sided(h_bkg_total) elif h_data: lefty = is_left_sided(h_data) elif h_sigs: lefty = is_left_sided(h_sigs[0]) if main_hists: header = '%.1f fb^{-1}, 13 TeV' % (lumi / 1000.0) if lefty: legend = Legend(main_hists, pad=canvas, header=header, textsize=16, topmargin=0.03, leftmargin=0.60, rightmargin=0.02, entrysep=0.01, entryheight=0.04) else: legend = Legend(main_hists, pad=canvas, header=header, textsize=16, topmargin=0.03, leftmargin=0.03, rightmargin=0.59, entrysep=0.01, entryheight=0.04) legend.Draw() stuff['legend'] = legend if h_sigs: # header = 'ATLAS Internal' header = '' if lefty: legend2 = Legend(h_sigs, pad=canvas, header=header, textsize=16, topmargin=0.03, leftmargin=0.37, rightmargin=0.23, entrysep=0.01, entryheight=0.04) else: legend2 = Legend(h_sigs, pad=canvas, header=header, textsize=16, topmargin=0.03, leftmargin=0.20, rightmargin=0.40, entrysep=0.01, entryheight=0.04) legend2.Draw() stuff['legend2'] = legend2 ## do_ratio if h_data and h_bkg_total and kwargs.get('do_ratio'): ## top canvas top_canvas = stuff.pop('canvas') stuff['top_canvas'] = top_canvas ## make SM/SM with error band: h_ratio_band i_sfratio = int(kwargs.get('sfratio', -1)) if i_sfratio < 0: # ratio plot of Data/Model h_ratio_band = h_bkg_total.Clone() nbins = h_ratio_band.GetNbinsX() for i_bin in xrange(nbins + 2): h_ratio_band.SetBinContent(i_bin, 1.0) c = h_bkg_total.GetBinContent(i_bin) e = h_bkg_total.GetBinError(i_bin) / c if c > 0.0 else 0.0 h_ratio_band.SetBinError(i_bin, e) stuff['h_ratio_band'] = h_ratio_band else: # ratio plot of Scale Factor for ith background hi = h_bkgs[i_sfratio] h_ratio_band = hi.Clone() nbins = h_ratio_band.GetNbinsX() for i_bin in xrange(nbins + 2): h_ratio_band.SetBinContent(i_bin, 1.0) c = hi.GetBinContent(i_bin) e = hi.GetBinError(i_bin) / c if c > 0.0 else 0.0 h_ratio_band.SetBinError(i_bin, e) stuff['h_ratio_band'] = h_ratio_band ## make data/(SM) h_ratio if i_sfratio < 0: h_ratio = h_data.Clone() h_ratio.Divide(h_data, h_bkg_total, 1.0, 1.0) stuff['h_ratio'] = h_ratio else: ## SF1 = 1.0 + (data - MCtot) / MC1 sfname = kwargs.get('sfname') sffile = kwargs.get('sffile') if not sfname: sfname = 'h_sf' hi = h_bkgs[i_sfratio] h_numer = h_data.Clone() h_numer.Add(h_bkg_total, -1.0) ## do the division h_ratio = h_data.Clone(sfname) h_ratio.Divide(h_numer, hi, 1.0, 1.0) ## add the 1.0 nbins = h_ratio.GetNbinsX() for i_bin in xrange(nbins + 2): c = h_ratio.GetBinContent(i_bin) h_ratio.SetBinContent(i_bin, c + 1.0) h_ratio_band.SetBinContent(i_bin, c + 1.0) ## ignore bins with no data for SF for i_bin in xrange(nbins + 2): c = h_data.GetBinContent(i_bin) if c <= 0: h_ratio.SetBinContent(i_bin, 0.0) h_ratio.SetBinError(i_bin, 0.0) h_ratio_band.SetBinError(i_bin, 0.0) stuff['h_ratio'] = h_ratio if sffile: f_out = ipyhep.file.write(h_ratio, sffile) # f_out.Close() ## convert ratio to a TGraphErrors so that Draw('E0') ## shows error bars for points off the pad g_ratio = ROOT.TGraphErrors() i_g = 0 for i_bin in xrange(1, nbins + 1): # skip underflow/overflow ratio_content = h_ratio.GetBinContent(i_bin) if ratio_content != 0.0: g_ratio.SetPoint(i_g, h_ratio.GetBinCenter(i_bin), ratio_content) g_ratio.SetPointError(i_g, h_ratio.GetBinWidth(i_bin) / 2., h_ratio.GetBinError(i_bin)) i_g += 1 else: h_ratio.SetBinError(i_bin, 0.0) stuff['g_ratio'] = g_ratio ## style ratio h_ratio_band.title = 'bkg uncert.' if i_sfratio < 0: h_ratio_band.linecolor = ipyhep.style.yellow else: h_ratio_band.linecolor = ipyhep.style.light_gray h_ratio_band.linewidth = 0 h_ratio_band.markerstyle = 0 if i_sfratio < 0: h_ratio_band.fillcolor = ipyhep.style.yellow else: h_ratio_band.linecolor = ipyhep.style.light_gray h_ratio_band.fillstyle = ipyhep.style.fill_solid h_ratio_band.drawstyle = 'E2' h_ratio_band.legendstyle = 'F' h_ratio.title = 'ratio' h_ratio.linecolor = ipyhep.style.black h_ratio.linewidth = 2 h_ratio.markercolor = ipyhep.style.black h_ratio.markerstyle = 20 h_ratio.markersize = 1.2 h_ratio.fillstyle = ipyhep.style.fill_hollow h_ratio.drawstyle = 'PE' h_ratio.legendstyle = 'LP' ## bottom canvas bottom_canvas = Canvas(800, 600) bottom_canvas.cd() stuff['bottom_canvas'] = bottom_canvas ## set ratio ylimits ratio_min = kwargs.get('ratio_min', -0.2) ratio_max = kwargs.get('ratio_max', 2.2) ratio_ylimits = (ratio_min, ratio_max) ## draw ratio band if i_sfratio < 0: _ytitle = 'Data / Model' else: hi = h_bkgs[i_sfratio] _ytitle = 'SF(%s)' % hi.title draw([h_ratio_band], pad=bottom_canvas, xtitle=xtitle, ytitle=_ytitle, xlimits=xlimits, ylimits=ratio_ylimits) ## set log x/y, for some reason doesn't work before draw? if logx: bottom_canvas.SetLogx() bottom_canvas.Update() ### make horiz lines in ratio plot every 0.5: line_ys = [ y / 10.0 for y in range(10 * int(round(ratio_min)), 10 * int(round(ratio_max)) + 5, 5) ] line_x1 = canvas.GetUxmin() line_x2 = canvas.GetUxmax() line_xwidth = abs(line_x2 - line_x1) lines = [] for line_y in line_ys: line = ROOT.TLine(line_x1 + 0.02 * line_xwidth, line_y, line_x2 - 0.02 * line_xwidth, line_y) line.SetLineWidth(1) line.SetLineStyle(7) if line_y == 1.0: line.SetLineColor(ROOT.kGray + 2) else: line.SetLineColor(ROOT.kGray + 0) line.Draw() lines.append(line) stuff['lines'] = lines ## draw blind_line if has_blinded_data: if isinstance(blind, tuple): blind_list = list(blind) else: blind_list = [blind] blind_lines = list() for bl in blind_list: line_y1 = ymin line_y2 = ymax blind_line = ROOT.TLine(bl, line_y1, bl, line_y2) blind_line.SetLineColor(ROOT.kGray + 2) blind_line.SetLineStyle(7) blind_line.SetLineWidth(2) blind_line.Draw() blind_lines.append(blind_line) stuff['blind_lines2'] = blind_lines canvas.Update() ## draw ratio g_ratio.Draw('PE0') # h_ratio.GetYaxis().SetRangeUser(ratio_min, ratio_max) # h_ratio.Draw('PE,SAME') ## shared canvas shared_canvas = Canvas(800, 800) shared_plot = plot_shared_axis(top_canvas, bottom_canvas, canvas=shared_canvas, split=0.35, axissep=0.01) stuff['canvas'] = shared_canvas canvas = shared_canvas ## save figures save = kwargs.get('save') if save is None: # NOTE: save can be False to skip saving save = ['pdf', 'png'] if save: ipyhep.file.save_figures(canvas, x, save) global results results = stuff return stuff
def calc_fake_factors(name, numers_data, numers_bkg, denoms_data, denoms_bkg, labels, xtitle, ytitle, save=None, ylimits=None): assert numers_data, numers_bkg if not isinstance(numers_data, list): numers_data = [numers_data] if not isinstance(numers_bkg, list): numers_bkg = [numers_bkg] if not isinstance(denoms_data, list): denoms_data = [denoms_data] if not isinstance(denoms_bkg, list): denoms_bkg = [denoms_bkg] if save is None: save = ['pdf', 'png'] stuff = dict() objects = list() colors = [ ipyhep.style.red, ipyhep.style.blue, ipyhep.style.orange, ipyhep.style.violet, ] i_ff = 0 for numer_data, numer_bkg, denom_data, denom_bkg in zip( numers_data, numers_bkg, denoms_data, denoms_bkg): for h in (numer_data, numer_bkg, denom_data, denom_bkg): if h and not h.GetSumw2N(): h.Sumw2() h_numer = numer_data.Clone() h_numer.SetDirectory(0) if numer_bkg: set_negative_bins_zero(numer_bkg) h_numer.Add(numer_bkg, -1.0) h_denom = denom_data.Clone() h_denom.SetDirectory(0) if denom_bkg: set_negative_bins_zero(denom_bkg) h_denom.Add(denom_bkg, -1.0) h_ff = h_numer.Clone() h_ff.SetDirectory(0) divide_option = '' h_ff.Divide(h_numer, h_denom, 1.0, 1.0, divide_option) h_ff.linecolor = colors[i_ff] h_ff.markercolor = colors[i_ff] h_ff.markerstyle = 20 h_ff.markersize = 1.2 h_ff.fillstyle = ipyhep.style.fill_hollow h_ff.drawstyle = 'PE' h_ff.legendstyle = 'LP' if labels: h_ff.title = '%s' % labels[i_ff] else: h_ff.title = '%i' % i_ff h_ff.name = 'h_ff_%s' % h_ff.title stuff['h_ff_%s' % h_ff.title] = h_ff objects.append(h_ff) i_ff += 1 ## make canvas canvas = Canvas(800, 600) stuff['canvas'] = canvas xmin, xmax, ymin, ymax = get_limits(objects) xlimits = (xmin, xmax) # ylimits = (ymin, ymax) ylimits = ylimits or (0.0, 1.0) draw(objects, pad=canvas, xtitle=xtitle, ytitle=ytitle, xlimits=xlimits, ylimits=ylimits) ## legend header = '' legend = Legend(objects, pad=canvas, header=header, textsize=16, topmargin=0.02, leftmargin=0.60, rightmargin=0.02, entrysep=0.01, entryheight=0.04) legend.Draw() stuff['legend'] = legend ## save figures if save: ipyhep.file.save_figures(canvas, 'h_ff', save) ## save fake factors timestamp = time.strftime('%Y_%m_%d_%Hh%M') outfile = 'fakefactors-%s.root' % timestamp f_out = None for h_ff in objects: f_out = ipyhep.file.write(h_ff, outfile) # f_out.Close() # ipyhep.file.close_all_files() return stuff
def draw_to_canvas(self): """ Draw this figure to a canvas, which is then returned. """ if len(self._plottables) == 0: raise IndexError("No plottables defined") c = Canvas(width=self.style.canvasWidth, height=self.style.canvasHeight, size_includes_decorations=True) if self.legend.position == 'seperate': legend_width = .2 pad_legend = Pad(1 - legend_width, 0, 1., 1., name="legend") pad_legend.SetLeftMargin(0.0) pad_legend.SetFillStyle(0) # make this pad transparent pad_legend.Draw() else: legend_width = 0 pad_plot = Pad(0., 0., 1 - legend_width, 1., name="plot", ) pad_plot.SetMargin(*self.style.plot_margins) pad_plot.Draw() pad_plot.cd() # awkward hack around a bug in get limits where everything fails if one plottable is shitty... xmin, xmax, ymin, ymax = None, None, None, None for pdic in self._plottables: try: limits = get_limits(pdic['p'], logx=self.plot.logx, logy=self.plot.logy) # Beware: Python 2 evaluates min/max of None in an undefined way with no error! Wow... xmin = min([xmin, limits[0]]) if xmin is not None else limits[0] xmax = max([xmax, limits[1]]) if xmax is not None else limits[1] ymin = min([ymin, limits[2]]) if ymin is not None else limits[2] ymax = max([ymax, limits[3]]) if ymax is not None else limits[3] except (TypeError, ValueError): # some plottables do not work with this rootpy function (eg. graph without points, tf1) # TODO: should be fixed upstream pass # overwrite these ranges if defaults are given if self.plot.xmin is not None: xmin = self.plot.xmin if self.plot.xmax is not None: xmax = self.plot.xmax if self.plot.ymax is not None: ymax = self.plot.ymax if self.plot.ymin is not None: ymin = self.plot.ymin if not all([val is not None for val in [xmin, xmax, ymin, ymax]]): raise TypeError("unable to determine plot axes ranges from the given plottables") colors = get_color_generator(self.plot.palette, self.plot.palette_ncolors) # draw an empty frame within the given ranges; frame_from_plottable = [p for p in self._plottables if p.get('use_as_frame')] if len(frame_from_plottable) > 0: frame = frame_from_plottable[0]['p'].Clone('__frame') frame.Reset() frame.SetStats(0) frame.xaxis.SetRangeUser(xmin, xmax) frame.yaxis.SetRangeUser(ymin, ymax) frame.GetXaxis().SetTitle(self.xtitle) frame.GetYaxis().SetTitle(self.ytitle) self._theme_plottable(frame) frame.Draw() else: frame = Graph() frame.SetName("__frame") # add a silly point in order to have root draw this frame... frame.SetPoint(0, 0, 0) frame.GetXaxis().SetLimits(xmin, xmax) frame.GetYaxis().SetLimits(ymin, ymax) frame.SetMinimum(ymin) frame.SetMaximum(ymax) frame.GetXaxis().SetTitle(self.xtitle) frame.GetYaxis().SetTitle(self.ytitle) self._theme_plottable(frame) # Draw this frame: 'A' should draw the axis, but does not work if nothing else is drawn. # L would draw a line between the points but is seems to do nothing if only one point is present # P would also draw that silly point but we don't want that! frame.Draw("AL") xtick_length = frame.GetXaxis().GetTickLength() ytick_length = frame.GetYaxis().GetTickLength() for i, pdic in enumerate(self._plottables): obj = pdic['p'] if isinstance(obj, ROOT.TLegendEntry): _root_color = Color(pdic['color']) _root_markerstyle = MarkerStyle(pdic['markerstyle']) obj.SetMarkerStyle(_root_markerstyle('root')) obj.SetMarkerColor(_root_color('root')) elif isinstance(obj, (ROOT.TH1, ROOT.TGraph, ROOT.TF1)): self._theme_plottable(obj) obj.SetMarkerStyle(pdic.get('markerstyle', 'circle')) if pdic.get('color', None): obj.color = pdic['color'] else: try: color = next(colors) except StopIteration: log.warning("Ran out of colors; defaulting to black") color = 1 obj.color = color xaxis = obj.GetXaxis() yaxis = obj.GetYaxis() # Set the title to the given title: obj.title = self.title # the xaxis depends on the type of the plottable :P if isinstance(obj, ROOT.TGraph): # SetLimit on a TH1 is simply messing up the # lables of the axis to screw over the user, presumably... xaxis.SetLimits(xmin, xmax) yaxis.SetLimits(ymin, ymax) # for unbinned data # 'P' plots the current marker, 'L' would connect the dots with a simple line # see: https://root.cern.ch/doc/master/classTGraphPainter.html for more draw options drawoption = 'Psame' elif isinstance(obj, ROOT.TH1): obj.SetStats(0) xaxis.SetRangeUser(xmin, xmax) yaxis.SetRangeUser(ymin, ymax) drawoption = 'same' elif isinstance(obj, ROOT.TF1): # xaxis.SetLimits(xmin, xmax) # yaxis.SetLimits(ymin, ymax) # for unbinned data drawoption = 'same' obj.Draw(drawoption) # Its ok if obj is non; then we just add it to the legend. else: raise TypeError("Un-plottable type given.") pad_plot.SetTicks() pad_plot.SetLogx(self.plot.logx) pad_plot.SetLogy(self.plot.logy) pad_plot.SetGridx(self.plot.gridx) pad_plot.SetGridy(self.plot.gridy) # do we have legend titles? if any([pdic.get('legend_title') for pdic in self._plottables]): leg = self._create_legend() longest_label = 0 for pdic in self._plottables: if not pdic.get('legend_title', False): continue leg.AddEntry(pdic['p'], pdic['legend_title']) if len(pdic['legend_title']) > longest_label: longest_label = len(pdic['legend_title']) # Set the legend position # vertical: if self.legend.position.startswith('t'): leg_hight = leg.y2 - leg.y1 leg.y2 = 1 - pad_plot.GetTopMargin() - ytick_length leg.y1 = leg.y2 - leg_hight elif self.legend.position.startswith('b'): leg_hight = leg.y2 - leg.y1 leg.y1 = pad_plot.GetBottomMargin() + ytick_length leg.y2 = leg.y1 + leg_hight # horizontal: if self.legend.position[1:].startswith('l'): leg_width = 0.3 leg.x1 = pad_plot.GetLeftMargin() + xtick_length leg.x2 = leg.x1 + leg_width elif self.legend.position[1:].startswith('r'): leg_width = 0.3 leg.x2 = 1 - pad_plot.GetRightMargin() - xtick_length leg.x1 = leg.x2 - leg_width if self.legend.position == 'seperate': with pad_legend: leg.Draw() else: leg.Draw() if self.plot.logx: pad_plot.SetLogx(True) if self.plot.logy: pad_plot.SetLogy(True) pad_plot.Update() # needed sometimes with import of canvas. maybe because other "plot" pads exist... return c