Ejemplo n.º 1
0
    def draw_legend(self,
                    axis: AxesType,
                    inside: bool,
                    loc: Optional[Union[int, str]] = None,
                    ncols: Optional[int] = None,
                    y_axis_scale: Optional[float] = None,
                    font_size: Optional[Union[int, float, str]] = None,
                    bbox_to_anchor_tuple: Tuple[float, float] = None) -> None:
        if loc is None:
            loc = self.legend_loc_default
        if ncols is None:
            ncols = self.legend_cols_default
        if font_size is None:
            font_size = self.legend_font_size_default

        if inside:
            axis.legend(frameon=False, loc=loc, ncol=ncols, fontsize=font_size)

            if y_axis_scale is not None:
                y_limits = axis.get_ylim()
                axis.set_ylim(bottom=y_limits[0],
                              top=y_axis_scale * y_limits[1])
        else:
            if bbox_to_anchor_tuple is None:
                bbox_to_anchor_tuple = (1., 1.)

            axis.legend(frameon=False,
                        loc=loc,
                        ncol=ncols,
                        bbox_to_anchor=bbox_to_anchor_tuple)
Ejemplo n.º 2
0
    def _set_axis_tick_labels(self, ax: AxesType,
                              migration_matrix_shape: Tuple[int, ...]) -> None:
        tick_positions = np.arange(0,
                                   len(self.bin_edges) + 2,
                                   1) - 0.5  # type: np.array
        assert len(tick_positions) - 1 == migration_matrix_shape[
            0] == migration_matrix_shape[1], (
                len(tick_positions),
                (tick_positions[0], tick_positions[-1]),
                (self.bin_edges[0], self.bin_edges[-1]),
                migration_matrix_shape,
            )

        tick_start = 0  # type: int
        tick_frequency = 1  # type: int

        if len(tick_positions) > self.max_number_of_ticks:
            tick_start = 1  # starting from second tick, because the first one is blank
            tick_frequency = int(
                np.ceil(1.0 * len(tick_positions) / self.max_number_of_ticks))
            assert tick_frequency > 0, (tick_frequency, len(tick_positions),
                                        self.max_number_of_ticks)

        ax.set_xticks(ticks=tick_positions[tick_start::tick_frequency])
        ax.set_yticks(ticks=tick_positions[tick_start::tick_frequency])

        if tick_frequency >= 2:
            ax.xaxis.set_minor_locator(locator=mpl_ticker.MultipleLocator(
                np.floor(tick_frequency / 2.0)))
            ax.yaxis.set_minor_locator(locator=mpl_ticker.MultipleLocator(
                np.floor(tick_frequency / 2.0)))
        else:
            ax.xaxis.set_minor_locator(locator=mpl_ticker.NullLocator())
            ax.yaxis.set_minor_locator(locator=mpl_ticker.NullLocator())

        x_labels = [
            self.get_tick_str(tick_pos=tick_pos)
            for tick_pos in ax.get_xticks()
        ]  # type: List[str]
        y_labels = [
            self.get_tick_str(tick_pos=tick_pos)
            for tick_pos in ax.get_yticks()
        ]  # type: List[str]
        ax.set_xticklabels(labels=x_labels, rotation=-45, ha="left")
        ax.set_yticklabels(labels=y_labels)
    def _set_2d_axis_tick_labels(ax: AxesType, binning: Binning) -> None:
        x_tick_positions = np.arange(binning.num_bins[0] +
                                     1) - 0.5  # type: np.array
        y_tick_positions = np.arange(binning.num_bins[1] +
                                     1) - 0.5  # type: np.array
        x_tick_labels = np.array(binning.bin_edges[0])  # type: np.array
        y_tick_labels = np.array(binning.bin_edges[1])  # type: np.array

        ax.set_xticks(ticks=x_tick_positions)
        ax.set_yticks(ticks=y_tick_positions)

        ax.xaxis.set_minor_locator(locator=mpl_ticker.NullLocator())
        ax.yaxis.set_minor_locator(locator=mpl_ticker.NullLocator())

        x_labels = [f"{la:.2f}" for la in x_tick_labels]  # type: List[str]
        y_labels = [f"{la:.2f}"
                    for la in y_tick_labels[::-1]]  # type: List[str]
        ax.set_xticklabels(labels=x_labels, rotation=-45, ha="left")
        ax.set_yticklabels(labels=y_labels)
