def plot(self, metric="woe", add_special=True, add_missing=True,
             style="bin", show_bin_labels=False, savefig=None, figsize=None,
             save_kwargs=None):
        """Plot the binning table.

        Visualize the non-event and event count, and the Weight of Evidence or
        the event rate for each bin.

        Parameters
        ----------
        metric : str, optional (default="woe")
            Supported metrics are "woe" to show the Weight of Evidence (WoE)
            measure and "event_rate" to show the event rate.

        add_special : bool (default=True)
            Whether to add the special codes bin.

        add_missing : bool (default=True)
            Whether to add the special values bin.

        style : str, optional (default="bin")
            Plot style. style="bin" shows the standard binning plot. If
            style="actual", show the plot with the actual scale, i.e, actual
            bin widths.

        show_bin_labels : bool (default=False)
            Whether to show the bin label instead of the bin id on the x-axis.
            For long labels (length > 27), labels are truncated.

            .. versionadded:: 0.15.1

        savefig : str or None (default=None)
            Path to save the plot figure.

        figsize : tuple or None (default=None)
            Size of the plot.

        save_kwargs : dict or None (default=None)
            Additional keyword arguments to be passed to `plt.savefig`.
        """
        _check_is_built(self)

        if metric not in ("event_rate", "woe", "iv"):
            raise ValueError('Invalid value for metric. Allowed string '
                             'values are "event_rate", "woe" and "iv".')

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

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

        if style not in ("bin", "actual"):
            raise ValueError('Invalid value for style. Allowed string '
                             'values are "bin" and "actual".')

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

        if show_bin_labels and style == "actual":
            raise ValueError('show_bin_labels only supported when '
                             'style="actual".')

        if figsize is not None:
            if not isinstance(figsize, tuple):
                raise TypeError("figsize argument must be a tuple.")

        if style == "actual":
            # Hide special and missing bin
            add_special = False
            add_missing = False

            if self.dtype == "categorical":
                raise ValueError('If style="actual", dtype must be numerical.')

            elif self.min_x is None or self.max_x is None:
                raise ValueError('If style="actual", min_x and max_x must be '
                                 'provided.')

        if metric == "woe":
            metric_values = self._woe
            metric_label = "WoE"
        elif metric == "event_rate":
            metric_values = self._event_rate
            metric_label = "Event rate"
        elif metric == "iv":
            metric_values = self._iv_values
            metric_label = "IV"

        fig, ax1 = plt.subplots(figsize=figsize)

        if style == "bin":
            n_bins = len(self._n_records)
            n_metric = n_bins - 1 - self._n_specials

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

            _n_event = list(self.n_event)
            _n_nonevent = list(self.n_nonevent)

            if not add_special:
                n_bins -= self._n_specials
                for _ in range(self._n_specials):
                    _n_event.pop(-2)
                    _n_nonevent.pop(-2)

            if not add_missing:
                _n_event.pop(-1)
                _n_nonevent.pop(-1)
                n_bins -= 1

            p2 = ax1.bar(range(n_bins), _n_event, color="tab:red")
            p1 = ax1.bar(range(n_bins), _n_nonevent, color="tab:blue",
                         bottom=_n_event)

            handles = [p1[0], p2[0]]
            labels = ['Non-event', 'Event']

            ax1.set_xlabel("Bin ID", fontsize=12)
            ax1.set_ylabel("Bin count", fontsize=13)

            ax2 = ax1.twinx()

            ax2.plot(range(n_metric), metric_values[:n_metric],
                     linestyle="solid", marker="o", color="black")

            # Positions special and missing bars
            pos_special = 0
            pos_missing = 0

            if add_special:
                pos_special = n_metric
                if add_missing:
                    pos_missing = n_metric + self._n_specials
            elif add_missing:
                pos_missing = n_metric

            # Add points for others (optional), special and missing bin
            if len(self.cat_others):
                pos_others = n_metric
                pos_special += 1
                pos_missing += 1

                p1[pos_others].set_alpha(0.5)
                p2[pos_others].set_alpha(0.5)

                ax2.plot(pos_others, metric_values[pos_others], marker="o",
                         color="black")

            if add_special:
                for i in range(self._n_specials):
                    p1[pos_special + i].set_hatch("/")
                    p2[pos_special + i].set_hatch("/")

                handle_special = mpatches.Patch(hatch="/", alpha=0.1)
                label_special = "Bin special"

                for s in range(self._n_specials):
                    ax2.plot(pos_special+s, metric_values[pos_special+s],
                             marker="o", color="black")

            if add_missing:
                p1[pos_missing].set_hatch("\\")
                p2[pos_missing].set_hatch("\\")
                handle_missing = mpatches.Patch(hatch="\\", alpha=0.1)
                label_missing = "Bin missing"

                ax2.plot(pos_missing, metric_values[pos_missing], marker="o",
                         color="black")

            if add_special and add_missing:
                handles.extend([handle_special, handle_missing])
                labels.extend([label_special, label_missing])
            elif add_special:
                handles.extend([handle_special])
                labels.extend([label_special])
            elif add_missing:
                handles.extend([handle_missing])
                labels.extend([label_missing])

            ax2.set_ylabel(metric_label, fontsize=13)
            ax2.xaxis.set_major_locator(mtick.MultipleLocator(1))

            if show_bin_labels:
                if self.dtype == "categorical":
                    bin_str = _bin_str_label_format(self._bin_str)
                else:
                    bin_str = self._bin_str

                if not add_special:
                    bin_str = bin_str[:-2] + [bin_str[-1]]

                if not add_missing:
                    bin_str = bin_str[:-1]

                ax1.set_xlabel("Bin", fontsize=12)
                ax1.set_xticks(np.arange(len(bin_str)))
                ax1.set_xticklabels(bin_str, rotation=45, ha="right")

        elif style == "actual":
            _n_nonevent = self.n_nonevent[:-(self._n_specials + 1)]
            _n_event = self.n_event[:-(self._n_specials + 1)]

            n_splits = len(self.splits)

            y_pos = np.empty(n_splits + 2)
            y_pos[0] = self.min_x
            y_pos[1:-1] = self.splits
            y_pos[-1] = self.max_x

            width = y_pos[1:] - y_pos[:-1]
            y_pos2 = y_pos[:-1]

            p2 = ax1.bar(y_pos2, _n_event, width, color="tab:red",
                         align="edge")
            p1 = ax1.bar(y_pos2, _n_nonevent, width, color="tab:blue",
                         bottom=_n_event, align="edge")

            handles = [p1[0], p2[0]]
            labels = ['Non-event', 'Event']

            ax1.set_xlabel("x", fontsize=12)
            ax1.set_ylabel("Bin count", fontsize=13)

            ax2 = ax1.twinx()

            for i in range(n_splits + 1):
                ax2.plot([y_pos[i], y_pos[i+1]], [metric_values[i]] * 2,
                         linestyle="solid", color="black")

            ax2.plot(width / 2 + y_pos2,
                     metric_values[:-(self._n_specials + 1)],
                     linewidth=0.75, marker="o", color="black")

            for split in self.splits:
                ax2.axvline(x=split, color="black", linestyle="--",
                            linewidth=0.9)

            ax2.set_ylabel(metric_label, fontsize=13)

        plt.title(self.name, fontsize=14)

        if show_bin_labels:
            legend_high = max(map(len, bin_str)) / 70 + 0.2
            plt.legend(handles, labels, loc="upper center",
                       bbox_to_anchor=(0.5, -legend_high), ncol=2, fontsize=12)
        else:
            plt.legend(handles, labels, loc="upper center",
                       bbox_to_anchor=(0.5, -0.2), ncol=2, fontsize=12)

        if savefig is None:
            plt.show()
        else:
            if not isinstance(savefig, str):
                raise TypeError("savefig must be a string path; got {}."
                                .format(savefig))
            if save_kwargs is None:
                save_kwargs = {}
            else:
                if not isinstance(save_kwargs, dict):
                    raise TypeError("save_kwargs must be a dictionary; got {}."
                                    .format(save_kwargs))
            plt.savefig(savefig, **save_kwargs)
            plt.close()
Plot the binning table.

        Visualize the non-event and event count, and the Weight of Evidence or
        the event rate for each bin.

        Parameters
        ----------
        metric : str, optional (default="woe")
            Supported metrics are "woe" to show the Weight of Evidence (WoE)
            measure and "event_rate" to show the event rate.

        add_special : bool (default=True)
            Whether to add the special codes bin.

        add_missing : bool (default=True)
            Whether to add the special values bin.

        style : str, optional (default="bin")
            Plot style. style="bin" shows the standard binning plot. If
            style="actual", show the plot with the actual scale, i.e, actual
            bin widths.

        show_bin_labels : bool (default=False)
            Whether to show the bin label instead of the bin id on the x-axis.
            For long labels (length > 27), labels are truncated.

            .. versionadded:: 0.15.1

        savefig : str or None (default=None)
            Path to save the plot figure.

        figsize : tuple or None (default=None)
            Size of the plot.

        save_kwargs : dict or None (default=None)
            Additional keyword arguments to be passed to `plt.savefig`.
        
optbinning/binning/binning_statistics.py