Source code for pretab.transformers.onehot.onehot
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils.validation import check_is_fitted
[docs]
class OneHotFromOrdinalTransformer(TransformerMixin, BaseEstimator):
"""Convert ordinal-encoded features into a one-hot encoded representation.
This is useful when features have already been ordinal-encoded and a one-hot
representation is required for model training.
Attributes
----------
max_bins_ : ndarray of shape (n_features,)
Array containing the maximum bin index (plus one) for each feature, which
determines the width of the one-hot block for that feature.
Notes
-----
Each input feature ``i`` is expanded into ``max_bins_[i]`` columns, so the
total number of output columns is the sum of ``max_bins_`` across all
features.
Examples
--------
>>> import numpy as np
>>> from pretab.transformers import OneHotFromOrdinalTransformer
>>> X = np.array([[0, 1], [1, 0], [2, 1]])
>>> transformer = OneHotFromOrdinalTransformer()
>>> transformer.fit_transform(X).shape
(3, 5)
"""
[docs]
def fit(self, X, y=None):
"""Learn the maximum bin index for each feature from the data.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The input data containing ordinal-encoded features.
y : Ignored
Not used, present for API consistency by convention.
Returns
-------
self : object
Fitted transformer.
"""
self.max_bins_ = (
np.max(X, axis=0).astype(int) + 1
) # Find the maximum bin index for each feature
self.n_features_in_ = np.asarray(X).shape[1]
return self
[docs]
def transform(self, X):
"""Convert ordinal-encoded features into one-hot encoded format.
Uses the ``max_bins_`` learned during fitting.
Parameters
----------
X : array-like of shape (n_samples, n_features)
The input data containing ordinal-encoded features.
Returns
-------
X_one_hot : ndarray of shape (n_samples, n_output_features)
The one-hot encoded features.
Notes
-----
Codes outside the range learned during :meth:`fit` (negative values or
values ``>= max_bins_[i]``) are treated as unknown and encoded as an
all-zero row, mirroring scikit-learn's ``handle_unknown="ignore"``.
"""
check_is_fitted(self, "max_bins_")
X = np.asarray(X)
# Initialize an empty list to hold the one-hot encoded arrays
one_hot_encoded = []
for i, max_bins in enumerate(self.max_bins_):
max_bins = int(max_bins)
codes = X[:, i].astype(int)
# Codes outside the fitted range map to an all-zero row instead of
# raising an IndexError on np.eye indexing.
in_range = (codes >= 0) & (codes < max_bins)
feature_one_hot = np.zeros((codes.shape[0], max_bins))
feature_one_hot[np.nonzero(in_range)[0], codes[in_range]] = 1.0
one_hot_encoded.append(feature_one_hot)
# Concatenate the one-hot encoded features horizontally
return np.hstack(one_hot_encoded)
[docs]
def get_feature_names_out(self, input_features=None):
"""Generate feature names for the one-hot encoded output.
Parameters
----------
input_features : list of str
The names of the input features that were ordinal-encoded.
Returns
-------
feature_names : ndarray of shape (n_output_features,)
The names of the one-hot encoded features.
"""
feature_names = []
for i, max_bins in enumerate(self.max_bins_):
feature_names.extend([f"{input_features[i]}_bin_{j}" for j in range(int(max_bins))]) # type: ignore
return np.array(feature_names)
def __sklearn_tags__(self):
"""Ordinal integer input is required; missing values are not supported."""
tags = super().__sklearn_tags__()
tags.input_tags.allow_nan = False
return tags