예제 #1
0
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!
예제 #2
0
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()
예제 #3
0
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