2017-06-23 05:24:12 +08:00
|
|
|
import warnings
|
|
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
from ..base import BaseEstimator, TransformerMixin
|
|
|
|
|
from ..utils import check_array
|
2017-10-26 04:49:28 +08:00
|
|
|
from ..utils.testing import assert_allclose_dense_sparse
|
2015-06-02 04:08:31 +08:00
|
|
|
|
|
|
|
|
|
2015-07-31 00:48:04 +08:00
|
|
|
def _identity(X):
|
|
|
|
|
"""The identity function.
|
|
|
|
|
"""
|
|
|
|
|
return X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FunctionTransformer(BaseEstimator, TransformerMixin):
|
|
|
|
|
"""Constructs a transformer from an arbitrary callable.
|
|
|
|
|
|
2015-07-04 02:36:47 +08:00
|
|
|
A FunctionTransformer forwards its X (and optionally y) arguments to a
|
|
|
|
|
user-defined function or function object and returns the result of this
|
|
|
|
|
function. This is useful for stateless transformations such as taking the
|
|
|
|
|
log of frequencies, doing custom scaling, etc.
|
|
|
|
|
|
2015-07-31 00:48:04 +08:00
|
|
|
Note: If a lambda is used as the function, then the resulting
|
|
|
|
|
transformer will not be pickleable.
|
2015-06-02 04:08:31 +08:00
|
|
|
|
2015-10-22 03:51:04 +08:00
|
|
|
.. versionadded:: 0.17
|
|
|
|
|
|
2016-09-13 06:33:22 +08:00
|
|
|
Read more in the :ref:`User Guide <function_transformer>`.
|
|
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
func : callable, optional default=None
|
|
|
|
|
The callable to use for the transformation. This will be passed
|
|
|
|
|
the same arguments as transform, with args and kwargs forwarded.
|
|
|
|
|
If func is None, then func will be the identity function.
|
2015-07-31 00:48:04 +08:00
|
|
|
|
2016-04-12 13:26:30 +08:00
|
|
|
inverse_func : callable, optional default=None
|
|
|
|
|
The callable to use for the inverse transformation. This will be
|
|
|
|
|
passed the same arguments as inverse transform, with args and
|
|
|
|
|
kwargs forwarded. If inverse_func is None, then inverse_func
|
|
|
|
|
will be the identity function.
|
|
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
validate : bool, optional default=True
|
|
|
|
|
Indicate that the input X array should be checked before calling
|
2018-07-10 17:48:03 +08:00
|
|
|
``func``. The possibilities are:
|
|
|
|
|
|
|
|
|
|
- If False, there is no input validation.
|
|
|
|
|
- If True, then X will be converted to a 2-dimensional NumPy array or
|
|
|
|
|
sparse matrix. If the conversion is not possible an exception is
|
|
|
|
|
raised.
|
|
|
|
|
|
|
|
|
|
.. deprecated:: 0.20
|
|
|
|
|
``validate=True`` as default will be replaced by
|
|
|
|
|
``validate=False`` in 0.22.
|
2015-07-31 00:48:04 +08:00
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
accept_sparse : boolean, optional
|
2015-07-04 02:36:47 +08:00
|
|
|
Indicate that func accepts a sparse matrix as input. If validate is
|
|
|
|
|
False, this has no effect. Otherwise, if accept_sparse is false,
|
|
|
|
|
sparse matrix inputs will cause an exception to be raised.
|
2015-07-31 00:48:04 +08:00
|
|
|
|
2016-10-06 11:01:12 +08:00
|
|
|
pass_y : bool, optional default=False
|
2015-07-31 00:48:04 +08:00
|
|
|
Indicate that transform should forward the y argument to the
|
|
|
|
|
inner callable.
|
2015-06-02 04:08:31 +08:00
|
|
|
|
2017-06-23 05:24:12 +08:00
|
|
|
.. deprecated::0.19
|
|
|
|
|
|
2017-10-26 04:49:28 +08:00
|
|
|
check_inverse : bool, default=True
|
|
|
|
|
Whether to check that or ``func`` followed by ``inverse_func`` leads to
|
|
|
|
|
the original inputs. It can be used for a sanity check, raising a
|
|
|
|
|
warning when the condition is not fulfilled.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.20
|
|
|
|
|
|
2015-11-24 22:05:07 +08:00
|
|
|
kw_args : dict, optional
|
|
|
|
|
Dictionary of additional keyword arguments to pass to func.
|
|
|
|
|
|
2016-04-12 13:26:30 +08:00
|
|
|
inv_kw_args : dict, optional
|
|
|
|
|
Dictionary of additional keyword arguments to pass to inverse_func.
|
|
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
"""
|
2018-07-10 17:48:03 +08:00
|
|
|
def __init__(self, func=None, inverse_func=None, validate=None,
|
2017-10-26 04:49:28 +08:00
|
|
|
accept_sparse=False, pass_y='deprecated', check_inverse=True,
|
2016-04-12 13:26:30 +08:00
|
|
|
kw_args=None, inv_kw_args=None):
|
2015-06-02 04:08:31 +08:00
|
|
|
self.func = func
|
2016-04-12 13:26:30 +08:00
|
|
|
self.inverse_func = inverse_func
|
2015-06-02 04:08:31 +08:00
|
|
|
self.validate = validate
|
|
|
|
|
self.accept_sparse = accept_sparse
|
2015-07-31 00:48:04 +08:00
|
|
|
self.pass_y = pass_y
|
2017-10-26 04:49:28 +08:00
|
|
|
self.check_inverse = check_inverse
|
2015-11-24 22:05:07 +08:00
|
|
|
self.kw_args = kw_args
|
2016-04-12 13:26:30 +08:00
|
|
|
self.inv_kw_args = inv_kw_args
|
2015-06-02 04:08:31 +08:00
|
|
|
|
2018-07-10 17:48:03 +08:00
|
|
|
def _check_input(self, X):
|
|
|
|
|
# FIXME: Future warning to be removed in 0.22
|
|
|
|
|
if self.validate is None:
|
|
|
|
|
self._validate = True
|
|
|
|
|
warnings.warn("The default validate=True will be replaced by "
|
|
|
|
|
"validate=False in 0.22.", FutureWarning)
|
|
|
|
|
else:
|
|
|
|
|
self._validate = self.validate
|
|
|
|
|
|
|
|
|
|
if self._validate:
|
|
|
|
|
return check_array(X, accept_sparse=self.accept_sparse)
|
|
|
|
|
return X
|
|
|
|
|
|
2017-10-26 04:49:28 +08:00
|
|
|
def _check_inverse_transform(self, X):
|
|
|
|
|
"""Check that func and inverse_func are the inverse."""
|
|
|
|
|
idx_selected = slice(None, None, max(1, X.shape[0] // 100))
|
|
|
|
|
try:
|
|
|
|
|
assert_allclose_dense_sparse(
|
|
|
|
|
X[idx_selected],
|
|
|
|
|
self.inverse_transform(self.transform(X[idx_selected])))
|
|
|
|
|
except AssertionError:
|
|
|
|
|
warnings.warn("The provided functions are not strictly"
|
|
|
|
|
" inverse of each other. If you are sure you"
|
|
|
|
|
" want to proceed regardless, set"
|
|
|
|
|
" 'check_inverse=False'.", UserWarning)
|
|
|
|
|
|
2015-06-02 04:08:31 +08:00
|
|
|
def fit(self, X, y=None):
|
2017-06-08 21:16:05 +08:00
|
|
|
"""Fit transformer by checking X.
|
|
|
|
|
|
|
|
|
|
If ``validate`` is ``True``, ``X`` will be checked.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape (n_samples, n_features)
|
|
|
|
|
Input array.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
self
|
|
|
|
|
"""
|
2018-07-10 17:48:03 +08:00
|
|
|
X = self._check_input(X)
|
2017-10-26 04:49:28 +08:00
|
|
|
if (self.check_inverse and not (self.func is None or
|
|
|
|
|
self.inverse_func is None)):
|
|
|
|
|
self._check_inverse_transform(X)
|
2015-06-02 04:08:31 +08:00
|
|
|
return self
|
|
|
|
|
|
2018-10-12 02:56:37 +08:00
|
|
|
def transform(self, X):
|
2017-06-08 21:16:05 +08:00
|
|
|
"""Transform X using the forward function.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape (n_samples, n_features)
|
|
|
|
|
Input array.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
X_out : array-like, shape (n_samples, n_features)
|
|
|
|
|
Transformed input.
|
|
|
|
|
"""
|
2018-10-12 02:56:37 +08:00
|
|
|
return self._transform(X, func=self.func, kw_args=self.kw_args)
|
2017-06-23 05:24:12 +08:00
|
|
|
|
2018-10-12 02:56:37 +08:00
|
|
|
def inverse_transform(self, X):
|
2017-06-08 21:16:05 +08:00
|
|
|
"""Transform X using the inverse function.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
X : array-like, shape (n_samples, n_features)
|
|
|
|
|
Input array.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
X_out : array-like, shape (n_samples, n_features)
|
|
|
|
|
Transformed input.
|
|
|
|
|
"""
|
2018-10-12 02:56:37 +08:00
|
|
|
return self._transform(X, func=self.inverse_func,
|
2017-06-23 05:24:12 +08:00
|
|
|
kw_args=self.inv_kw_args)
|
2016-04-12 13:26:30 +08:00
|
|
|
|
2018-10-12 02:56:37 +08:00
|
|
|
def _transform(self, X, func=None, kw_args=None):
|
2018-07-10 17:48:03 +08:00
|
|
|
X = self._check_input(X)
|
2015-07-31 00:48:04 +08:00
|
|
|
|
2016-04-12 13:26:30 +08:00
|
|
|
if func is None:
|
|
|
|
|
func = _identity
|
2015-08-04 00:59:41 +08:00
|
|
|
|
2018-10-12 02:56:37 +08:00
|
|
|
return func(X, **(kw_args if kw_args else {}))
|