Expand source code
from .mdlp import BRLDiscretizer
from .simple import SimpleDiscretizer
from .discretizer import BasicDiscretizer, ExtraBasicDiscretizer, RFDiscretizer

# re-exported for callers; listed so the intent is explicit
__all__ = [
    "BRLDiscretizer", "BasicDiscretizer", "ExtraBasicDiscretizer",
    "RFDiscretizer", "SimpleDiscretizer",
]

Sub-modules

imodels.discretization.discretizer
imodels.discretization.mdlp

Discretization MDLP Python implementation of Fayyad and Irani's MDLP criterion discretization algorithm …

imodels.discretization.simple

Classes

class BRLDiscretizer (feature_labels, verbose=False)
Expand source code
class BRLDiscretizer:

    def __init__(self, feature_labels, verbose=False):
        self.feature_labels_original = feature_labels
        self.verbose = verbose

    def fit(self, X, y, undiscretized_features=[]):

        # check which features are numeric (to be discretized)
        self.discretized_features = []

        # _encode_strings indexes positionally, so a DataFrame has to be
        # unwrapped here the same way transform does
        if isinstance(X, (pd.DataFrame, pd.Series)):
            X = X.values
        X_str_disc = self._encode_strings(X)

        for fi in range(X_str_disc.shape[1]):
            # if not string, has values other than 0 and 1, and not specified as undiscretized
            if (
                    isinstance(X_str_disc[0][fi], numbers.Number)
                    and (not set(np.unique(X_str_disc[:, fi])).issubset({0, 1}))
                    and (len(self.feature_labels) == 0 or
                         len(undiscretized_features) == 0 or
                         self.feature_labels[fi] not in undiscretized_features
            )
            ):
                self.discretized_features.append(self.feature_labels[fi])

        if len(self.discretized_features) > 0:
            if self.verbose:
                print(
                    "Warning: non-categorical data found. Trying to discretize. (Please convert categorical values to "
                    "strings, and/or specify the argument 'undiscretized_features', to avoid this.)")
            X_str_and_num_disc = self.discretize(X_str_disc, y)

            self.discretized_X = X_str_and_num_disc
        else:
            self.discretizer = None
        return self

    def fit_transform(self, X, y, undiscretized_features=[], return_onehot=True):
        return self.fit(X, y, undiscretized_features).transform(
            X, return_onehot=return_onehot)

    def discretize(self, X, y):
        '''Discretize the features specified in self.discretized_features
        '''
        if self.verbose:
            print("Discretizing ", self.discretized_features, "...")
        D = pd.DataFrame(np.hstack((X, np.expand_dims(y, axis=1))), columns=list(self.feature_labels) + ["y"])
        self.discretizer = MDLPDiscretizer(dataset=D, class_label="y", features=self.discretized_features)

        # object dtype: the columns are filled with bin-label strings, which
        # pandas refuses to write into the float frame np.zeros_like would give
        cat_data = pd.DataFrame(np.zeros(np.shape(X), dtype=object))
        for i in range(len(self.feature_labels)):
            label = self.feature_labels[i]
            if label in self.discretized_features:
                new_column = label + " : " + self.discretizer._data[label].astype(str)
                cat_data.iloc[:, i] = new_column
            else:
                cat_data.iloc[:, i] = D[label]

        return np.array(cat_data).tolist()

    def _encode_strings(self, X):
        # handle string data
        X_str_disc = pd.DataFrame([])
        for fi in range(X.shape[1]):
            if issubclass(type(X[0][fi]), str):
                new_columns = pd.get_dummies(X[:, fi])
                new_columns.columns = [self.feature_labels_original[fi] + '_' + value for value in new_columns.columns]
                new_columns_colon_format = new_columns.apply(lambda s: s.name + ' : ' + s.astype(str))
                X_str_disc = pd.concat([X_str_disc, new_columns_colon_format], axis=1)
            else:
                X_str_disc = pd.concat([X_str_disc, pd.Series(X[:, fi], name=self.feature_labels_original[fi])], axis=1)
        self.feature_labels = list(X_str_disc.columns)
        return X_str_disc.values

    def transform(self, X, return_onehot=True):

        if type(X) in [pd.DataFrame, pd.Series]:
            X = X.values

        if self.discretizer is None:
            return pd.DataFrame(X, columns=self.feature_labels_original)

        self.data = pd.DataFrame(self._encode_strings(X), columns=self.feature_labels)
        self._apply_cutpoints()
        D = np.array(self.data)

        # prepend feature labels
        Dl = np.copy(D).astype(str).tolist()
        for i in range(len(Dl)):
            for j in range(len(Dl[0])):
                Dl[i][j] = self.feature_labels[j] + " : " + Dl[i][j]

        if not return_onehot:
            return Dl
        else:
            return self.get_onehot_df(Dl)

    @property
    def onehot_df(self):
        return self.get_onehot_df(self.discretized_X)

    def get_onehot_df(self, discretized_X):
        '''Create readable one-hot encoded DataFrame from discretized features
        '''
        data = list(discretized_X[:])

        X_colname_removed = data.copy()
        replace_str_entries_func = lambda s: s.split(' : ')[1] if type(s) is str else s
        for i in range(len(data)):
            X_colname_removed[i] = list(map(replace_str_entries_func, X_colname_removed[i]))

        X_df_categorical = pd.DataFrame(X_colname_removed, columns=self.feature_labels)
        X_df_onehot = pd.get_dummies(X_df_categorical)
        return X_df_onehot

    @property
    def data(self):
        return self.discretizer._data

    @data.setter
    def data(self, value):
        self.discretizer._data = value

    def _apply_cutpoints(self):
        return self.discretizer._apply_cutpoints()

Instance variables

var data
Expand source code
@property
def data(self):
    return self.discretizer._data
var onehot_df
Expand source code
@property
def onehot_df(self):
    return self.get_onehot_df(self.discretized_X)

Methods

def discretize(self, X, y)

Discretize the features specified in self.discretized_features

Expand source code
def discretize(self, X, y):
    '''Discretize the features specified in self.discretized_features
    '''
    if self.verbose:
        print("Discretizing ", self.discretized_features, "...")
    D = pd.DataFrame(np.hstack((X, np.expand_dims(y, axis=1))), columns=list(self.feature_labels) + ["y"])
    self.discretizer = MDLPDiscretizer(dataset=D, class_label="y", features=self.discretized_features)

    # object dtype: the columns are filled with bin-label strings, which
    # pandas refuses to write into the float frame np.zeros_like would give
    cat_data = pd.DataFrame(np.zeros(np.shape(X), dtype=object))
    for i in range(len(self.feature_labels)):
        label = self.feature_labels[i]
        if label in self.discretized_features:
            new_column = label + " : " + self.discretizer._data[label].astype(str)
            cat_data.iloc[:, i] = new_column
        else:
            cat_data.iloc[:, i] = D[label]

    return np.array(cat_data).tolist()