Ejemplo n.º 4
0
    def add_residual_ratio_plot(
            self,
            axis: AxesType,
            ratio_type: str,
            data_bin_count: np.ndarray,
            mc_bin_count: np.ndarray,
            mc_error_sq: np.ndarray,
            markers_with_width: bool = True,
            systematics_are_included: bool = False,
            marker_color: str = plot_style.KITColors.kit_black,
            include_outlier_info: bool = False,
            plot_outlier_indicators: bool = False) -> None:
        if ratio_type.lower() == "normal":
            axis.set_ylabel(r"$\frac{\mathrm{Data - MC}}{\mathrm{Data}}$")
        elif ratio_type.lower() == "vs_uncert":
            if systematics_are_included:
                axis.set_ylabel(
                    r"$\frac{\mathrm{Data - MC}}{\sigma_\mathrm{stat + sys}^\mathrm{Data - MC}}$"
                )
            else:
                axis.set_ylabel(
                    r"$\frac{\mathrm{Data - MC}}{\sigma_\mathrm{stat}^\mathrm{Data - MC}}$"
                )
        else:
            raise ValueError(
                f"The provided ratio_type '{ratio_type}' is not valid!\n"
                f"The ratio_type must be one of {DataMCHistogramPlot.valid_ratio_types}!"
            )

        try:
            uh_data = unp.uarray(data_bin_count, np.sqrt(data_bin_count))
            uh_mc = unp.uarray(mc_bin_count, np.sqrt(mc_error_sq))

            if ratio_type.lower() == "normal":
                divisor = copy.deepcopy(uh_data)
            elif ratio_type.lower() == "vs_uncert":
                divisor = unp.uarray(unp.std_devs(uh_data - uh_mc), 0.)
            else:
                divisor = None

            divisor[divisor == 0] = ufloat(0.01, 0.1)
            ratio = (uh_data - uh_mc) / divisor
            ratio[(uh_data == 0.) & (uh_mc == 0.)] = ufloat(0., 0.)

            if ratio_type.lower() == "normal":
                ratio[np.logical_xor((uh_data == 0.),
                                     (uh_mc == 0.))] = ufloat(-99, 0.)
                max_val = 1.
                axis.set_ylim(bottom=-1. * max_val, top=1. * max_val)
            elif ratio_type.lower() == "vs_uncert":
                max_val_mask = (uh_data != 0.) & (uh_mc != 0.) & (
                    (uh_data - uh_mc) != 0)
                max_val = np.around(max(
                    abs(
                        np.min(
                            unp.nominal_values(ratio[max_val_mask]) -
                            unp.std_devs(ratio[max_val_mask]))),
                    abs(
                        np.max(
                            unp.nominal_values(ratio[max_val_mask]) +
                            unp.std_devs(ratio[max_val_mask])))),
                                    decimals=1)
                assert isinstance(max_val, float), (type(max_val), max_val)
                axis.set_ylim(bottom=-1. * max_val, top=max_val)
            else:
                max_val = None

            axis.axhline(y=0, color=plot_style.KITColors.dark_grey, alpha=0.8)
            axis.errorbar(self.bin_mids,
                          unp.nominal_values(ratio),
                          yerr=unp.std_devs(ratio),
                          xerr=self.bin_widths /
                          2 if markers_with_width else None,
                          ls="",
                          marker=".",
                          color=marker_color)

            if not include_outlier_info:
                return

            for bin_mid, r_val, mc_val, data_val in zip(
                    self.bin_mids, ratio, uh_mc, uh_data):
                if mc_val == 0. and (
                    (data_val != 0. and ratio_type.lower() != "vs_uncert") or
                    (abs(r_val) > max_val
                     and ratio_type.lower() == "vs_uncert")):
                    axis.text(x=bin_mid,
                              y=+0.1 * max_val,
                              s="No MC",
                              fontsize=5,
                              rotation=90,
                              ha="center",
                              va="bottom")
                    axis.text(x=bin_mid,
                              y=+0.1 * max_val,
                              s=f"#Data={int(unp.nominal_values(data_val))}",
                              fontsize=5,
                              rotation=90,
                              ha="center",
                              va="bottom")
                elif data_val == 0. and (
                    (mc_val != 0. and ratio_type.lower() != "vs_uncert") or
                    (abs(r_val) > max_val
                     and ratio_type.lower() == "vs_uncert")):
                    axis.text(x=bin_mid,
                              y=+0.1 * max_val,
                              s=f"#MC={unp.nominal_values(mc_val):.0f}",
                              fontsize=5,
                              rotation=90,
                              ha="center",
                              va="bottom")
                    axis.text(x=bin_mid,
                              y=-0.1 * max_val,
                              s="No Data",
                              fontsize=5,
                              rotation=90,
                              ha="center",
                              va="top")
                elif r_val > 1.0 and plot_outlier_indicators:
                    axis.text(x=bin_mid,
                              y=+0.08 * max_val,
                              s=f"{unp.nominal_values(r_val):3.2f}" +
                              r"$\rightarrow$",
                              fontsize=5,
                              rotation=90,
                              ha="right",
                              va="bottom")
                elif r_val < -1.0 and plot_outlier_indicators:
                    axis.text(x=bin_mid,
                              y=-0.08 * max_val,
                              s=r"$\leftarrow$" +
                              f"{unp.nominal_values(r_val):3.2f}",
                              fontsize=5,
                              rotation=90,
                              ha="right",
                              va="top")
                else:
                    pass

        except ZeroDivisionError:
            axis.text(x=self.bin_mids[int(np.ceil(len(self.bin_mids) / 2.))],
                      y=0.1,
                      s="DataMCHistogramPlot: ZeroDivisionError occurred!",
                      fontsize=8,
                      ha="center",
                      va="bottom")
            axis.axhline(y=0, color=plot_style.KITColors.dark_grey, alpha=0.8)
    def plot_on(
        self,
        ax1: AxesType,
        include_sys: bool = False,
        markers_with_width: bool = True,
        sum_color: str = plot_style.KITColors.kit_purple,
        draw_legend: bool = True,
        legend_inside: bool = True,
        legend_cols: Optional[int] = None,
        legend_loc: Optional[Union[int, str]] = None,
        y_scale: float = 1.1,
    ) -> None:
        self._check_required_histograms()

        bin_scaling = self.binning.get_bin_scaling()  # type: np.ndarray

        template_bin_counts = self._histograms[self.hist_key].get_bin_counts(
            factor=bin_scaling)
        assert isinstance(
            template_bin_counts,
            list) and len(template_bin_counts) == 1, template_bin_counts

        mc_sum_bin_error_sq = self._histograms[
            self.hist_key].get_statistical_uncertainty_per_bin()

        bar_bottom = template_bin_counts[0] - np.sqrt(mc_sum_bin_error_sq)
        height_corr = np.where(bar_bottom < 0.0, bar_bottom, 0.0)
        bar_bottom[bar_bottom < 0.0] = 0.0
        bar_height = 2 * np.sqrt(mc_sum_bin_error_sq) - height_corr

        ax1.hist(
            x=[
                self.bin_mids for _ in range(self._histograms[
                    self.hist_key].number_of_components)
            ],
            bins=self.bin_edges,
            weights=template_bin_counts,
            stacked=True,
            edgecolor="black",
            lw=0.3,
            color=self._histograms[self.hist_key].colors,
            label=self._histograms[self.hist_key].
            labels,  # type: ignore  # The type here is correct!
            histtype="stepfilled",
        )

        ax1.bar(
            x=self.bin_mids,
            height=bar_height,
            width=self.bin_widths,
            bottom=bar_bottom,
            color="black",
            hatch="///////",
            fill=False,
            lw=0,
            label="MC stat. unc."
            if not include_sys else "MC stat. + sys. unc.",
        )

        if draw_legend:
            self.draw_legend(
                axis=ax1,
                inside=legend_inside,
                loc=legend_loc,
                ncols=legend_cols,
                font_size="smaller",
                y_axis_scale=y_scale,
            )

        ax1.set_ylabel(self._get_y_label(normed=False), plot_style.ylabel_pos)
        ax1.set_xlabel(self._variable.x_label, plot_style.xlabel_pos)
    def plot_on(
        self,
        ax1: AxesType,
        style: str = "stacked",
        include_sys: bool = False,
        markers_with_width: bool = True,
        sum_color: str = plot_style.KITColors.kit_purple,
        draw_legend: bool = True,
        legend_inside: bool = True,
        legend_cols: Optional[int] = None,
        legend_loc: Optional[Union[int, str]] = None,
        y_scale: float = 1.1,
    ) -> None:
        self._check_required_histograms()

        bin_scaling = self.binning.get_bin_scaling()  # type: np.ndarray

        data_bin_count = self._histograms[self.data_key].get_bin_count_of_component(index=0)
        data_bin_errors_sq = self._histograms[self.data_key].get_histogram_squared_bin_errors_of_component(index=0)

        mc_bin_counts = self._histograms[self.mc_key].get_bin_counts(factor=bin_scaling)
        # clean_mc_bin_counts = [np.where(bc < 0., 0., bc) for bc in mc_bin_counts]

        mc_sum_bin_count = np.sum(np.array(mc_bin_counts), axis=0)
        mc_sum_bin_error_sq = self._histograms[self.mc_key].get_statistical_uncertainty_per_bin()

        bar_bottom = mc_sum_bin_count - np.sqrt(mc_sum_bin_error_sq)
        height_corr = np.where(bar_bottom < 0.0, bar_bottom, 0.0)
        bar_bottom[bar_bottom < 0.0] = 0.0
        bar_height = 2 * np.sqrt(mc_sum_bin_error_sq) - height_corr

        if style.lower() == "stacked":
            ax1.hist(
                x=[self.bin_mids for _ in range(self._histograms[self.mc_key].number_of_components)],
                bins=self.bin_edges,
                weights=mc_bin_counts,
                stacked=True,
                edgecolor="black",
                lw=0.3,
                color=self._histograms[self.mc_key].colors,
                label=self._histograms[self.mc_key].labels,  # type: ignore  # The type here is correct!
                histtype="stepfilled",
            )

            ax1.bar(
                x=self.bin_mids,
                height=bar_height,
                width=self.bin_widths,
                bottom=bar_bottom,
                color="black",
                hatch="///////",
                fill=False,
                lw=0,
                label="MC stat. unc." if not include_sys else "MC stat. + sys. unc.",
            )
        elif style.lower() == "summed":
            ax1.bar(
                x=self.bin_mids,
                height=bar_height,
                width=self.bin_widths,
                bottom=bar_bottom,
                color=sum_color,
                lw=0,
                label="MC stat. unc." if not include_sys else "MC stat. + sys. unc.",
            )
        else:
            raise RuntimeError(f"Invalid style '{style.lower()}!'\n style must be one of {self.valid_styles}!")

        ax1.errorbar(
            x=self.bin_mids,
            y=data_bin_count * bin_scaling,
            yerr=np.sqrt(data_bin_errors_sq),
            xerr=self.bin_widths / 2 if markers_with_width else None,
            ls="",
            marker=".",
            color="black",
            label=self._histograms[self.data_key].labels[0],
        )

        if draw_legend:
            if style == "stacked":
                self.draw_legend(
                    axis=ax1,
                    inside=legend_inside,
                    loc=legend_loc,
                    ncols=legend_cols,
                    font_size="smaller",
                    y_axis_scale=y_scale,
                )
            else:
                self.draw_legend(
                    axis=ax1,
                    inside=legend_inside,
                    loc=legend_loc,
                    ncols=legend_cols,
                    y_axis_scale=y_scale,
                )

        ax1.set_ylabel(self._get_y_label(normed=False), plot_style.ylabel_pos)
        ax1.set_xlabel(self._variable.x_label, plot_style.xlabel_pos)