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))