def fit(self, X, y, undiscretized_features=[])
Expand source code
def fit(self, X, y, undiscretized_features=[]):

    # check which features are numeric (to be discretized)
    self.discretized_features = []

    # _encode_strings indexes positionally, so a DataFrame has to be
    # unwrapped here the same way transform does
    if isinstance(X, (pd.DataFrame, pd.Series)):
        X = X.values
    X_str_disc = self._encode_strings(X)

    for fi in range(X_str_disc.shape[1]):
        # if not string, has values other than 0 and 1, and not specified as undiscretized
        if (
                isinstance(X_str_disc[0][fi], numbers.Number)
                and (not set(np.unique(X_str_disc[:, fi])).issubset({0, 1}))
                and (len(self.feature_labels) == 0 or
                     len(undiscretized_features) == 0 or
                     self.feature_labels[fi] not in undiscretized_features
        )
        ):
            self.discretized_features.append(self.feature_labels[fi])

    if len(self.discretized_features) > 0:
        if self.verbose:
            print(
                "Warning: non-categorical data found. Trying to discretize. (Please convert categorical values to "
                "strings, and/or specify the argument 'undiscretized_features', to avoid this.)")
        X_str_and_num_disc = self.discretize(X_str_disc, y)

        self.discretized_X = X_str_and_num_disc
    else:
        self.discretizer = None
    return self
def fit_transform(self, X, y, undiscretized_features=[], return_onehot=True)
Expand source code
def fit_transform(self, X, y, undiscretized_features=[], return_onehot=True):
    return self.fit(X, y, undiscretized_features).transform(
        X, return_onehot=return_onehot)
def get_onehot_df(self, discretized_X)

Create readable one-hot encoded DataFrame from discretized features

Expand source code
def get_onehot_df(self, discretized_X):
    '''Create readable one-hot encoded DataFrame from discretized features
    '''
    data = list(discretized_X[:])

    X_colname_removed = data.copy()
    replace_str_entries_func = lambda s: s.split(' : ')[1] if type(s) is str else s
    for i in range(len(data)):
        X_colname_removed[i] = list(map(replace_str_entries_func, X_colname_removed[i]))

    X_df_categorical = pd.DataFrame(X_colname_removed, columns=self.feature_labels)
    X_df_onehot = pd.get_dummies(X_df_categorical)
    return X_df_onehot
def transform(self, X, return_onehot=True)
Expand source code
def transform(self, X, return_onehot=True):

    if type(X) in [pd.DataFrame, pd.Series]:
        X = X.values

    if self.discretizer is None:
        return pd.DataFrame(X, columns=self.feature_labels_original)

    self.data = pd.DataFrame(self._encode_strings(X), columns=self.feature_labels)
    self._apply_cutpoints()
    D = np.array(self.data)

    # prepend feature labels
    Dl = np.copy(D).astype(str).tolist()
    for i in range(len(Dl)):
        for j in range(len(Dl[0])):
            Dl[i][j] = self.feature_labels[j] + " : " + Dl[i][j]

    if not return_onehot:
        return Dl
    else:
        return self.get_onehot_df(Dl)
class BasicDiscretizer (n_bins=2, dcols=[], encode='onehot', strategy='quantile', onehot_drop='if_binary')

Discretize numeric data into bins. Provides a wrapper around KBinsDiscretizer from sklearn

Params

n_bins : int or array-like of shape (len(dcols),), default=2 Number of bins to discretize each feature into.

dcols : list of strings The names of the columns to be discretized; by default, discretize all float and int columns in X.

encode : {'onehot', 'ordinal'}, default='onehot' Method used to encode the transformed result.

onehot
    Encode the transformed result with one-hot encoding and
    return a dense array.
ordinal
    Return the bin identifier encoded as an integer value.

strategy : {'uniform', 'quantile', 'kmeans'}, default='quantile' Strategy used to define the widths of the bins.

uniform
    All bins in each feature have identical widths.
quantile
    All bins in each feature have the same number of points.
kmeans
    Values in each bin have the same nearest center of a 1D
    k-means cluster.

onehot_drop : {‘first’, ‘if_binary’} or a array-like of shape (len(dcols),), default='if_binary' Specifies a methodology to use to drop one of the categories per feature when encode = "onehot".

None
    Retain all features (the default).
‘first’
    Drop the first y_str in each feature. If only one y_str
    is present, the feature will be dropped entirely.
‘if_binary’
    Drop the first y_str in each feature with two categories.
    Features with 1 or more than 2 categories are left intact.

Attributes

discretizer_ : object of class KBinsDiscretizer()
Primary discretization method used to bin numeric data
manual_discretizer_ : dictionary
Provides bin_edges to feed into _quantile_discretization() and do quantile discretization manually for features where KBinsDiscretizer() failed. Ignored if strategy != 'quantile' or no errors in KBinsDiscretizer().
onehot_ : object of class OneHotEncoder()
One hot encoding fit. Ignored if encode != 'onehot'

Examples

