def build(self, show_digits=2, show_bin_xy=False, add_totals=True):
        """Build the binning table.

        Parameters
        ----------
        show_digits : int, optional (default=2)
            The number of significant digits of the bin column.

        show_bin_xy: bool (default=False)
            Whether to show a single bin column with x and y.

        add_totals : bool (default=True)
            Whether to add a last row with totals.

        Returns
        -------
        binning_table : pandas.DataFrame
        """
        _check_build_parameters(show_digits, add_totals)

        if not isinstance(show_bin_xy, bool):
            raise TypeError("show_bin_xy must be a boolean; got {}."
                            .format(show_bin_xy))

        n_nonevent = self.n_nonevent
        n_event = self.n_event

        n_records = n_event + n_nonevent
        t_n_nonevent = n_nonevent.sum()
        t_n_event = n_event.sum()
        t_n_records = t_n_nonevent + t_n_event
        t_event_rate = t_n_event / t_n_records

        p_records = n_records / t_n_records
        p_event = n_event / t_n_event
        p_nonevent = n_nonevent / t_n_nonevent

        mask = (n_event > 0) & (n_nonevent > 0)
        event_rate = np.zeros(len(n_records))
        woe = np.zeros(len(n_records))
        iv = np.zeros(len(n_records))
        js = np.zeros(len(n_records))

        # Compute weight of evidence and event rate
        event_rate[mask] = n_event[mask] / n_records[mask]
        constant = np.log(t_n_event / t_n_nonevent)
        woe[mask] = np.log(1 / event_rate[mask] - 1) + constant
        W = np.log(1 / self.D - 1) + constant

        # Compute Gini
        self._gini = gini(self.n_event, self.n_nonevent)

        # Compute divergence measures
        p_ev = p_event[mask]
        p_nev = p_nonevent[mask]

        iv[mask] = jeffrey(p_ev, p_nev, return_sum=False)
        js[mask] = jensen_shannon(p_ev, p_nev, return_sum=False)
        t_iv = iv.sum()
        t_js = js.sum()

        self._iv = t_iv
        self._js = t_js
        self._hellinger = hellinger(p_ev, p_nev, return_sum=True)
        self._triangular = triangular(p_ev, p_nev, return_sum=True)

        # Keep data for plotting
        self._n_records = n_records
        self._event_rate = event_rate
        self._woe = woe
        self._W = W

        # Compute KS
        self._ks = np.abs(p_event.cumsum() - p_nonevent.cumsum()).max()

        # Compute HHI
        self._hhi = hhi(p_records)
        self._hhi_norm = hhi(p_records, normalized=True)

        # Compute paths. This is required for both plot and analysis
        self._paths_x, self._paths_y = get_paths(self.m, self.n, self.P)

        if show_bin_xy:
            bin_xy_str = bin_xy_str_format(
                self.dtype_x, self.dtype_y, self.splits_x, self.splits_y,
                show_digits, self.categories_x, self.categories_y)

            bin_xy_str.extend(["Special", "Missing"])

            df = pd.DataFrame({
                "Bin": bin_xy_str,
                "Count": n_records,
                "Count (%)": p_records,
                "Non-event": n_nonevent,
                "Event": n_event,
                "Event rate": event_rate,
                "WoE": woe,
                "IV": iv,
                "JS": js
                })
        else:
            bin_x_str = bin_str_format(
                self.dtype_x, self.splits_x, show_digits, self.categories_x)
            bin_y_str = bin_str_format(
                self.dtype_y, self.splits_y, show_digits, self.categories_y)

            bin_x_str.extend(["Special", "Missing"])
            bin_y_str.extend(["Special", "Missing"])

            df = pd.DataFrame({
                "Bin x": bin_x_str,
                "Bin y": bin_y_str,
                "Count": n_records,
                "Count (%)": p_records,
                "Non-event": n_nonevent,
                "Event": n_event,
                "Event rate": event_rate,
                "WoE": woe,
                "IV": iv,
                "JS": js
                })

        if add_totals:
            if show_bin_xy:
                totals = ["", t_n_records, 1, t_n_nonevent, t_n_event,
                          t_event_rate, "", t_iv, t_js]
            else:
                totals = ["", "", t_n_records, 1, t_n_nonevent, t_n_event,
                          t_event_rate, "", t_iv, t_js]

            df.loc["Totals"] = totals

        self._is_built = True

        return df
Build the binning table.

        Parameters
        ----------
        show_digits : int, optional (default=2)
            The number of significant digits of the bin column.

        show_bin_xy: bool (default=False)
            Whether to show a single bin column with x and y.

        add_totals : bool (default=True)
            Whether to add a last row with totals.

        Returns
        -------
        binning_table : pandas.DataFrame
        
optbinning/binning/multidimensional/binning_statistics_2d.py