def analysis(self, print_output=True):
        r"""Binning table analysis.

        Statistical analysis of the binning table, computing the Information
        Value (IV) and Herfindahl-Hirschman Index (HHI).

        Parameters
        ----------
        print_output : bool (default=True)
            Whether to print analysis information.

        Notes
        -----
        The IV for a continuous target is computed as follows:

        .. math::

            IV = \sum_{i=1}^n |U_i - \mu| \frac{r_i}{r_T},

        where :math:`U_i` is the target mean value for each bin, :math:`\mu` is
        the total target mean, :math:`r_i` is the number of records for each
        bin, and :math:`r_T` is the total number of records.
        """
        _check_is_built(self)

        # Significance tests
        n_bins = len(self.n_records)
        n_metric = n_bins - 1 - self._n_specials

        if len(self.cat_others):
            n_metric -= 1

        n_records = self.n_records[:n_metric]
        mean = self._mean[:n_metric]
        std = self.stds[:n_metric]

        t_statistics = []
        p_values = []

        for i in range(n_metric-1):
            u, u2 = mean[i], mean[i+1]
            s, s2 = std[i], std[i+1]
            r, r2 = n_records[i], n_records[i+1]

            t_statistic, p_value = stats.ttest_ind_from_stats(
                u, s, r, u2, s2, r2, False)

            t_statistics.append(t_statistic)
            p_values.append(p_value)

        df_tests = pd.DataFrame({
                "Bin A": np.arange(n_metric-1),
                "Bin B": np.arange(n_metric-1) + 1,
                "t-statistic": t_statistics,
                "p-value": p_values
            })

        tab = 4
        if len(df_tests):
            df_tests_string = dataframe_to_string(df_tests, tab)
        else:
            df_tests_string = " " * tab + "None"

        # Quality score
        if self._t_mean == 0:
            rwoe = self._woe
        else:
            rwoe = self._woe / abs(self._t_mean)

        self._quality_score = continuous_binning_quality_score(
            rwoe, p_values, self._hhi_norm)

        # Monotonic trend
        type_mono = type_of_monotonic_trend(self._mean[:-2])

        report = (
            "-------------------------------------------------\n"
            "OptimalBinning: Continuous Binning Table Analysis\n"
            "-------------------------------------------------\n"
            "\n"
            "  General metrics"
            "\n\n"
            "    IV                  {:>15.8f}\n"
            "    WoE                 {:>15.8f}\n"
            "    WoE (normalized)    {:>15.8f}\n"
            "    HHI                 {:>15.8f}\n"
            "    HHI (normalized)    {:>15.8f}\n"
            "    Quality score       {:>15.8f}\n"
            "\n"
            "  Monotonic trend       {:>15}\n"
            "\n"
            "  Significance tests\n\n{}\n"
            ).format(self._iv, self._woe, rwoe, self._hhi, self._hhi_norm,
                     self._quality_score, type_mono, df_tests_string)

        if print_output:
            print(report)

        self._is_analyzed = True
Binning table analysis.

        Statistical analysis of the binning table, computing the Information
        Value (IV) and Herfindahl-Hirschman Index (HHI).

        Parameters
        ----------
        print_output : bool (default=True)
            Whether to print analysis information.

        Notes
        -----
        The IV for a continuous target is computed as follows:

        .. math::

            IV = \sum_{i=1}^n |U_i - \mu| \frac{r_i}{r_T},

        where :math:`U_i` is the target mean value for each bin, :math:`\mu` is
        the total target mean, :math:`r_i` is the number of records for each
        bin, and :math:`r_T` is the total number of records.
        
optbinning/binning/binning_statistics.py