Expand source code
class BasicDiscretizer(AbstractDiscretizer):
    """
    Discretize numeric data into bins. Provides a wrapper around
    KBinsDiscretizer from sklearn

    Params
    ------
    n_bins : int or array-like of shape (len(dcols),), default=2
        Number of bins to discretize each feature into.

    dcols : list of strings
        The names of the columns to be discretized; by default,
        discretize all float and int columns in X.

    encode : {'onehot', 'ordinal'}, default='onehot'
        Method used to encode the transformed result.

        onehot
            Encode the transformed result with one-hot encoding and
            return a dense array.
        ordinal
            Return the bin identifier encoded as an integer value.

    strategy : {'uniform', 'quantile', 'kmeans'}, default='quantile'
        Strategy used to define the widths of the bins.

        uniform
            All bins in each feature have identical widths.
        quantile
            All bins in each feature have the same number of points.
        kmeans
            Values in each bin have the same nearest center of a 1D
            k-means cluster.

    onehot_drop : {‘first’, ‘if_binary’} or a array-like of shape  (len(dcols),), default='if_binary'
        Specifies a methodology to use to drop one of the categories
        per feature when encode = "onehot".

        None
            Retain all features (the default).
        ‘first’
            Drop the first y_str in each feature. If only one y_str
            is present, the feature will be dropped entirely.
        ‘if_binary’
            Drop the first y_str in each feature with two categories.
            Features with 1 or more than 2 categories are left intact.

    Attributes
    ----------
    discretizer_ : object of class KBinsDiscretizer()
        Primary discretization method used to bin numeric data

    manual_discretizer_ : dictionary
        Provides bin_edges to feed into _quantile_discretization()
        and do quantile discretization manually for features where
        KBinsDiscretizer() failed. Ignored if strategy != 'quantile'
        or no errors in KBinsDiscretizer().

    onehot_ : object of class OneHotEncoder()
        One hot encoding fit. Ignored if encode != 'onehot'

    Examples
    --------
    """

    def __init__(self, n_bins=2, dcols=[],
                 encode='onehot', strategy='quantile',
                 onehot_drop='if_binary'):
        super().__init__(n_bins=n_bins, dcols=dcols,
                         encode=encode, strategy=strategy,
                         onehot_drop=onehot_drop)

    def fit(self, X, y=None):
        """
        Fit the estimator.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            (Training) data to be discretized.

        y : Ignored. This parameter exists only for compatibility with
            :class:`~sklearn.pipeline.Pipeline` and fit_transform method

        Returns
        -------
        self
        """

        # initialization and error checking
        self._fit_preprocessing(X)

        # apply KBinsDiscretizer to the selected columns
        discretizer = KBinsDiscretizer(n_bins=self.n_bins_,
                                       encode='ordinal',
                                       strategy=self.strategy)

        discretizer.fit(X[self.dcols_])
        self.discretizer_ = discretizer

        if (self.encode == 'onehot') | (self.strategy == 'quantile'):
            discretized_df = discretizer.transform(X[self.dcols_])

            discretized_df = pd.DataFrame(discretized_df,
                                          columns=self.dcols_,
                                          index=X.index).astype(int)

        # fix KBinsDiscretizer errors if any when strategy = "quantile"
        if self.strategy == "quantile":
            err_idx = np.where(discretized_df.nunique() != self.n_bins_)[0]
            self.manual_discretizer_ = dict()
            for idx in err_idx:
                col = self.dcols_[idx]
                if X[col].nunique() > 1:
                    q_values = np.linspace(0, 1, self.n_bins_[idx] + 1)
                    bin_edges = np.quantile(X[col], q_values)
                    discretized_df[col] = self._discretize_to_bins(X[col], bin_edges,
                                                                   keep_pointwise_bins=True)
                    self.manual_discretizer_[col] = bin_edges

        # fit onehot encoded X if specified
        if self.encode == "onehot":
            onehot = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
            onehot.fit(discretized_df.astype(str))
            self.onehot_ = onehot

        return self

    def transform(self, X):
        """
        Discretize the data.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            Data to be discretized.

        Returns
        -------
        X_discretized : data frame
            Data with features in dcols transformed to the
            binned space. All other features remain unchanged.
        """

        check_is_fitted(self)

        # transform using KBinsDiscretizer
        discretized_df = self.discretizer_.transform(
            X[self.dcols_]).astype(int)
        discretized_df = pd.DataFrame(discretized_df,
                                      columns=self.dcols_,
                                      index=X.index)

        # fix KBinsDiscretizer errors (if any) when strategy = "quantile"
        if self.strategy == "quantile":
            for col in self.manual_discretizer_.keys():
                bin_edges = self.manual_discretizer_[col]
                discretized_df[col] = self._discretize_to_bins(X[col], bin_edges,
                                                               keep_pointwise_bins=True)

        # return onehot encoded data if specified and
        # join discretized columns with rest of X
        X_discretized = self._transform_postprocessing(discretized_df, X)

        return X_discretized

Ancestors

  • AbstractDiscretizer
  • sklearn.base.TransformerMixin
  • sklearn.utils._set_output._SetOutputMixin
  • sklearn.base.BaseEstimator
  • sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
  • sklearn.utils._metadata_requests._MetadataRequester

Methods

def fit(self, X, y=None)

Fit the estimator.

Parameters

X : data frame of shape (n_samples, n_features)
(Training) data to be discretized.
y : Ignored. This parameter exists only for compatibility with
:class:~sklearn.pipeline.Pipeline and fit_transform method

Returns

self
 
Expand source code
def fit(self, X, y=None):
    """
    Fit the estimator.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        (Training) data to be discretized.

    y : Ignored. This parameter exists only for compatibility with
        :class:`~sklearn.pipeline.Pipeline` and fit_transform method

    Returns
    -------
    self
    """

    # initialization and error checking
    self._fit_preprocessing(X)

    # apply KBinsDiscretizer to the selected columns
    discretizer = KBinsDiscretizer(n_bins=self.n_bins_,
                                   encode='ordinal',
                                   strategy=self.strategy)

    discretizer.fit(X[self.dcols_])
    self.discretizer_ = discretizer

    if (self.encode == 'onehot') | (self.strategy == 'quantile'):
        discretized_df = discretizer.transform(X[self.dcols_])

        discretized_df = pd.DataFrame(discretized_df,
                                      columns=self.dcols_,
                                      index=X.index).astype(int)

    # fix KBinsDiscretizer errors if any when strategy = "quantile"
    if self.strategy == "quantile":
        err_idx = np.where(discretized_df.nunique() != self.n_bins_)[0]
        self.manual_discretizer_ = dict()
        for idx in err_idx:
            col = self.dcols_[idx]
            if X[col].nunique() > 1:
                q_values = np.linspace(0, 1, self.n_bins_[idx] + 1)
                bin_edges = np.quantile(X[col], q_values)
                discretized_df[col] = self._discretize_to_bins(X[col], bin_edges,
                                                               keep_pointwise_bins=True)
                self.manual_discretizer_[col] = bin_edges

    # fit onehot encoded X if specified
    if self.encode == "onehot":
        onehot = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
        onehot.fit(discretized_df.astype(str))
        self.onehot_ = onehot

    return self
def transform(self, X)

Discretize the data.

Parameters

X : data frame of shape (n_samples, n_features)
Data to be discretized.

Returns

X_discretized : data frame
Data with features in dcols transformed to the binned space. All other features remain unchanged.
Expand source code
def transform(self, X):
    """
    Discretize the data.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        Data to be discretized.

    Returns
    -------
    X_discretized : data frame
        Data with features in dcols transformed to the
        binned space. All other features remain unchanged.
    """

    check_is_fitted(self)

    # transform using KBinsDiscretizer
    discretized_df = self.discretizer_.transform(
        X[self.dcols_]).astype(int)
    discretized_df = pd.DataFrame(discretized_df,
                                  columns=self.dcols_,
                                  index=X.index)

    # fix KBinsDiscretizer errors (if any) when strategy = "quantile"
    if self.strategy == "quantile":
        for col in self.manual_discretizer_.keys():
            bin_edges = self.manual_discretizer_[col]
            discretized_df[col] = self._discretize_to_bins(X[col], bin_edges,
                                                           keep_pointwise_bins=True)

    # return onehot encoded data if specified and
    # join discretized columns with rest of X
    X_discretized = self._transform_postprocessing(discretized_df, X)

    return X_discretized
