Source code for mlinsights.mlmodel.target_predictors

"""
Implements a slightly different
version of the :epkg:`sklearn:compose:TransformedTargetRegressor`.


:githublink:`%|py|6`
"""
from sklearn.base import BaseEstimator, RegressorMixin, ClassifierMixin, clone
from sklearn.exceptions import NotFittedError
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.metrics import r2_score, accuracy_score
from .sklearn_transform_inv import BaseReciprocalTransformer
from .sklearn_transform_inv_fct import FunctionReciprocalTransformer, PermutationReciprocalTransformer


[docs]def _common_get_transform(transformer, is_regression): if isinstance(transformer, str): closest = is_regression if transformer == 'permute': return PermutationReciprocalTransformer(closest=closest) else: return FunctionReciprocalTransformer(transformer) elif isinstance(transformer, BaseReciprocalTransformer): return clone(transformer) raise TypeError("Transformer {} must be a string or on object of type " "BaseReciprocalTransformer.".format(type(transformer)))
[docs]class TransformedTargetRegressor2(BaseEstimator, RegressorMixin): """ Meta-estimator to regress on a transformed target. Useful for applying a non-linear transformation in regression problems. Parameters ---------- regressor : object, default=LinearRegression() Regressor object such as derived from ``RegressorMixin``. This regressor will automatically be cloned each time prior to fitting. transformer : str or object of type :class:`BaseReciprocalTransformer <mlinsights.mlmodel.sklearn_transform_inv.BaseReciprocalTransformer>` Attributes ---------- regressor_ : object Fitted regressor. transformer_ : object Transformer used in ``fit`` and ``predict``. Examples -------- .. runpython:: :showcode: import numpy from sklearn.linear_model import LinearRegression from mlinsights.mlmodel import TransformedTargetRegressor2 tt = TransformedTargetRegressor2(regressor=LinearRegression(), transformer='log') X = numpy.arange(4).reshape(-1, 1) y = numpy.exp(2 * X).ravel() print(tt.fit(X, y)) print(tt.score(X, y)) print(tt.regressor_.coef_) See notebook :ref:`sklearntransformedtargetrst` for a more complete example. :githublink:`%|py|66` """
[docs] def __init__(self, regressor=None, transformer=None): self.regressor = regressor self.transformer = transformer
[docs] def fit(self, X, y, sample_weight=None): """ Fits the model according to the given training data. :param X: {array-like, sparse matrix}, shape (n_samples, n_features) Training vector, where n_samples is the number of samples and n_features is the number of features. :param y: array-like, shape (n_samples,) Target values. :param sample_weight: array-like, shape (n_samples,) optional Array of weights that are assigned to individual samples. If not provided, then each sample is given unit weight. :return: self, object :githublink:`%|py|85` """ self.transformer_ = _common_get_transform(self.transformer, True) self.transformer_.fit(X, y, sample_weight=sample_weight) X_trans, y_trans = self.transformer_.transform(X, y) if self.regressor is None: self.regressor_ = LinearRegression() else: self.regressor_ = clone(self.regressor) if sample_weight is None: self.regressor_.fit(X_trans, y_trans) else: self.regressor_.fit(X_trans, y_trans, sample_weight=sample_weight) return self
[docs] def predict(self, X): """ Predicts using the base regressor, applying inverse. :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) Samples. :return: y_hat : array, shape = (n_samples,) Predicted values. :githublink:`%|py|110` """ if not hasattr(self, 'regressor_'): raise NotFittedError( "This instance {} is not fitted yet. Call 'fit' with " "appropriate arguments before using this method.".format( type(self))) X_trans, _ = self.transformer_.transform(X, None) pred = self.regressor_.predict(X_trans) inv = self.transformer_.get_fct_inv() _, pred_inv = inv.transform(X_trans, pred) return pred_inv
[docs] def score(self, X, y, sample_weight=None): """ Scores the model with :epkg:`sklearn:metrics:r2_score`. :githublink:`%|py|127` """ yp = self.predict(X) return r2_score(y, yp, sample_weight=sample_weight)
[docs] def _more_tags(self): return {'poor_score': True, 'no_validation': True}
[docs]class TransformedTargetClassifier2(BaseEstimator, ClassifierMixin): """ Meta-estimator to classify on a transformed target. Useful for applying permutation transformation in classification problems. Parameters ---------- classifier : object, default=LogisticRegression() Classifier object such as derived from ``ClassifierMixin``. This classifier will automatically be cloned each time prior to fitting. transformer : str or object of type :class:`BaseReciprocalTransformer <mlinsights.mlmodel.sklearn_transform_inv.BaseReciprocalTransformer>` Attributes ---------- classifier_ : object Fitted classifier. transformer_ : object Transformer used in ``fit``, ``predict``, ``decision_function``, ``predict_proba``. Examples -------- .. runpython:: :showcode: import numpy from sklearn.linear_model import LogisticRegression from mlinsights.mlmodel import TransformedTargetClassifier2 tt = TransformedTargetClassifier2(classifier=LogisticRegression(), transformer='permute') X = numpy.arange(4).reshape(-1, 1) y = numpy.array([0, 1, 0, 1]) print(tt.fit(X, y)) print(tt.score(X, y)) print(tt.classifier_.coef_) See notebook :ref:`sklearntransformedtargetrst` for a more complete example. :githublink:`%|py|175` """
[docs] def __init__(self, classifier=None, transformer=None): self.classifier = classifier self.transformer = transformer
[docs] def fit(self, X, y, sample_weight=None): """ Fits the model according to the given training data. :param X: {array-like, sparse matrix}, shape (n_samples, n_features) Training vector, where n_samples is the number of samples and n_features is the number of features. :param y: array-like, shape (n_samples,) Target values. :param sample_weight: array-like, shape (n_samples,) optional Array of weights that are assigned to individual samples. If not provided, then each sample is given unit weight. :return: self, object :githublink:`%|py|194` """ self.transformer_ = _common_get_transform(self.transformer, False) self.transformer_.fit(X, y, sample_weight=sample_weight) X_trans, y_trans = self.transformer_.transform(X, y) if self.classifier is None: self.classifier_ = LogisticRegression() else: self.classifier_ = clone(self.classifier) if sample_weight is None: self.classifier_.fit(X_trans, y_trans) else: self.classifier_.fit(X_trans, y_trans, sample_weight=sample_weight) return self
[docs] def _check_is_fitted(self): if not hasattr(self, 'classifier_'): raise NotFittedError( "This instance {} is not fitted yet. Call 'fit' with " "appropriate arguments before using this method.".format( type(self)))
@property def classes_(self): """ Returns the classes. :githublink:`%|py|222` """ self._check_is_fitted() inv = self.transformer_.get_fct_inv() _, pred_inv = inv.transform(None, self.classifier_.classes_) return pred_inv
[docs] def _apply(self, X, method): """ Calls *predict*, *predict_proba* or *decision_function* using the base classifier, applying inverse. :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) Samples. :return: y_hat, array, shape = (n_samples,) Predicted values. :githublink:`%|py|237` """ self._check_is_fitted() if not hasattr(self.classifier_, method): raise RuntimeError("Unable to find method '{}' in model {}.".format( method, type(self.classifier_))) meth = getattr(self.classifier_, method) X_trans, _ = self.transformer_.transform(X, None) pred = meth(X_trans) inv = self.transformer_.get_fct_inv() _, pred_inv = inv.transform(X_trans, pred) return pred_inv
[docs] def predict(self, X): """ Predicts using the base classifier, applying inverse. :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) Samples. :return: y_hat, array, shape = (n_samples,) Predicted values. :githublink:`%|py|257` """ return self._apply(X, 'predict')
[docs] def predict_proba(self, X): """ Predicts using the base classifier, applying inverse. :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) Samples. :return: predict probabilities, array, shape = (n_samples, n_classes) Predicted values. :githublink:`%|py|268` """ return self._apply(X, 'predict_proba')
[docs] def decision_function(self, X): """ Predicts using the base classifier, applying inverse. :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) Samples. :return: raw score : array, shape = (n_samples, ?) :githublink:`%|py|278` """ return self._apply(X, 'decision_function')
[docs] def score(self, X, y, sample_weight=None): """ Scores the model with :epkg:`sklearn:metrics:accuracy_score`. :githublink:`%|py|285` """ yp = self.predict(X) return accuracy_score(y, yp, sample_weight=sample_weight)
[docs] def _more_tags(self): return {'poor_score': True, 'no_validation': True}