def plotHistos(histos, text="", option="", statbox=True): """ Plots a list of histograms """ log = logging.getLogger('pyroplot') import rootpy from rootpy.plotting import Hist, HistStack from ROOT import kRed, gPad, TPaveStats # only for 1D and 2D type histograms: # determine and set maximum for all histograms stack = HistStack() for hist in histos: if not hist.__class__.__name__ == "Hist3D": stack.Add(hist) # determine maximum value and set it for all histograms if stack.GetHists(): maxplot = stack.GetMaximum() minplot = stack.GetMinimum() for hist in stack.GetHists(): hist.SetMaximum(maxplot) # special treatment for log scale Y if gPad.GetLogy(): hist.SetMinimum(1.) else: # if histogram minimum is positive, set to 0. if minplot > 0: hist.SetMinimum(0.) for idx, hist in enumerate(histos): try: thisopt = option # if not first histo, add "same" to options so previous ones are not overwritten if idx: if statbox: thisopt += "sames" else: thisopt += "same" histcopy = hist.DrawCopy(thisopt) # on first go: print identifying text on pad if not idx and text: markPad(text=text, x=.14, y=.85, size=0.041) if statbox: thisopt += "sames" else: thisopt += "same" histcopy.Draw(thisopt) gPad.Update() if statbox: try: statBox = histcopy.GetListOfFunctions().FindObject("stats") # offset from last statbox offset = .18 # needs to be larger for Profile & 2D histos if (hist.__class__.__name__ == "Profile" or hist.__class__.__name__ == "Hist2D" or hist.__class__.__name__ == "Profile2D" or hist.__class__.__name__ == "Hist3D"): offset = .26 statBox.SetY1NDC(statBox.GetY1NDC() - offset * (idx)) statBox.SetY2NDC(statBox.GetY2NDC() - offset * (idx)) statBox.SetTextColor(hist.GetLineColor()) statBox.SetBorderSize(2) except AttributeError: log.debug("Could not get statbox for histogram " + hist.GetName()) except rootpy.ROOTError, e: log.error( "Drawing histogram %s caused ROOTError exception ('%s')" % (hist.GetName(), e.msg)) gPad.Clear() # otherwise this happens again when drawing.. markPad(text="Could not draw %s ('%s')" % (hist.GetName(), e.msg), x=0.14, y=0.4, size=0.023, color=kRed) return # give up!
def make_data_mc_comparison_plot( histograms=[], histogram_lables=[], histogram_colors=[], histogram_properties=Histogram_properties(), data_index=0, save_folder='plots/', save_as=['pdf'], normalise=False, show_ratio=False, show_stat_errors_on_mc=False, draw_vertical_line=0, systematics_for_ratio=None, systematics_for_plot=None, histograms_to_compare=None, ): ''' systematics_for_plot takes the same input as systematics_for_ratio. There may be some repition with reagrds to mc_error and mc_relative_errors, but these only deal with a flat error up and down. ''' save_folder = check_save_folder(save_folder) # make copies in order not to mess with existing histograms histograms_ = deepcopy(histograms) stack = HistStack() add_mc = stack.Add for index, histogram in enumerate(histograms_): label = histogram_lables[index] color = histogram_colors[index] histogram.SetTitle(label) if normalise: histogram.Sumw2() if not index == data_index: histogram.fillstyle = 'solid' histogram.fillcolor = color histogram.legendstyle = 'F' add_mc(histogram) data = histograms_[data_index] data.SetMarkerSize(CMS.data_marker_size) if normalise: n_events_data = data.Integral() n_events_mc = stack.Integral() data.Scale(1 / n_events_data) stack.Scale(1 / n_events_mc) # plot with matplotlib plt.figure(figsize=CMS.figsize, dpi=CMS.dpi, facecolor=CMS.facecolor) axes = None if show_ratio: ratio = data.Clone('ratio') sumHists = sum(stack.GetHists()) for bin_i in range(1, sumHists.GetNbinsX()): sumHists.SetBinError(bin_i, 0) ratio.Divide(sum(stack.GetHists())) ratio.SetMarkerSize(3) gs = gridspec.GridSpec(2, 1, height_ratios=[5, 1]) axes = plt.subplot(gs[0]) else: axes = plt.axes() if histogram_properties.set_log_y: axes.set_yscale('log', nonposy="clip") axes.set_ylim(ymin=1e-2) if systematics_for_plot != None: plusErrors = [x + 1 for x in systematics_for_plot] minusErrors = [1 - x for x in systematics_for_plot] stack_lower = sum(stack.GetHists()) stack_upper = stack_lower.Clone('upper') for bin_i in range(1, stack_lower.GetNbinsX() + 1): central_value = stack_lower.GetBinContent(bin_i) error_upper_bound = plusErrors[bin_i - 1] * central_value error_lower_bound = minusErrors[bin_i - 1] * central_value stack_upper.SetBinContent(bin_i, error_upper_bound) stack_lower.SetBinContent(bin_i, error_lower_bound) rplt.fill_between( stack_lower, stack_upper, axes, hatch='//', # facecolor = 'Black', facecolor='None', edgecolor='Grey', alpha=1., linewidth=0., zorder=len(histograms_) + 1) mc_error = histogram_properties.mc_error mc_relative_errors = histogram_properties.mc_relative_errors if mc_relative_errors: stack_lower = sum(stack.GetHists()) stack_upper = stack_lower.Clone('upper') for bin_i in range(1, stack_lower.GetNbinsX()): central_value = stack_lower.GetBinContent(bin_i) relative_error = mc_relative_errors[bin_i - 1] error_upper_bound = central_value * (1 + relative_error) error_lower_bound = central_value * (1 - relative_error) stack_lower.SetBinContent(bin_i, error_upper_bound) stack_upper.SetBinContent(bin_i, error_lower_bound) rplt.fill_between(stack_upper, stack_lower, axes, facecolor='0.75', alpha=0.5, hatch='/', zorder=len(histograms_) + 1) else: if mc_error > 0: stack_lower = sum(stack.GetHists()) stack_upper = stack_lower.Clone('upper') stack_lower.Scale(1 - mc_error) stack_upper.Scale(1 + mc_error) rplt.fill_between(stack_upper, stack_lower, axes, facecolor='0.75', alpha=0.5, hatch='/', zorder=len(histograms_) + 1) if not mc_error > 0 and show_stat_errors_on_mc: stack_lower = sum(stack.GetHists()) mc_errors = list(stack_lower.yerravg()) stack_upper = stack_lower.Clone('upper') for bin_i in range(1, stack_lower.GetNbinsX()): central_value = stack_lower.GetBinContent(bin_i) error = mc_errors[bin_i - 1] error_upper_bound = central_value + error error_lower_bound = central_value - error stack_lower.SetBinContent(bin_i, error_lower_bound) stack_upper.SetBinContent(bin_i, error_upper_bound) rplt.fill_between(stack_upper, stack_lower, axes, facecolor='0.75', alpha=0.5, hatch='/', zorder=len(histograms_) + 1) # a comment on zorder: the MC stack should be always at the very back (z = 1), # then the MC error (z = len(histograms_) + 1) and finally the data # (z = len(histograms_) + 2) rplt.hist(stack, stacked=True, axes=axes, zorder=1) rplt.errorbar(data, emptybins=histogram_properties.emptybins, axes=axes, xerr=histogram_properties.xerr, elinewidth=2, capsize=10, capthick=2, zorder=len(histograms_) + 2) if histograms_to_compare: h_compare = {} for h, l, c in zip(histograms_to_compare['hists'], histograms_to_compare['labels'], histograms_to_compare['colours']): for histogram in histograms_: if histogram.GetTitle() not in [ histograms_to_compare['to_replace'], 'data' ]: h += histogram h_compare[l] = [h, c] rplt.step( h, axes=axes, label=l, color=c, linewidth=4, ) # put legend into the correct order (data is always first!) handles, labels = axes.get_legend_handles_labels() data_label_index = labels.index('data') data_handle = handles[data_label_index] labels.remove('data') handles.remove(data_handle) labels.insert(0, 'data') handles.insert(0, data_handle) if mc_error > 0 or (not mc_error > 0 and show_stat_errors_on_mc): p1 = Rectangle((0, 0), 1, 1, fc="0.75", alpha=0.5, hatch='/') handles.append(p1) labels.append(histogram_properties.mc_errors_label) l1 = axes.legend(handles, labels, numpoints=1, frameon=histogram_properties.legend_color, bbox_to_anchor=histogram_properties.legend_location, bbox_transform=plt.gcf().transFigure, prop=CMS.legend_properties, ncol=histogram_properties.legend_columns) l1.set_zorder(102) set_labels(plt, histogram_properties, show_x_label=not show_ratio, axes=axes) x_limits = histogram_properties.x_limits y_limits = histogram_properties.y_limits if len(x_limits) >= 2: axes.set_xlim(xmin=x_limits[0], xmax=x_limits[-1]) if len(y_limits) >= 2: axes.set_ylim(ymin=y_limits[0], ymax=y_limits[-1]) else: y_max = get_best_max_y( histograms_, x_limits=x_limits) * histogram_properties.y_max_scale print("Chosen limits : ", 0, y_max) axes.set_ylim(ymin=0, ymax=y_max) if histogram_properties.set_log_y: if not len(y_limits) == 2: # if not user set y-limits, set default axes.set_ylim(ymin=1e-1) #draw a red vertical line if needed: if draw_vertical_line != 0: plt.axvline(x=draw_vertical_line, color='red', linewidth=3) if show_ratio: plt.setp(axes.get_xticklabels(), visible=False) ax1 = plt.subplot(gs[1]) ax1.minorticks_on() ax1.grid(True, 'major', linewidth=1) ax1.axhline(y=1, linewidth=1) set_labels(plt, histogram_properties, show_x_label=True, show_title=False) plt.ylabel(r'$\frac{\mathrm{data}}{\mathrm{pred.}}$', CMS.y_axis_title) ax1.yaxis.set_label_coords(-0.115, 0.8) rplt.errorbar(ratio, emptybins=histogram_properties.emptybins, axes=ax1, xerr=histogram_properties.xerr, elinewidth=1.5, capsize=5, capthick=1.5) if histograms_to_compare: for l, h in h_compare.iteritems(): r = data.Clone(l).Divide(h[0]) rplt.step( r, axes=ax1, label='', colour=h[1], linewidth=2, ) if len(x_limits) >= 2: ax1.set_xlim(xmin=x_limits[0], xmax=x_limits[-1]) if len(histogram_properties.ratio_y_limits) >= 2: ax1.set_ylim(ymin=histogram_properties.ratio_y_limits[0], ymax=histogram_properties.ratio_y_limits[-1]) # dynamic tick placement adjust_ratio_ticks(ax1.yaxis, n_ticks=3, y_limits=histogram_properties.ratio_y_limits) if histogram_properties.integerXVariable: ax1.tick_params(axis='x', which='minor', bottom='off', top='off') if systematics_for_ratio != None: plusErrors = [x + 1 for x in systematics_for_ratio] minusErrors = [1 - x for x in systematics_for_ratio] ratioPlusError = ratio.Clone('plus') ratioMinusError = ratio.Clone('minus') for bin_i in range(1, ratioPlusError.GetNbinsX() + 1): ratioPlusError.SetBinContent(bin_i, plusErrors[bin_i - 1]) ratioMinusError.SetBinContent(bin_i, minusErrors[bin_i - 1]) rplt.fill_between( ratioPlusError, ratioMinusError, axes, hatch='//', # facecolor = 'Black', facecolor='None', edgecolor='Grey', alpha=1., linewidth=0., # zorder = len(histograms_) + 1 zorder=0) if CMS.tight_layout: plt.tight_layout() for save in save_as: if save == 'root': saveHistogramsToROOTFile( data, stack, save_folder + histogram_properties.name + '.' + save) else: plt.savefig(save_folder + histogram_properties.name + '.' + save) plt.close()
canvas = Canvas() objects = [] # create a stack stack = HistStack() stack.Add( Hist(100, -5, 5, color='salmon', drawstyle='hist', fillstyle='solid').FillRandom(F1('TMath::Gaus(x, 2, 1)'), 500)) stack.Add( Hist(100, -5, 5, color='powderblue', drawstyle='hist', fillstyle='solid').FillRandom(F1('TMath::Gaus(x, 2, 0.6)'), 300)) objects.append(stack) _sumStack = None for h in stack.GetHists(): if _sumStack is None: _sumStack = h.Clone() else: _sumStack.Add(h) # _sumStack.fillstyle = '/' # stack.markerstyle = 0 stackError = _sumStack.poisson_errors() stackError.fillstyle = '/' stackError.fillcolor = 'black' stackError.drawstyle = '2' # stack.Draw('HIST') # stackError.Draw('2 [] same') objects.append(stackError) import ROOT