class ExtraBasicDiscretizer (dcols, n_bins=4, strategy='quantile', onehot_drop='if_binary')

Discretize provided columns into bins and return in one-hot format. Generates meaningful column names based on bin edges. Wraps KBinsDiscretizer from sklearn.

Params

dcols : list of strings The names of the columns to be discretized.

n_bins : int or array-like of shape (len(dcols),), default=4 Number of bins to discretize each feature into.

strategy : {'uniform', 'quantile', 'kmeans'}, default='quantile' Strategy used to define the widths of the bins.

uniform
    All bins in each feature have identical widths.
quantile
    All bins in each feature have the same number of points.
kmeans
    Values in each bin have the same nearest center of a 1D
    k-means cluster.

onehot_drop : {'first', 'if_binary'} or a array-like of shape (len(dcols),), default='if_binary' Specifies a methodology to use to drop one of the categories per feature when encode = "onehot".

None
    Retain all features (the default).
'first'
    Drop the first y_str in each feature. If only one y_str
    is present, the feature will be dropped entirely.
'if_binary'
    Drop the first y_str in each feature with two categories.
    Features with 1 or more than 2 categories are left intact.

Attributes

discretizer_ : object of class KBinsDiscretizer()
Primary discretization method used to bin numeric data

Examples

Expand source code
class ExtraBasicDiscretizer(TransformerMixin):
    """
    Discretize provided columns into bins and return in one-hot format. 
    Generates meaningful column names based on bin edges.
    Wraps KBinsDiscretizer from sklearn.

    Params
    ------
    dcols : list of strings
        The names of the columns to be discretized.

    n_bins : int or array-like of shape (len(dcols),), default=4
        Number of bins to discretize each feature into.

    strategy : {'uniform', 'quantile', 'kmeans'}, default='quantile'
        Strategy used to define the widths of the bins.

        uniform
            All bins in each feature have identical widths.
        quantile
            All bins in each feature have the same number of points.
        kmeans
            Values in each bin have the same nearest center of a 1D
            k-means cluster.

    onehot_drop : {'first', 'if_binary'} or a array-like of shape  (len(dcols),), default='if_binary'
        Specifies a methodology to use to drop one of the categories
        per feature when encode = "onehot".

        None
            Retain all features (the default).
        'first'
            Drop the first y_str in each feature. If only one y_str
            is present, the feature will be dropped entirely.
        'if_binary'
            Drop the first y_str in each feature with two categories.
            Features with 1 or more than 2 categories are left intact.

    Attributes
    ----------
    discretizer_ : object of class KBinsDiscretizer()
        Primary discretization method used to bin numeric data

    Examples
    --------
    """

    def __init__(self,
                 dcols,
                 n_bins=4,
                 strategy='quantile',
                 onehot_drop='if_binary'):
        self.dcols = dcols
        self.n_bins = n_bins
        self.strategy = strategy
        self.onehot_drop = onehot_drop

    def fit(self, X, y=None):
        """
        Fit the estimator.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            (Training) data to be discretized.

        y : Ignored. This parameter exists only for compatibility with
            :class:`~sklearn.pipeline.Pipeline` and fit_transform method

        Returns
        -------
        self
        """

        # Fit KBinsDiscretizer to the selected columns
        discretizer = KBinsDiscretizer(
            n_bins=self.n_bins, strategy=self.strategy, encode='ordinal')
        discretizer.fit(X[self.dcols])
        self.discretizer_ = discretizer

        # Fit OneHotEncoder to the ordinal output of KBinsDiscretizer
        disc_ordinal_np = discretizer.transform(X[self.dcols])
        disc_ordinal_df = pd.DataFrame(disc_ordinal_np, columns=self.dcols)
        disc_ordinal_df_str = disc_ordinal_df.astype(int).astype(str)

        encoder = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
        encoder.fit(disc_ordinal_df_str)
        self.encoder_ = encoder

        return self

    def transform(self, X):
        """
        Discretize the data.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            Data to be discretized.

        Returns
        -------
        X_discretized : data frame
            Data with features in dcols transformed to the
            binned space. All other features remain unchanged.
        """

        # Apply discretizer transform to get ordinally coded DF
        disc_ordinal_np = self.discretizer_.transform(X[self.dcols])
        disc_ordinal_df = pd.DataFrame(disc_ordinal_np, columns=self.dcols)
        disc_ordinal_df_str = disc_ordinal_df.astype(int).astype(str)

        # One-hot encode the ordinal DF
        disc_onehot_np = self.encoder_.transform(disc_ordinal_df_str)
        if scipy.sparse.issparse(disc_onehot_np):
            # OneHotEncoder returns a sparse matrix by default, which pandas
            # would otherwise store as a single column of sparse rows
            disc_onehot_np = disc_onehot_np.toarray()
        disc_onehot = pd.DataFrame(
            disc_onehot_np, columns=self.encoder_.get_feature_names_out(),
            index=X.index)

        # Name columns after the interval they represent (e.g. 0.1_to_0.5)
        for col, bin_edges in zip(self.dcols, self.discretizer_.bin_edges_):
            bin_edges = bin_edges.astype(str)

            # every bin learned during fit, not just the ones present in X, so
            # that transform gives the same columns whatever data it is given
            for ordinal_value in range(len(bin_edges) - 1):
                bin_lb = bin_edges[int(ordinal_value)]
                bin_ub = bin_edges[int(ordinal_value) + 1]
                interval_string = f'{bin_lb}_to_{bin_ub}'

                disc_onehot = disc_onehot.rename(
                    columns={f'{col}_{ordinal_value}': f'{col}_' + interval_string})

        # Join discretized columns with rest of X
        non_dcols = [col for col in X.columns if col not in self.dcols]
        X_discretized = pd.concat([disc_onehot, X[non_dcols]], axis=1)

        return X_discretized

Ancestors

  • sklearn.base.TransformerMixin
  • sklearn.utils._set_output._SetOutputMixin

Methods

def fit(self, X, y=None)

Fit the estimator.

Parameters

X : data frame of shape (n_samples, n_features)
(Training) data to be discretized.
y : Ignored. This parameter exists only for compatibility with
:class:~sklearn.pipeline.Pipeline and fit_transform method

Returns

self
 
