feature_engine.wrappers.wrappers 源代码

from typing import List, Optional, Union

import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin, clone
from sklearn.utils.validation import check_is_fitted

from feature_engine._check_init_parameters.check_variables import (
    _check_variables_input_value,
)
from feature_engine.dataframe_checks import _check_X_matches_training_df, check_X
from feature_engine.tags import _return_tags
from feature_engine.variable_handling import (
    check_numerical_variables,
    find_numerical_variables,
)
from feature_engine.variable_handling.check_variables import check_all_variables
from feature_engine.variable_handling.find_variables import find_all_variables

_SELECTORS = [
    "GenericUnivariateSelect",
    "RFE",
    "RFECV",
    "SelectFdr",
    "SelectFpr",
    "SelectFromModel",
    "SelectFwe",
    "SelectKBest",
    "SelectPercentile",
    "SequentialFeatureSelector",
    "VarianceThreshold",
]

_CREATORS = [
    # 'FeatureHasher',
    "OneHotEncoder",
    "PolynomialFeatures",
]

_TRANSFORMERS = [
    # transformers
    "Binarizer",
    "FunctionTransformer",
    "KBinsDiscretizer",
    "PowerTransformer",
    "QuantileTransformer",
    # imputers
    "SimpleImputer",
    "IterativeImputer",
    "KNNImputer",
    # encoders
    "OrdinalEncoder",
    # scalers
    "MaxAbsScaler",
    "MinMaxScaler",
    "StandardScaler",
    "RobustScaler",
    "Normalizer",
]

_ALL_TRANSFORMERS = _SELECTORS + _CREATORS + _TRANSFORMERS

_INVERSE_TRANSFORM = [
    "PowerTransformer",
    "QuantileTransformer",
    "OrdinalEncoder",
    "MaxAbsScaler",
    "MinMaxScaler",
    "StandardScaler",
    "RobustScaler",
]


