def plot_unfolded_normalised_pt_bin_offset(self, bin_offset_2=0):

        for ibin, (bin_edge_low, bin_edge_high) in enumerate(zip(self.bins[:-1], self.bins[1:])):
            # print(bin_edge_low, bin_edge_high)
            ind2 = ibin+bin_offset_2
            if ind2 < 0:
                # print("...skipping")
                continue

            hbc1_args = dict(ind=ibin, binning_scheme='generator')
            unfolded1_hist_bin_stat_errors = self.hist_bin_chopper1.get_pt_bin_normed_div_bin_width('unfolded_stat_err', **hbc1_args)

            hbc2_args = dict(ind=ind2, binning_scheme='generator')
            unfolded2_hist_bin_stat_errors = self.hist_bin_chopper2.get_pt_bin_normed_div_bin_width('unfolded_stat_err', **hbc2_args)

            entries = [
                Contribution(unfolded1_hist_bin_stat_errors,
                             label="Data (stat. unc.)\n%s" % self.setup1.label,
                             line_color=self.plot_styles['unfolded_stat_colour'],
                             line_width=self.line_width,
                             line_style=1,
                             marker_color=self.plot_styles['unfolded_stat_colour'],
                             marker_style=cu.Marker.get('circle'),
                             marker_size=0.75),
                Contribution(unfolded2_hist_bin_stat_errors,
                             label="Data (stat. unc.)\n%s" % self.setup2.label,
                             line_color=self.plot_styles['unfolded_unreg_colour'],
                             line_width=self.line_width,
                             line_style=1,
                             marker_color=self.plot_styles['unfolded_unreg_colour'],
                             marker_style=cu.Marker.get('square', filled=False),
                             marker_size=0.75,
                             subplot=unfolded1_hist_bin_stat_errors),
            ]
            if not self.check_entries(entries, "plot_unfolded_normalised_pt_bin_offset %d" % (ibin)):
                return

            plot = Plot(entries,
                        ytitle=self.setup1.pt_bin_normalised_differential_label,
                        title=self.get_pt_bin_title(bin_edge_low, bin_edge_high),
                        xlim=qgp.calc_auto_xlim(entries),
                        subplot_limits=(0.8, 1.2),
                        **self.pt_bin_plot_args)
            self._modify_plot(plot)
            plot.subplot_title = "* / %s" % (self.region1['label'])
            plot.plot("NOSTACK E1")
            plot.save("%s/compare_unfolded_%s_bin_%d_divBinWidth.%s" % (self.setup1.output_dir, self.setup1.append, ibin, self.setup1.output_fmt))
    def plot_detector_unfolded(self, do_dijet=True, do_zpj=True):
        for ibin, (bin_edge_low, bin_edge_high) in enumerate(
                zip(self.bins[:-1], self.bins[1:])):
            hbc_args = dict(ind=ibin,
                            axis='pt',
                            do_norm=True,
                            do_div_bin_width=True,
                            binning_scheme='generator')
            print("----------pt bin", ibin, "=", bin_edge_low, "-",
                  bin_edge_high)

            def determine_num_dp(values):
                """Determine correct number of decimal places
                so that the smallest value has 1 digit

                If smallest > 1, then just returns 0
                """
                smallest_value = min([v for v in values if v > 0])
                print('determine_num_dp', smallest_value)
                if smallest_value > 1:
                    return 0
                # use log10 to figure out exponent, then floor it
                # e.g. log10(5.5E-3) = -2.25...., floor(-2.25...) = -3
                n_dp = abs(floor(log10(smallest_value)))
                return n_dp

            def setup_beta_function(name):
                fit = ROOT.TF1(name, "[2]*TMath::BetaDist(x,[0],[1])", 0, 1)
                fit.SetParameter(0, 3)
                fit.SetParLimits(0, 0, 100)
                fit.SetParameter(1, 5)
                fit.SetParLimits(1, 0, 100)
                fit.SetParameter(2, 0.05)
                fit.SetParLimits(2, 0, 10)
                # fit.SetParameter(3, 0)
                # fit.SetParLimits(3, 0, 10)
                # fit.SetParameter(4, 1)
                # fit.SetParLimits(4, 0.1, 10)
                return fit

            fit_opts = "S"

            # Weird setup here: basically need all the values going into legend first,
            # to be able to determine the number of decimal points
            # Then we can actually create the Contributions and plot afterwards
            dijet_detector_hist, dijet_unfolded_hist = None, None
            dijet_detector_mean, dijet_detector_mean_err = None, None
            dijet_unfolded_mean, dijet_unfolded_mean_err = None, None
            zpj_detector_hist, zpj_unfolded_hist = None, None
            zpj_detector_mean, zpj_detector_mean_err = None, None
            zpj_unfolded_mean, zpj_unfolded_mean_err = None, None
            dijet_entries, zpj_entries = [], []
            errors = []

            if do_dijet:
                # get detector-level data
                dijet_detector_hist = self.dijet_hbc.get_bin_plot(
                    'input_hist_gen_binning_bg_subtracted', **hbc_args)
                dijet_detector_mean, dijet_detector_mean_err = self.get_uncorrelated_mean_err(
                    dijet_detector_hist, is_density=True)
                errors.append(dijet_detector_mean_err)

                # fit with beta function
                # dijet_detector_fit = setup_beta_function("beta_fit_dijet_detector")
                # fit_result = dijet_detector_hist.Fit(dijet_detector_fit, fit_opts, "")
                # fit_result.Print()

                # get unfolded data
                dijet_unfolded_hist = self.dijet_hbc.get_bin_plot(
                    "unfolded", **hbc_args)
                dijet_cov_matrix = self.dijet_hbc.get_bin_plot(
                    self.dijet_region['unfolder'].total_ematrix_name,
                    **hbc_args)
                dijet_unfolded_mean, dijet_unfolded_mean_err = self.get_correlated_mean_err(
                    dijet_unfolded_hist, dijet_cov_matrix, is_density=True)
                errors.append(dijet_unfolded_mean_err)

                # dijet_unfolded_fit = setup_beta_function("beta_fit_dijet_unfolded")
                # fit_result = dijet_unfolded_hist.Fit(dijet_unfolded_fit, fit_opts, "")
                # fit_result.Print()

            if do_zpj:
                # get detector-level data
                zpj_detector_hist = self.zpj_hbc.get_bin_plot(
                    'input_hist_gen_binning_bg_subtracted', **hbc_args)
                zpj_detector_mean, zpj_detector_mean_err = self.get_uncorrelated_mean_err(
                    zpj_detector_hist, is_density=True)
                errors.append(zpj_detector_mean_err)

                # zpj_detector_fit = setup_beta_function("beta_fit_zpj_detector")
                # fit_result = zpj_detector_hist.Fit(zpj_detector_fit, fit_opts, "")
                # fit_result.Print()

                # get unfolded data
                zpj_unfolded_hist = self.zpj_hbc.get_bin_plot(
                    "unfolded", **hbc_args)
                zpj_cov_matrix = self.zpj_hbc.get_bin_plot(
                    self.zpj_region['unfolder'].total_ematrix_name, **hbc_args)
                zpj_unfolded_mean, zpj_unfolded_mean_err = self.get_correlated_mean_err(
                    zpj_unfolded_hist, zpj_cov_matrix, is_density=True)
                errors.append(zpj_unfolded_mean_err)

                # zpj_unfolded_fit = setup_beta_function("beta_fit_zpj_unfolded")
                # fit_result = zpj_unfolded_hist.Fit(zpj_unfolded_fit, fit_opts, "")
                # fit_result.Print()

            # n_dp = determine_num_dp(errors)
            n_dp = 3

            # kerning necessary as it puts massive space around #pm
            # but the first #kern doesn't affect the space after #pm as much (?!),
            # so I have to add another one with harder kerning
            # we use %.(n_dp)f as our float format str to ensure the correct number of dp are shown (and not rounded off)
            stat_template = 'Mean = {:.%df}#kern[-0.2dx]{{ #pm}}#kern[-0.5dx]{{ }}{}' % (
                n_dp)
            err_template = "{:.%df}" % n_dp

            def _stat_label(mean, err, dp):
                err_str = err_template.format(err)
                # if the error is so small that it would display as 0.000,
                # i.e. < 0.0005, instead show < 0.001
                if err < 5 * 10**(-dp - 1):
                    err_str = "#lower[-0.09dy]{<}#kern[-0.75dx]{ }" + err_template.format(
                        1 * 10**(-dp))
                return stat_template.format(round(mean, dp), err_str)

            if do_dijet:
                dijet_entries.append(
                    Contribution(
                        dijet_detector_hist,
                        label='Detector-level (stat. only)\n%s' %
                        (_stat_label(dijet_detector_mean,
                                     dijet_detector_mean_err, n_dp)),
                        line_color=self.plot_colours['dijet_colour'],
                        line_width=self.line_width,
                        line_style=self.line_style_detector,
                        marker_color=self.plot_colours['dijet_colour'],
                        marker_style=cu.Marker.get('circle', filled=False),
                        marker_size=0.75,
                        subplot=zpj_detector_hist if do_zpj else None))
                dijet_entries.append(
                    Contribution(
                        dijet_unfolded_hist,
                        label='Particle-level\n%s' %
                        (_stat_label(dijet_unfolded_mean,
                                     dijet_unfolded_mean_err, n_dp)),
                        line_color=self.plot_colours['dijet_colour'],
                        line_width=self.line_width,
                        line_style=1,
                        marker_color=self.plot_colours['dijet_colour'],
                        marker_style=cu.Marker.get('circle', filled=True),
                        marker_size=0.75,
                        subplot=zpj_unfolded_hist if do_zpj else None))

            if do_zpj:
                zpj_entries.append(
                    Contribution(
                        zpj_detector_hist,
                        label='Detector-level (stat. only)\n%s' % (_stat_label(
                            zpj_detector_mean, zpj_detector_mean_err, n_dp)),
                        line_color=self.plot_colours['zpj_colour'],
                        line_width=self.line_width,
                        line_style=self.line_style_detector,
                        marker_color=self.plot_colours['zpj_colour'],
                        marker_style=cu.Marker.get('square', filled=False),
                        marker_size=0.75))
                zpj_entries.append(
                    Contribution(
                        zpj_unfolded_hist,
                        label='Particle-level\n%s' % (_stat_label(
                            zpj_unfolded_mean, zpj_unfolded_mean_err, n_dp)),
                        line_color=self.plot_colours['zpj_colour'],
                        line_width=self.line_width,
                        line_style=1,
                        marker_color=self.plot_colours['zpj_colour'],
                        marker_style=cu.Marker.get('square', filled=True),
                        marker_size=0.75))

            all_entries = list(chain(dijet_entries, zpj_entries))
            plot = Plot(
                all_entries,
                ytitle=self.pt_bin_normalised_differential_label,
                title=self.get_pt_bin_title(bin_edge_low, bin_edge_high),
                legend=True,
                xlim=qgp.calc_auto_xlim(
                    all_entries),  # set x lim to where data is non-0
                what="hist",
                xtitle=self.particle_title,
                has_data=self.has_data,
                ylim=[0, None],
                is_preliminary=self.is_preliminary)
            # plot.default_canvas_size = (600, 600)
            # plot.left_margin = 0.2
            # plot.left_title_offset_fudge_factor = 8
            plot.y_padding_max_linear = 1.8
            plot.top_margin = 0.07
            plot.title_start_y = 0.888
            plot.cms_text_y = 0.94
            plot.lumi = cu.get_lumi_str(do_dijet=do_dijet, do_zpj=do_zpj)
            if do_zpj and do_dijet:
                plot.subplot_type = 'ratio'
                plot.subplot_title = 'Dijet / Z+jet'
                plot.subplot_limits = (0.25, 2.75)

            # disable adding objects to legend & drawing - we'll do it manually
            # since we want proper error bar
            plot.do_legend = False
            plot.splitline_legend = False
            # plot.legend.SetFillColor(ROOT.kRed)
            # plot.legend.SetFillStyle(1001)
            plot.plot("NOSTACK E1 X0")
            # plot.get_modifier().GetYaxis().SetTitleOffset(plot.get_modifier().GetYaxis().GetTitleOffset()*1.5)
            plot.main_pad.cd()

            for e in [plot.contributions[0].obj, plot.contributions[2].obj]:
                e.Draw("HIST SAME")
            for e in [plot.contributions[1].obj, plot.contributions[3].obj]:
                e.Draw("L SAME")

            plot.canvas.cd()

            # unfolded_fit = ROOT.TF1("beta_fit_dijet_unfolded", "[2]*TMath::BetaDist(x,[0],[1])", 0, 1)
            # unfolded_fit.SetParameter(0, 3)
            # unfolded_fit.SetParLimits(0, 0, 100)
            # unfolded_fit.SetParameter(1, 5)
            # unfolded_fit.SetParLimits(1, 0, 100)
            # unfolded_fit.SetParameter(2, .1)
            # unfolded_fit.SetParLimits(2, 0, 1000)
            # # fit_result = unfolded_hist.Fit(unfolded_fit, "EMSR", "", 0, 1)
            # # fit_result.Print()
            # unfolded_fit.SetLineColor(ROOT.kRed)
            # unfolded_fit.SetLineWidth(2)
            # plot.main_pad.cd()
            # unfolded_fit.Draw("SAME")

            # Create dummy graphs with the same styling to put into the legend
            dummy_gr = ROOT.TGraphErrors(1, array('d', [1]), array('d', [1]),
                                         array('d', [1]), array('d', [1]))
            dummies = []  # to stop garbage collection
            dummy_entries = []  # to stop garbage collection
            label_height = 0.03
            legend_height = 0.12
            legend_x1 = 0.54
            legend_x2 = 0.75  # this doesn't really control width - legend_text_size mainly does
            label_left_offset = 0.01
            label_text_size = 0.032
            label_top = plot.title_start_y
            legend_text_size = 0.028
            inter_region_offset = 0.025
            if do_dijet:
                dijet_legend = plot.legend.Clone()
                dijet_legend.SetX1(legend_x1)
                dijet_legend.SetX2(legend_x2)
                dijet_legend.SetY1(label_top - label_height - legend_height)
                dijet_legend.SetY2(label_top - label_height + 0.01)
                dijet_legend.SetTextSize(legend_text_size)
                # Add text with region label
                dijet_pt = ROOT.TPaveText(legend_x1 - label_left_offset,
                                          label_top - label_height,
                                          legend_x2 - label_left_offset,
                                          label_top, "NDC NB")
                dijet_pt.SetFillStyle(0)
                dijet_pt.SetBorderSize(0)
                text = dijet_pt.AddText(qgc.Dijet_CEN_LABEL)
                text.SetTextAlign(11)
                text.SetTextFont(62)
                text.SetTextSize(label_text_size)
                dummies.append(dijet_pt)
                dummies.append(text)
                dijet_pt.Draw()
                for cont in dijet_entries:
                    this_dummy_entry = dummy_gr.Clone()
                    cont.update_obj_styling(this_dummy_entry)
                    if "\n" in cont.label:
                        parts = cont.label.split("\n")
                        dijet_legend.AddEntry(
                            this_dummy_entry,
                            "#splitline{%s}{%s}" % (parts[0], parts[1]), "LEP")
                    else:
                        dijet_legend.AddEntry(this_dummy_entry, cont.label,
                                              "LEP")
                    dummy_entries.append(
                        this_dummy_entry)  # to avoid garbage collection
                dijet_legend.Draw()
                dummies.append(dijet_legend)
                # setup for Z+J
                label_top -= label_height + legend_height + inter_region_offset

            if do_zpj:
                zpj_legend = plot.legend.Clone()
                zpj_legend.SetX1(legend_x1)
                zpj_legend.SetX2(legend_x2)
                zpj_legend.SetY1(label_top - label_height - legend_height)
                zpj_legend.SetY2(label_top - label_height + 0.01)
                zpj_legend.SetTextSize(legend_text_size)
                # Add text with region label
                zpj_pt = ROOT.TPaveText(legend_x1 - label_left_offset,
                                        label_top - label_height,
                                        legend_x2 - label_left_offset,
                                        label_top, "NDC NB")
                zpj_pt.SetFillStyle(0)
                zpj_pt.SetBorderSize(0)
                text = zpj_pt.AddText(qgc.ZpJ_LABEL)
                text.SetTextAlign(11)
                text.SetTextFont(62)
                text.SetTextSize(label_text_size)
                dummies.append(zpj_pt)
                dummies.append(text)
                zpj_pt.Draw()
                for cont in zpj_entries:
                    this_dummy_entry = dummy_gr.Clone()
                    cont.update_obj_styling(this_dummy_entry)
                    if "\n" in cont.label:
                        parts = cont.label.split("\n")
                        zpj_legend.AddEntry(
                            this_dummy_entry,
                            "#splitline{%s}{%s}" % (parts[0], parts[1]), "LEP")
                    else:
                        zpj_legend.AddEntry(this_dummy_entry, cont.label,
                                            "LEP")
                    dummy_entries.append(this_dummy_entry)
                zpj_legend.Draw()
                dummies.append(zpj_legend)

            # Add legend to ratio plot
            plot.subplot_pad.cd()

            for e in [plot.subplot_contributions[0]]:
                e.Draw("HIST SAME")
            for e in [plot.subplot_contributions[1]]:
                e.Draw("L SAME")

            plot.subplot_leg = ROOT.TLegend(0.3, 0.73, 0.9, 0.9)
            plot.subplot_leg.SetTextSize(0.07)
            plot.subplot_leg.SetFillStyle(0)
            plot.subplot_leg.SetNColumns(2)
            plot.subplot_leg.AddEntry(dummy_entries[0], "Detector-level",
                                      "LEP")
            plot.subplot_leg.AddEntry(dummy_entries[1], "Particle-level",
                                      "LEP")
            plot.subplot_leg.Draw()

            plot.canvas.cd()

            parts = [
                'detector_unfolded', 'dijet' if do_dijet else None,
                'zpj' if do_zpj else None, self.append,
                'bin_%d' % ibin, 'divBinWidth',
                f'{self.paper_str}.{self.output_fmt}'
            ]
            filename = '_'.join([x for x in parts if x])
            plot.save("%s/%s" % (self.output_dir, filename))
            print("%s/%s" % (self.output_dir, filename))
    def plot_unfolded_with_yoda_normalised(self,
                                           do_chi2=False,
                                           do_zoomed=True):
        data_total_errors_style = dict(
            label="Data (total unc.)",
            line_color=self.plot_styles['unfolded_total_colour'],
            line_width=self.line_width,
            line_style=1,
            marker_color=self.plot_styles['unfolded_total_colour'],
            marker_style=cu.Marker.get('circle'),
            marker_size=self.plot_styles['unfolded_marker_size'],
            leg_draw_opt="LEP")
        data_stat_errors_style = dict(
            label="Data (stat. unc.)",
            line_color=self.plot_styles['unfolded_stat_colour'],
            line_width=self.line_width,
            line_style=1,
            marker_color=self.plot_styles['unfolded_stat_colour'],
            marker_style=cu.Marker.get('circle'),
            marker_size=0.0001,
            leg_draw_opt="LEP"
        )  # you need a non-0 marker to get the horizontal bars at the end of errors

        mc_style = dict(label=self.region['mc_label'],
                        line_color=self.plot_styles['gen_colour'],
                        line_width=self.line_width,
                        marker_color=self.plot_styles['gen_colour'],
                        marker_size=self.plot_styles['gen_marker_size'],
                        marker_style=self.plot_styles['gen_marker'],
                        leg_draw_opt="LEP"
                        if self.plot_styles['gen_marker_size'] > 0 else "LE")

        rivet_path, rivet_region, rivet_radius, rivet_lambda, rivet_pt_bins = get_matching_rivet_setup(
            self.setup)

        for ibin, (bin_edge_low, bin_edge_high) in enumerate(
                zip(self.bins[:-1], self.bins[1:])):
            hbc_args = dict(ind=ibin, binning_scheme='generator')
            mc_gen_hist_bin = self.hist_bin_chopper.get_pt_bin_normed_div_bin_width(
                'hist_truth', **hbc_args)
            unfolded_hist_bin_stat_errors = self.hist_bin_chopper.get_pt_bin_normed_div_bin_width(
                'unfolded_stat_err', **hbc_args)
            unfolded_hist_bin_total_errors = self.hist_bin_chopper.get_pt_bin_normed_div_bin_width(
                'unfolded', **hbc_args)

            # Get RIVET hists, which are absolute counts, so need normalising
            rivet_hist_name = '/%s/%s' % (
                rivet_path,
                rn.get_plot_name(rivet_radius, rivet_region, rivet_lambda,
                                 rivet_pt_bins[ibin]))
            rivet_hists = [
                qgp.normalise_hist_divide_bin_width(
                    yoda.root.to_root(ent['yoda_dict'][rivet_hist_name]))
                for ent in self.rivet_entries
            ]

            # Create copy of data to go on top of stat unc,
            # but remove vertical error bar so we can see the stat unc
            # Note that you CAN'T set it to 0, otherwise vertical lines connecting
            # bins start being drawn. Instead set it to some super small value.
            unfolded_hist_bin_total_errors_marker_noerror = unfolded_hist_bin_total_errors.Clone(
            )  # clone to avoid restyling the original as well
            for i in range(
                    1,
                    unfolded_hist_bin_total_errors_marker_noerror.GetNbinsX() +
                    1):
                unfolded_hist_bin_total_errors_marker_noerror.SetBinError(
                    i, 1E-100)

            data_entries = [
                Contribution(unfolded_hist_bin_total_errors,
                             **data_total_errors_style),
                Contribution(unfolded_hist_bin_stat_errors,
                             **data_stat_errors_style),
                # do data with black marker to get it on top
                Contribution(unfolded_hist_bin_total_errors_marker_noerror,
                             **data_total_errors_style),
            ]

            # For subplot to ensure only MC errors drawn, not MC+data
            data_no_errors = unfolded_hist_bin_total_errors_marker_noerror.Clone(
            )
            cu.remove_th1_errors(data_no_errors)

            this_mc_style = deepcopy(mc_style)

            rivet_styles = []
            for ind, _ in enumerate(rivet_hists):
                s_dict = self.rivet_entries[ind]['style_dict']
                rivet_styles.append(
                    dict(label=s_dict['label'],
                         line_color=s_dict['color'],
                         line_width=self.line_width,
                         marker_color=s_dict['color'],
                         marker_size=s_dict.get(
                             'marker_size',
                             self.plot_styles['gen_marker_size']),
                         marker_style=s_dict['marker_style'],
                         leg_draw_opt="LEP"
                         if self.plot_styles['gen_marker_size'] > 0 else "LE"))

            # Calculate chi2 between data and MCs if desired
            if do_chi2:
                # print("unfolded_alt_truth bin", ibin)
                ematrix = self.hist_bin_chopper.get_pt_bin_normed_div_bin_width(
                    self.unfolder.total_ematrix_name, **hbc_args)
                # stats are chi2, ndof, p
                mc_stats = calc_chi2_stats(unfolded_hist_bin_total_errors,
                                           mc_gen_hist_bin, ematrix)
                # print(mc_stats)
                # print(alt_mc_stats)
                nbins = sum([
                    1 for i in range(
                        1,
                        unfolded_hist_bin_total_errors.GetNbinsX() + 1)
                    if unfolded_hist_bin_total_errors.GetBinContent(i) != 0
                ])
                # reduced_chi2 = mc_stats[0] / nbins
                # alt_reduced_chi2 = alt_mc_stats[0] / nbins

                n_sig_fig = 2
                chi2_template = "\n#lower[-0.1]{{(#chi^{{2}} / N_{{bins}} = {chi2:g} / {nbins:d})}}"
                this_mc_style['label'] += chi2_template.format(chi2=cu.nsf(
                    mc_stats[0], n_sig_fig),
                                                               nbins=nbins)

                for ind, h in enumerate(rivet_hists):
                    this_stats = calc_chi2_stats(
                        unfolded_hist_bin_total_errors, h, ematrix)
                    rivet_styles[ind]['label'] += chi2_template.format(
                        chi2=cu.nsf(this_stats[0], n_sig_fig), nbins=nbins)

            mc_entries = [
                Contribution(mc_gen_hist_bin,
                             subplot=data_no_errors,
                             **this_mc_style),
            ]

            for h, s_dict in zip(rivet_hists, rivet_styles):
                mc_entries.append(
                    Contribution(h, subplot=data_no_errors, **s_dict))

            entries = [
                # Draw MC
                *mc_entries,
                # Draw data after to put on top of MC
                *data_entries
            ]

            func_name = cu.get_current_func_name()
            if not self.check_entries(entries, "%s bin %d" %
                                      (func_name, ibin)):
                return

            ymin = 0
            if np.any(
                    cu.th1_to_ndarray(unfolded_hist_bin_total_errors)[0] < 0):
                ymin = None  # let it do its thing and auto calc ymin
            max_rel_err = 0.5 if "multiplicity" in self.setup.angle.var.lower(
            ) else -1
            plot = Plot(
                entries,
                ytitle=self.setup.pt_bin_normalised_differential_label,
                title=self.get_pt_bin_title(bin_edge_low, bin_edge_high),
                legend=True,
                xlim=qgp.calc_auto_xlim(
                    entries[2:3],
                    max_rel_err=0.5),  # set x lim to where data is non-0
                ylim=[ymin, None],
                **self.pt_bin_plot_args)

            plot.subplot_title = qgc.SIM_DATA_STR
            self._modify_plot_paper(plot)

            # disable adding objects to legend & drawing - we'll do it manually
            plot.do_legend = False
            plot.legend.SetTextSize(0.03)
            plot.legend.SetY1(0.6)
            plot.legend.SetX1(0.57)
            plot.legend.SetX2(0.93)
            if len(entries) > 4:
                # if lots of entries, try auto-expand
                plot.legend.SetY1(0.6 - (0.02 * (len(entries) - 4)))
            # plot.legend.SetEntrySeparation(0.005)
            subplot_draw_opts = "NOSTACK E1"
            plot.plot("NOSTACK E1", subplot_draw_opts)

            dummy_graphs = qgp.do_fancy_legend(chain(data_entries[:2],
                                                     mc_entries),
                                               plot,
                                               use_splitline=False)

            plot.canvas.cd()
            plot.legend.Draw()

            # Create hists for data with error region for ratio
            # Easiest way to get errors right is to do data (with 0 errors)
            # and divide by data (with errors), as if you had MC = data with 0 error
            data_stat_ratio = data_no_errors.Clone()
            data_stat_ratio.Divide(unfolded_hist_bin_stat_errors)
            data_stat_ratio.SetFillStyle(3245)
            data_stat_ratio.SetFillColor(
                self.plot_styles['unfolded_stat_colour'])
            data_stat_ratio.SetLineWidth(0)
            data_stat_ratio.SetMarkerSize(0)

            data_total_ratio = data_no_errors.Clone()
            data_total_ratio.Divide(unfolded_hist_bin_total_errors)
            data_total_ratio.SetFillStyle(3254)
            data_total_ratio.SetFillColor(
                self.plot_styles['unfolded_total_colour'])
            data_total_ratio.SetLineWidth(0)
            data_total_ratio.SetMarkerSize(0)

            # now draw the data error shaded area
            # this is a bit hacky - basically draw them on the ratio pad,
            # then redraw the existing hists & line to get them ontop
            # note that we use "same" for all - this is to keep the original axes
            # (we may want to rethink this later?)
            plot.subplot_pad.cd()
            draw_opt = "E2 SAME"
            data_stat_ratio.Draw(draw_opt)
            data_total_ratio.Draw(draw_opt)
            plot.subplot_line.Draw()
            plot.subplot_container.Draw("SAME" + subplot_draw_opts)

            # Add subplot legend
            x_left = 0.25
            y_bottom = 0.75
            width = 0.67
            height = 0.15
            plot.subplot_legend = ROOT.TLegend(x_left, y_bottom,
                                               x_left + width,
                                               y_bottom + height)
            plot.subplot_legend.AddEntry(data_total_ratio,
                                         qgc.DATA_TOTAL_UNC_STR, "F")
            plot.subplot_legend.AddEntry(data_stat_ratio,
                                         qgc.DATA_STAT_UNC_STR, "F")
            plot.subplot_legend.SetTextSize(0.085)
            plot.subplot_legend.SetFillStyle(0)
            plot.subplot_legend.SetNColumns(2)
            plot.subplot_legend.Draw()
            plot.canvas.cd()

            stp = self.setup
            fname = f"unfolded_{stp.append}_rivet_bin_{ibin:d}_divBinWidth{stp.paper_str}.{stp.output_fmt}"
            self.save_plot(plot, os.path.join(stp.output_dir, fname))

            # Do version with small x values only
            if do_zoomed:
                if self.setup.angle.var in [
                        "jet_thrust_charged", "jet_width_charged",
                        "jet_thrust", "jet_width"
                ]:
                    # plot.ylim = (1E-5)
                    plot.y_padding_max_log = 50
                    plot.y_padding_min_log = 0.5
                    plot.ylim = None
                    plot.set_logy(do_exponent=False, do_more_labels=False)
                    fname = f"unfolded_{stp.append}_alt_truth_bin_{ibin:d}_divBinWidth_logY.{stp.output_fmt}"
                    self.save_plot(plot, os.path.join(stp.output_dir, fname))

                if self.setup.angle.var in [
                        "jet_LHA_charged", "jet_thrust_charged",
                        "jet_width_charged", "jet_thrust", "jet_width"
                ]:
                    bin_edges = cu.get_bin_edges(mc_gen_hist_bin, 'x')
                    # get the bin edge thats smallest between 0.2, and 5th bin
                    bin_lt_lim = [x for x in bin_edges if x < 0.2][-1]
                    upper_bin = min(bin_edges[5], bin_lt_lim)
                    plot2 = Plot(
                        entries,
                        ytitle=self.setup.pt_bin_normalised_differential_label,
                        title=self.get_pt_bin_title(bin_edge_low,
                                                    bin_edge_high),
                        xlim=(0, upper_bin),
                        **self.pt_bin_plot_args)
                    self._modify_plot(plot2)
                    plot2.subplot_title = "* / Generator"
                    plot2.plot("NOSTACK E1")
                    # plot2.set_logx(do_exponent=False)
                    fname = f"unfolded_{stp.append}_rivet_bin_{ibin:d}_divBinWidth_lowX.{stp.output_fmt}"
                    self.save_plot(plot2, os.path.join(stp.output_dir, fname))