Expand source code
def fit(self, X, y=None):
    """
    Fit the estimator.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        (Training) data to be discretized.

    y : Ignored. This parameter exists only for compatibility with
        :class:`~sklearn.pipeline.Pipeline` and fit_transform method

    Returns
    -------
    self
    """

    # Fit KBinsDiscretizer to the selected columns
    discretizer = KBinsDiscretizer(
        n_bins=self.n_bins, strategy=self.strategy, encode='ordinal')
    discretizer.fit(X[self.dcols])
    self.discretizer_ = discretizer

    # Fit OneHotEncoder to the ordinal output of KBinsDiscretizer
    disc_ordinal_np = discretizer.transform(X[self.dcols])
    disc_ordinal_df = pd.DataFrame(disc_ordinal_np, columns=self.dcols)
    disc_ordinal_df_str = disc_ordinal_df.astype(int).astype(str)

    encoder = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
    encoder.fit(disc_ordinal_df_str)
    self.encoder_ = encoder

    return self
def transform(self, X)

Discretize the data.

Parameters

X : data frame of shape (n_samples, n_features)
Data to be discretized.

Returns

X_discretized : data frame
Data with features in dcols transformed to the binned space. All other features remain unchanged.
Expand source code
def transform(self, X):
    """
    Discretize the data.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        Data to be discretized.

    Returns
    -------
    X_discretized : data frame
        Data with features in dcols transformed to the
        binned space. All other features remain unchanged.
    """

    # Apply discretizer transform to get ordinally coded DF
    disc_ordinal_np = self.discretizer_.transform(X[self.dcols])
    disc_ordinal_df = pd.DataFrame(disc_ordinal_np, columns=self.dcols)
    disc_ordinal_df_str = disc_ordinal_df.astype(int).astype(str)

    # One-hot encode the ordinal DF
    disc_onehot_np = self.encoder_.transform(disc_ordinal_df_str)
    if scipy.sparse.issparse(disc_onehot_np):
        # OneHotEncoder returns a sparse matrix by default, which pandas
        # would otherwise store as a single column of sparse rows
        disc_onehot_np = disc_onehot_np.toarray()
    disc_onehot = pd.DataFrame(
        disc_onehot_np, columns=self.encoder_.get_feature_names_out(),
        index=X.index)

    # Name columns after the interval they represent (e.g. 0.1_to_0.5)
    for col, bin_edges in zip(self.dcols, self.discretizer_.bin_edges_):
        bin_edges = bin_edges.astype(str)

        # every bin learned during fit, not just the ones present in X, so
        # that transform gives the same columns whatever data it is given
        for ordinal_value in range(len(bin_edges) - 1):
            bin_lb = bin_edges[int(ordinal_value)]
            bin_ub = bin_edges[int(ordinal_value) + 1]
            interval_string = f'{bin_lb}_to_{bin_ub}'

            disc_onehot = disc_onehot.rename(
                columns={f'{col}_{ordinal_value}': f'{col}_' + interval_string})

    # Join discretized columns with rest of X
    non_dcols = [col for col in X.columns if col not in self.dcols]
    X_discretized = pd.concat([disc_onehot, X[non_dcols]], axis=1)

    return X_discretized
class RFDiscretizer (rf_model=None, classification=False, n_bins=2, dcols=[], encode='onehot', strategy='quantile', backup_strategy='quantile', onehot_drop='if_binary')

Discretize numeric data into bins using RF splits.

Parameters

rf_model : RandomForestClassifer() or RandomForestRegressor()
RF model from which to extract splits for discretization. Default is RandomForestClassifer(n_estimators = 500) or RandomForestRegressor(n_estimators = 500)
classification : boolean; default=False
Used only if rf_model=None. If True, rf_model=RandomForestClassifier(n_estimators = 500). Else, rf_model=RandomForestRegressor(n_estimators = 500)
n_bins : int or array-like of shape (len(dcols),), default=2
Number of bins to discretize each feature into.
dcols : list of strings
The names of the columns to be discretized; by default, discretize all float and int columns in X.
encode : {‘onehot’, ‘ordinal’}, default=’onehot’

Method used to encode the transformed result.

onehot - Encode the transformed result with one-hot encoding and return a dense array. ordinal - Return the bin identifier encoded as an integer value.

strategy : {‘uniform’, ‘quantile’}, default=’quantile’
Strategy used to choose RF split points. uniform - RF split points chosen to be uniformly spaced out. quantile - RF split points chosen based on equally-spaced quantiles.
backup_strategy : {‘uniform’, ‘quantile’, ‘kmeans’}, default=’quantile’
Strategy used to define the widths of the bins if no rf splits exist for that feature. Used in KBinsDiscretizer. uniform All bins in each feature have identical widths. quantile All bins in each feature have the same number of points. kmeans Values in each bin have the same nearest center of a 1D k-means cluster.
onehot_drop : {‘first’, ‘if_binary’} or array-like of shape (len(dcols),), default='if_binary'
Specifies a methodology to use to drop one of the categories per feature when encode = "onehot". None Retain all features (the default). ‘first’ Drop the first y_str in each feature. If only one y_str is present, the feature will be dropped entirely. ‘if_binary’ Drop the first y_str in each feature with two categories. Features with 1 or more than 2 categories are left intact.

Attributes