[文档]class SklearnTransformerWrapper(BaseEstimator, TransformerMixin): """ Wrapper to apply Scikit-learn transformers to a selected group of variables. It supports the following transformers: - Binarizer and KBinsDiscretizer (only when encoding=Ordinal) - FunctionTransformer, PowerTransformer and QuantileTransformer - SimpleImputer, IterativeImputer and KNNImputer (only when add_indicators=False) - OrdinalEncoder and OneHotEncoder (only when sparse is False) - MaxAbsScaler, MinMaxScaler, StandardScaler, RobustScaler, Normalizer - All selection transformers including VarianceThreshold - PolynomialFeautures More details in the :ref:`User Guide <sklearn_wrapper>`. Parameters ---------- transformer: sklearn transformer The desired Scikit-learn transformer. variables: list, default=None The list of variables to be transformed. If None, the wrapper will select all variables of type numeric for all transformers, except the SimpleImputer, OrdinalEncoder and OneHotEncoder, in which case, it will select all variables in the dataset. Attributes ---------- transformer_: The fitted Scikit-learn transformer. variables_: The group of variables that will be transformed. features_to_drop_: The variables that will be dropped. Only present when using selection transformers feature_names_in_: List with the names of features seen during `fit`. n_features_in_: The number of features in the train set used in fit. Methods ------- fit: Fit Scikit-learn transformer. fit_transform: Fit to data, then transform it. get_feature_names_out: Get output feature names for transformation. get_params: Get parameters for this estimator. set_params: Set the parameters of this estimator. inverse_transform: Convert the data back to the original representation. transform: Transform data with the Scikit-learn transformer. Notes ----- This transformer offers similar functionality to the ColumnTransformer from Scikit-learn, but it allows entering the transformations directly into a Pipeline and returns pandas dataframes. See Also -------- sklearn.compose.ColumnTransformer Examples -------- >>> import pandas as pd >>> from feature_engine.wrappers import SklearnTransformerWrapper >>> from sklearn.preprocessing import StandardScaler >>> X = pd.DataFrame(dict(x1 = ["a","b","c"], x2 = [1,2,3], x3 = [4,5,6])) >>> skw = SklearnTransformerWrapper(StandardScaler()) >>> skw.fit(X) >>> skw.transform(X) x1 x2 x3 0 a -1.224745 -1.224745 1 b 0.000000 0.000000 2 c 1.224745 1.224745 >>> import pandas as pd >>> from feature_engine.wrappers import SklearnTransformerWrapper >>> from sklearn.preprocessing import OneHotEncoder >>> X = pd.DataFrame(dict(x1 = ["a","b","c"], x2 = [1,2,3], x3 = [4,5,6])) >>> skw = SklearnTransformerWrapper( >>> OneHotEncoder(sparse_output = False), variables = "x1") >>> skw.fit(X) >>> skw.transform(X) x2 x3 x1_a x1_b x1_c 0 1 4 1.0 0.0 0.0 1 2 5 0.0 1.0 0.0 2 3 6 0.0 0.0 1.0 >>> import pandas as pd >>> from feature_engine.wrappers import SklearnTransformerWrapper >>> from sklearn.preprocessing import PolynomialFeatures >>> X = pd.DataFrame(dict(x1 = ["a","b","c"], x2 = [1,2,3], x3 = [4,5,6])) >>> skw = SklearnTransformerWrapper(PolynomialFeatures(include_bias = False)) >>> skw.fit(X) >>> skw.transform(X) x1 x2 x3 x2^2 x2 x3 x3^2 0 a 1.0 4.0 1.0 4.0 16.0 1 b 2.0 5.0 4.0 10.0 25.0 2 c 3.0 6.0 9.0 18.0 36.0 """ def __init__( self, transformer, variables: Union[None, int, str, List[Union[str, int]]] = None, ) -> None: if not issubclass(transformer.__class__, TransformerMixin): raise TypeError( "transformer expected a Scikit-learn transformer. " f"got {transformer} instead. " ) if transformer.__class__.__name__ not in _ALL_TRANSFORMERS: raise NotImplementedError( "This transformer is not compatible with the wrapper. " "Supported transformers are {}.".format(", ".join(_ALL_TRANSFORMERS)) ) if ( transformer.__class__.__name__ in ["SimpleImputer", "KNNImputer", "IterativeImputer"] and transformer.add_indicator is True ): raise NotImplementedError( "The imputer is only compatible with the wrapper when the " "parameter `add_indicator` is False. " ) if ( transformer.__class__.__name__ == "KBinsDiscretizer" and transformer.encode != "ordinal" ): raise NotImplementedError( "The KBinsDiscretizer is only compatible with the wrapper when the " "parameter `encode` is `ordinal`. " ) if transformer.__class__.__name__ == "OneHotEncoder": msg = ( "The SklearnTransformerWrapper can only wrap the OneHotEncoder if the " "sparse is set to False." ) if getattr(transformer, "sparse", False) or getattr( transformer, "sparse_output", False ): raise NotImplementedError(msg) self.transformer = transformer self.variables = _check_variables_input_value(variables)
[文档] def fit(self, X: pd.DataFrame, y: Optional[str] = None): """ Fits the Scikit-learn transformer to the selected variables. Parameters ---------- X: Pandas DataFrame The dataset to fit the transformer. y: pandas Series, default=None The target variable. """ # check input dataframe X = check_X(X) self.transformer_ = clone(self.transformer) if self.transformer_.__class__.__name__ in [ "OneHotEncoder", "OrdinalEncoder", "SimpleImputer", "FunctionTransformer", ]: if self.variables is None: self.variables_ = find_all_variables(X) else: self.variables_ = check_all_variables(X, self.variables) else: if self.variables is None: self.variables_ = find_numerical_variables(X) else: self.variables_ = check_numerical_variables(X, self.variables) self.transformer_.fit(X[self.variables_], y) if self.transformer_.__class__.__name__ in _SELECTORS: # Find features to drop. selected = X[self.variables_].columns[self.transformer_.get_support()] self.features_to_drop_ = [f for f in self.variables_ if f not in selected] # save input features self.feature_names_in_ = X.columns.tolist() self.n_features_in_ = X.shape[1] return self
[文档] def transform(self, X: pd.DataFrame) -> pd.DataFrame: """ Apply the transformation to the dataframe. Only the selected variables will be modified. If the Scikit-learn transformer is the OneHotEncoder or the PolynomialFeatures, the new features will be concatenated to the input dataset. If the Scikit-learn transformer is for feature selection, the non-selected features will be dropped from the dataframe. For all other transformers, the original variables will be replaced by the transformed ones. Parameters ---------- X: Pandas DataFrame The data to transform. Returns ------- X_new: Pandas DataFrame The transformed dataset. """ check_is_fitted(self) # check that input is a dataframe X = check_X(X) # Check that input data contains same number of columns than # the dataframe used to fit the imputer. _check_X_matches_training_df(X, self.n_features_in_) # reorder df to match train set X = X[self.feature_names_in_] # Transformers that add features: creators if self.transformer_.__class__.__name__ in [ "OneHotEncoder", "PolynomialFeatures", ]: new_features_df = pd.DataFrame( data=self.transformer_.transform(X[self.variables_]), columns=self.transformer_.get_feature_names_out(self.variables_), index=X.index, ) X = pd.concat([X.drop(columns=self.variables_), new_features_df], axis=1) # Feature selection: transformers that remove features elif self.transformer_.__class__.__name__ in _SELECTORS: # return the dataframe with the selected features X.drop(columns=self.features_to_drop_, inplace=True) # Transformers that modify existing features else: X[self.variables_] = self.transformer_.transform(X[self.variables_]) return X
[文档] def inverse_transform(self, X: pd.DataFrame) -> pd.DataFrame: """Convert the transformed variables back to the original values. Only implemented for the following Scikit-learn transformers: PowerTransformer, QuantileTransformer, OrdinalEncoder, MaxAbsScaler, MinMaxScaler, StandardScaler, RobustScaler. If you would like this method implemented for additional transformers, please check if they have the inverse_transform method in Scikit-learn and then raise an issue in our repo. Parameters ---------- X: pandas dataframe of shape = [n_samples, n_features]. The transformed dataframe. Returns ------- X_tr: pandas dataframe of shape = [n_samples, n_features]. The dataframe with the original values. """ # Check method fit has been called check_is_fitted(self) # check that input is a dataframe X = check_X(X) if self.transformer_.__class__.__name__ not in _INVERSE_TRANSFORM: raise NotImplementedError( "The method `inverse_transform` is not implemented for this " "transformer. Supported transformers are {}.".format( ", ".join(_INVERSE_TRANSFORM) ) ) # For safety, we check that the transformer has the method implemented. if hasattr(self.transformer_, "inverse_transform") and callable( self.transformer_.inverse_transform ): X[self.variables_] = self.transformer_.inverse_transform(X[self.variables_]) else: raise NotImplementedError( "This Scikit-learn transformer does not have the method " "`inverse_transform` implemented." ) return X
[文档] def get_feature_names_out( self, input_features: Optional[List[Union[str, int]]] = None ) -> List: """Get output feature names for transformation. input_features: list, default=None If `None`, then the names of all the variables in the transformed dataset is returned. For those transformers that create and add new features to the dataset, like the OneHotEncoder or the PolynomialFeatures, you have the option to pass a list with the input features to obtain the newly created variables. For all other transformers, this parameter will be ignored. Returns ------- feature_names_out: list The feature names. """ # Check method fit has been called check_is_fitted(self) if self.transformer_.__class__.__name__ in _TRANSFORMERS: feature_names = self.feature_names_in_ if self.transformer_.__class__.__name__ in _CREATORS: if input_features is None: added_features = self.transformer_.get_feature_names_out( self.variables_ ) original_features = [ feature for feature in self.feature_names_in_ if feature not in self.variables_ ] feature_names = original_features + list(added_features) else: feature_names = list( self.transformer_.get_feature_names_out(input_features) ) if self.transformer_.__class__.__name__ in _SELECTORS: feature_names = [ f for f in self.feature_names_in_ if f not in self.features_to_drop_ ] return feature_names
def _more_tags(self): tags_dict = _return_tags() # add additional test that fails tags_dict["_xfail_checks"]["check_estimators_nan_inf"] = "transformer allows NA" tags_dict["_xfail_checks"][ "check_parameters_default_constructible" ] = "transformer has 1 mandatory parameter" return tags_dict