rf_splits : dictionary where
key = feature name value = array of all RF split threshold values
bin_edges_ : dictionary where
key = feature name value = array of bin edges used for discretization, taken from RF split values
missing_rf_cols_ : array-like
List of features that were not used in RF
backup_discretizer_ : object of class BasicDiscretizer
Discretization method used to bin numeric data for features in missing_rf_cols_
onehot_ : object of class OneHotEncoder()
One hot encoding fit. Ignored if encode != 'onehot'
Expand source code
class RFDiscretizer(AbstractDiscretizer):
    """
    Discretize numeric data into bins using RF splits.

    Parameters
    ----------
    rf_model : RandomForestClassifer() or RandomForestRegressor()
        RF model from which to extract splits for discretization.
        Default is RandomForestClassifer(n_estimators = 500) or
        RandomForestRegressor(n_estimators = 500)

    classification : boolean; default=False
        Used only if rf_model=None. If True,
        rf_model=RandomForestClassifier(n_estimators = 500).
        Else, rf_model=RandomForestRegressor(n_estimators = 500)

    n_bins : int or array-like of shape (len(dcols),), default=2
        Number of bins to discretize each feature into.

    dcols : list of strings
        The names of the columns to be discretized; by default,
        discretize all float and int columns in X.

    encode : {‘onehot’, ‘ordinal’}, default=’onehot’
        Method used to encode the transformed result.

        onehot - Encode the transformed result with one-hot encoding and
            return a dense array.
        ordinal - Return the bin identifier encoded as an integer value.

    strategy : {‘uniform’, ‘quantile’}, default=’quantile’
        Strategy used to choose RF split points.
        uniform - RF split points chosen to be uniformly spaced out.
        quantile - RF split points chosen based on equally-spaced quantiles.

    backup_strategy : {‘uniform’, ‘quantile’, ‘kmeans’}, default=’quantile’
        Strategy used to define the widths of the bins if no rf splits exist for
        that feature. Used in KBinsDiscretizer.
        uniform
            All bins in each feature have identical widths.
        quantile
            All bins in each feature have the same number of points.
        kmeans
            Values in each bin have the same nearest center of a 1D
            k-means cluster.

    onehot_drop : {‘first’, ‘if_binary’} or array-like of shape  (len(dcols),), default='if_binary'
        Specifies a methodology to use to drop one of the categories
        per feature when encode = "onehot".
        None
            Retain all features (the default).
        ‘first’
            Drop the first y_str in each feature. If only one y_str
            is present, the feature will be dropped entirely.
        ‘if_binary’
            Drop the first y_str in each feature with two categories.
            Features with 1 or more than 2 categories are left intact.

    Attributes
    ----------
    rf_splits : dictionary where
        key = feature name
        value = array of all RF split threshold values

    bin_edges_ : dictionary where
        key = feature name
        value = array of bin edges used for discretization, taken from
            RF split values

    missing_rf_cols_ : array-like
        List of features that were not used in RF

    backup_discretizer_ : object of class BasicDiscretizer()
        Discretization method used to bin numeric data for features
        in missing_rf_cols_

    onehot_ : object of class OneHotEncoder()
        One hot encoding fit. Ignored if encode != 'onehot'

    """

    def __init__(self, rf_model=None, classification=False,
                 n_bins=2, dcols=[], encode='onehot',
                 strategy='quantile', backup_strategy='quantile',
                 onehot_drop='if_binary'):
        super().__init__(n_bins=n_bins, dcols=dcols,
                         encode=encode, strategy=strategy,
                         onehot_drop=onehot_drop)
        self.backup_strategy = backup_strategy
        self.rf_model = rf_model
        if rf_model is None:
            self.classification = classification

    def _validate_args(self):
        """
        Check if encode, strategy, backup_strategy arguments are valid.
        """
        super()._validate_args()
        valid_backup_strategy = ('uniform', 'quantile', 'kmeans')
        if (self.backup_strategy not in valid_backup_strategy):
            raise ValueError("Valid options for 'strategy' are {}. Got strategy={!r} instead."
                             .format(valid_backup_strategy, self.backup_strategy))

    def _get_rf_splits(self, col_names):
        """
        Get all splits in random forest ensemble

        Parameters
        ----------
        col_names : array-like of shape (n_features,)
            Column names for X used to train rf_model

        Returns
        -------
        rule_dict : dictionary where
            key = feature name
            value = array of all RF split threshold values
        """

        rule_dict = {}
        for model in self.rf_model.estimators_:
            tree = model.tree_
            tree_it = enumerate(zip(tree.children_left,
                                    tree.children_right,
                                    tree.feature,
                                    tree.threshold))
            for node_idx, data in tree_it:
                left, right, feature, th = data
                if (left != -1) | (right != -1):
                    feature = col_names[feature]
                    if feature in rule_dict:
                        rule_dict[feature].append(th)
                    else:
                        rule_dict[feature] = [th]
        return rule_dict

    def _fit_rf(self, X, y=None):
        """
        Fit random forest (if necessary) and obtain RF split thresholds

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            Training data used to fit RF

        y : array-like of shape (n_samples,)
            Training response vector used to fit RF

        Returns
        -------
        rf_splits : dictionary where
            key = feature name
            value = array of all RF split threshold values
        """

        # If no rf_model given, train default random forest model
        if self.rf_model is None:
            if y is None:
                raise ValueError("Must provide y if rf_model is not given.")
            if self.classification:
                self.rf_model = RandomForestClassifier(n_estimators=500)
            else:
                self.rf_model = RandomForestRegressor(n_estimators=500)
            self.rf_model.fit(X, y)

        else:
            # provided rf model has not yet been trained
            if not check_is_fitted(self.rf_model):
                if y is None:
                    raise ValueError(
                        "Must provide y if rf_model has not been trained.")
                self.rf_model.fit(X, y)

        # get all random forest split points
        self.rf_splits = self._get_rf_splits(list(X.columns))

    def reweight_n_bins(self, X, y=None, by="nsplits"):
        """
        Reallocate number of bins per feature.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            (Training) data to be discretized.

        y : array-like of shape (n_samples,)
            (Training) response vector. Required only if
            rf_model = None or rf_model has not yet been fitted

        by : {'nsplits'}, default='nsplits'
            Specifies how to reallocate number of bins per feature.

            nsplits
                Reallocate number of bins so that each feature
                in dcols get at a minimum of 2 bins with the
                remaining bins distributed proportionally to the
                number of RF splits using that feature

        Returns
        -------
        self.n_bins : array of shape (len(dcols),)
            number of bins per feature reallocated according to
            'by' argument
        """
        # initialization and error checking
        self._fit_preprocessing(X)

        # get all random forest split points
        self._fit_rf(X=X, y=y)

        # get total number of bins to reallocate
        total_bins = np.asarray(self.n_bins_).sum()

        # reweight n_bins
        if by == "nsplits":
            # each col gets at least 2 bins; remaining bins get
            # reallocated based on number of RF splits using that feature
            n_rules = np.array([len(self.rf_splits[col])
                               for col in self.dcols_])
            self.n_bins = np.round(n_rules / n_rules.sum() *
                                   (total_bins - 2 * len(self.dcols_))) + 2
        else:
            valid_by = ('nsplits')
            raise ValueError("Valid options for 'by' are {}. Got by={!r} instead."
                             .format(valid_by, by))

    def fit(self, X, y=None):
        """
        Fit the estimator.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            (Training) data to be discretized.

        y : array-like of shape (n_samples,)
            (Training) response vector. Required only if
            rf_model = None or rf_model has not yet been fitted

        Returns
        -------
        self
        """
        # initialization and error checking
        self._fit_preprocessing(X)

        # get all random forest split points
        self._fit_rf(X=X, y=y)

        # features that were not used in the rf but need to be discretized
        self.missing_rf_cols_ = list(set(self.dcols_) -
                                     set(self.rf_splits.keys()))
        if len(self.missing_rf_cols_) > 0:
            print("{} did not appear in random forest so were discretized via {} discretization"
                  .format(self.missing_rf_cols_, self.strategy))
            missing_n_bins = np.array([self.n_bins_[np.array(self.dcols_) == col][0]
                                       for col in self.missing_rf_cols_])

            backup_discretizer = BasicDiscretizer(n_bins=missing_n_bins,
                                                  dcols=self.missing_rf_cols_,
                                                  encode='ordinal',
                                                  strategy=self.backup_strategy)
            backup_discretizer.fit(X[self.missing_rf_cols_])
            self.backup_discretizer_ = backup_discretizer
        else:
            self.backup_discretizer_ = None

        if self.encode == 'onehot':
            if len(self.missing_rf_cols_) > 0:
                discretized_df = backup_discretizer.transform(
                    X[self.missing_rf_cols_])
            else:
                discretized_df = pd.DataFrame({}, index=X.index)

        # do discretization based on rf split thresholds
        self.bin_edges_ = dict()
        for col in self.dcols_:
            if col in self.rf_splits.keys():
                # .item(): numpy no longer converts a size-1 array to a scalar
                b = self.n_bins_[np.array(self.dcols_) == col].item()
                if self.strategy == "quantile":
                    q_values = np.linspace(0, 1, int(b) + 1)
                    bin_edges = np.quantile(self.rf_splits[col], q_values)
                elif self.strategy == "uniform":
                    width = (max(self.rf_splits[col]) -
                             min(self.rf_splits[col])) / b
                    bin_edges = width * \
                        np.arange(0, b + 1) + min(self.rf_splits[col])
                self.bin_edges_[col] = bin_edges
                if self.encode == 'onehot':
                    discretized_df[col] = self._discretize_to_bins(
                        X[col], bin_edges)

        # fit onehot encoded X if specified
        if self.encode == "onehot":
            onehot = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
            onehot.fit(discretized_df[self.dcols_].astype(str))
            self.onehot_ = onehot

        return self

    def transform(self, X):
        """
        Discretize the data.

        Parameters
        ----------
        X : data frame of shape (n_samples, n_features)
            Data to be discretized.

        Returns
        -------
        X_discretized : data frame
            Data with features in dcols transformed to the
            binned space. All other features remain unchanged.
        """

        check_is_fitted(self)

        # transform features that did not appear in RF
        if len(self.missing_rf_cols_) > 0:
            discretized_df = self.backup_discretizer_.transform(
                X[self.missing_rf_cols_])
            discretized_df = pd.DataFrame(discretized_df,
                                          columns=self.missing_rf_cols_,
                                          index=X.index)
        else:
            discretized_df = pd.DataFrame({}, index=X.index)

        # do discretization based on rf split thresholds
        for col in self.bin_edges_.keys():
            discretized_df[col] = self._discretize_to_bins(
                X[col], self.bin_edges_[col])

        # return onehot encoded data if specified and
        # join discretized columns with rest of X
        X_discretized = self._transform_postprocessing(discretized_df, X)

        return X_discretized

Ancestors

  • AbstractDiscretizer
  • sklearn.base.TransformerMixin
  • sklearn.utils._set_output._SetOutputMixin
  • sklearn.base.BaseEstimator
  • sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
  • sklearn.utils._metadata_requests._MetadataRequester

Methods

def fit(self, X, y=None)

Fit the estimator.

Parameters

X : data frame of shape (n_samples, n_features)
(Training) data to be discretized.
y : array-like of shape (n_samples,)
(Training) response vector. Required only if rf_model = None or rf_model has not yet been fitted

Returns

self
 
Expand source code
def fit(self, X, y=None):
    """
    Fit the estimator.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        (Training) data to be discretized.

    y : array-like of shape (n_samples,)
        (Training) response vector. Required only if
        rf_model = None or rf_model has not yet been fitted

    Returns
    -------
    self
    """
    # initialization and error checking
    self._fit_preprocessing(X)

    # get all random forest split points
    self._fit_rf(X=X, y=y)

    # features that were not used in the rf but need to be discretized
    self.missing_rf_cols_ = list(set(self.dcols_) -
                                 set(self.rf_splits.keys()))
    if len(self.missing_rf_cols_) > 0:
        print("{} did not appear in random forest so were discretized via {} discretization"
              .format(self.missing_rf_cols_, self.strategy))
        missing_n_bins = np.array([self.n_bins_[np.array(self.dcols_) == col][0]
                                   for col in self.missing_rf_cols_])

        backup_discretizer = BasicDiscretizer(n_bins=missing_n_bins,
                                              dcols=self.missing_rf_cols_,
                                              encode='ordinal',
                                              strategy=self.backup_strategy)
        backup_discretizer.fit(X[self.missing_rf_cols_])
        self.backup_discretizer_ = backup_discretizer
    else:
        self.backup_discretizer_ = None

    if self.encode == 'onehot':
        if len(self.missing_rf_cols_) > 0:
            discretized_df = backup_discretizer.transform(
                X[self.missing_rf_cols_])
        else:
            discretized_df = pd.DataFrame({}, index=X.index)

    # do discretization based on rf split thresholds
    self.bin_edges_ = dict()
    for col in self.dcols_:
        if col in self.rf_splits.keys():
            # .item(): numpy no longer converts a size-1 array to a scalar
            b = self.n_bins_[np.array(self.dcols_) == col].item()
            if self.strategy == "quantile":
                q_values = np.linspace(0, 1, int(b) + 1)
                bin_edges = np.quantile(self.rf_splits[col], q_values)
            elif self.strategy == "uniform":
                width = (max(self.rf_splits[col]) -
                         min(self.rf_splits[col])) / b
                bin_edges = width * \
                    np.arange(0, b + 1) + min(self.rf_splits[col])
            self.bin_edges_[col] = bin_edges
            if self.encode == 'onehot':
                discretized_df[col] = self._discretize_to_bins(
                    X[col], bin_edges)

    # fit onehot encoded X if specified
    if self.encode == "onehot":
        onehot = OneHotEncoder(drop=self.onehot_drop)  # , sparse=False)
        onehot.fit(discretized_df[self.dcols_].astype(str))
        self.onehot_ = onehot

    return self
def reweight_n_bins(self, X, y=None, by='nsplits')

Reallocate number of bins per feature.

Parameters

X : data frame of shape (n_samples, n_features)
(Training) data to be discretized.
y : array-like of shape (n_samples,)
(Training) response vector. Required only if rf_model = None or rf_model has not yet been fitted
by : {'nsplits'}, default='nsplits'

Specifies how to reallocate number of bins per feature.

nsplits Reallocate number of bins so that each feature in dcols get at a minimum of 2 bins with the remaining bins distributed proportionally to the number of RF splits using that feature

Returns

self.n_bins : array of shape (len(dcols),)
number of bins per feature reallocated according to 'by' argument
Expand source code
def reweight_n_bins(self, X, y=None, by="nsplits"):
    """
    Reallocate number of bins per feature.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        (Training) data to be discretized.

    y : array-like of shape (n_samples,)
        (Training) response vector. Required only if
        rf_model = None or rf_model has not yet been fitted

    by : {'nsplits'}, default='nsplits'
        Specifies how to reallocate number of bins per feature.

        nsplits
            Reallocate number of bins so that each feature
            in dcols get at a minimum of 2 bins with the
            remaining bins distributed proportionally to the
            number of RF splits using that feature

    Returns
    -------
    self.n_bins : array of shape (len(dcols),)
        number of bins per feature reallocated according to
        'by' argument
    """
    # initialization and error checking
    self._fit_preprocessing(X)

    # get all random forest split points
    self._fit_rf(X=X, y=y)

    # get total number of bins to reallocate
    total_bins = np.asarray(self.n_bins_).sum()

    # reweight n_bins
    if by == "nsplits":
        # each col gets at least 2 bins; remaining bins get
        # reallocated based on number of RF splits using that feature
        n_rules = np.array([len(self.rf_splits[col])
                           for col in self.dcols_])
        self.n_bins = np.round(n_rules / n_rules.sum() *
                               (total_bins - 2 * len(self.dcols_))) + 2
    else:
        valid_by = ('nsplits')
        raise ValueError("Valid options for 'by' are {}. Got by={!r} instead."
                         .format(valid_by, by))
def transform(self, X)

Discretize the data.

Parameters

X : data frame of shape (n_samples, n_features)
Data to be discretized.

Returns

X_discretized : data frame
Data with features in dcols transformed to the binned space. All other features remain unchanged.
Expand source code
def transform(self, X):
    """
    Discretize the data.

    Parameters
    ----------
    X : data frame of shape (n_samples, n_features)
        Data to be discretized.

    Returns
    -------
    X_discretized : data frame
        Data with features in dcols transformed to the
        binned space. All other features remain unchanged.
    """

    check_is_fitted(self)

    # transform features that did not appear in RF
    if len(self.missing_rf_cols_) > 0:
        discretized_df = self.backup_discretizer_.transform(
            X[self.missing_rf_cols_])
        discretized_df = pd.DataFrame(discretized_df,
                                      columns=self.missing_rf_cols_,
                                      index=X.index)
    else:
        discretized_df = pd.DataFrame({}, index=X.index)

    # do discretization based on rf split thresholds
    for col in self.bin_edges_.keys():
        discretized_df[col] = self._discretize_to_bins(
            X[col], self.bin_edges_[col])

    # return onehot encoded data if specified and
    # join discretized columns with rest of X
    X_discretized = self._transform_postprocessing(discretized_df, X)

    return X_discretized
class SimpleDiscretizer (n_bins: int = 8, strategy: str = 'uniform')
Expand source code
class SimpleDiscretizer:

    def __init__(self, n_bins: int = 8, strategy: str = 'uniform'):
        self.n_bins = n_bins
        self.strategy = strategy

    def fit(self, X: np.array, feature_labels: np.array):
        self.is_categorical = np.array([set(np.unique(X[:, i])).issubset({0, 1}) for i in np.arange(X.shape[1])])

        if False not in self.is_categorical:
            self.feature_labels = feature_labels
            self.discretizer = None
            return

        if isinstance(feature_labels, list):
            feature_labels = np.array(feature_labels)

        # X_categorical = X[:, self.is_categorical]
        X_categorical_columns = feature_labels[self.is_categorical]
        # X_numeric = X[:, ~self.is_categorical]
        X_numeric_columns = feature_labels[~self.is_categorical]

        self.discretizer = KBinsDiscretizer(n_bins=self.n_bins, encode='onehot', strategy=self.strategy)
        # X_numeric_discretized = self.discretizer.fit(X_numeric)

        discretized_featnames = []
        for feat_name, bin_edges in zip(X_numeric_columns, self.discretizer.bin_edges_):
            be_str = bin_edges.astype(str)
            discretized_featnames += (
                [f'{feat_name}_' + '_to_'.join([be_str[i], be_str[i + 1]]) for i in range(bin_edges.shape[0] - 1)]
            )
        self.featnames_after_disc = np.append(discretized_featnames, X_categorical_columns)

    def transform(self, X: np.array):
        if self.discretizer is None:
            return pd.DataFrame(X, columns=self.feature_labels)

        X_categorical = X[:, self.is_categorical]
        X_numeric = X[:, ~self.is_categorical]

        X_numeric_discretized = self.discretizer.transform(X_numeric).toarray()
        X_concat = np.concatenate((X_numeric_discretized, X_categorical), axis=1)
        X_df_onehot = pd.DataFrame(X_concat, columns=self.featnames_after_disc)

        return X_df_onehot

    def fit_transform(self, X: np.array, feature_labels: np.array):
        self.fit(X, feature_labels)
        return self.transform(X)

Methods

def fit(self, X: , feature_labels: )
Expand source code
def fit(self, X: np.array, feature_labels: np.array):
    self.is_categorical = np.array([set(np.unique(X[:, i])).issubset({0, 1}) for i in np.arange(X.shape[1])])

    if False not in self.is_categorical:
        self.feature_labels = feature_labels
        self.discretizer = None
        return

    if isinstance(feature_labels, list):
        feature_labels = np.array(feature_labels)

    # X_categorical = X[:, self.is_categorical]
    X_categorical_columns = feature_labels[self.is_categorical]
    # X_numeric = X[:, ~self.is_categorical]
    X_numeric_columns = feature_labels[~self.is_categorical]

    self.discretizer = KBinsDiscretizer(n_bins=self.n_bins, encode='onehot', strategy=self.strategy)
    # X_numeric_discretized = self.discretizer.fit(X_numeric)

    discretized_featnames = []
    for feat_name, bin_edges in zip(X_numeric_columns, self.discretizer.bin_edges_):
        be_str = bin_edges.astype(str)
        discretized_featnames += (
            [f'{feat_name}_' + '_to_'.join([be_str[i], be_str[i + 1]]) for i in range(bin_edges.shape[0] - 1)]
        )
    self.featnames_after_disc = np.append(discretized_featnames, X_categorical_columns)
def fit_transform(self, X: , feature_labels: )
Expand source code
def fit_transform(self, X: np.array, feature_labels: np.array):
    self.fit(X, feature_labels)
    return self.transform(X)
def transform(self, X: )
Expand source code
def transform(self, X: np.array):
    if self.discretizer is None:
        return pd.DataFrame(X, columns=self.feature_labels)

    X_categorical = X[:, self.is_categorical]
    X_numeric = X[:, ~self.is_categorical]

    X_numeric_discretized = self.discretizer.transform(X_numeric).toarray()
    X_concat = np.concatenate((X_numeric_discretized, X_categorical), axis=1)
    X_df_onehot = pd.DataFrame(X_concat, columns=self.featnames_after_disc)

    return X_df_onehot