-
Notifications
You must be signed in to change notification settings - Fork 89
/
func_transform.py
194 lines (162 loc) · 7.14 KB
/
func_transform.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""Implements FunctionTransformer, a class to create custom transformers."""
__maintainer__ = []
__all__ = ["FunctionTransformer"]
import numpy as np
from aeon.transformations.base import BaseTransformer
def _identity(X):
"""Return X."""
return X
class FunctionTransformer(BaseTransformer):
r"""
Constructs a transformer from an arbitrary callable.
A FunctionTransformer forwards its y (and optionally X) 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.
Note: If a lambda is used as the function, then the resulting
transformer will not be pickleable.
Parameters
----------
func : callable (X: X_type, **kwargs) -> X_type, default=identity (return X)
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.
inverse_func : callable (X: X_type, **kwargs) -> X_type, default=identity
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.
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.
kw_args : dict, default=None
Dictionary of additional keyword arguments to pass to func.
inv_kw_args : dict, default=None
Dictionary of additional keyword arguments to pass to inverse_func.
X_type : str, one of "pd.DataFrame, pd.Series, np.ndarray", or list thereof
Default = ["pd.DataFrame", "pd.Series", "np.ndarray"]
list of types that func is assumed to allow for X (see signature above)
if X passed to transform/inverse_transform is not on the list,
it will be converted to the first list element before passed to funcs
See Also
--------
aeon.transformations.boxcox.LogTransformer :
Transformer input data using natural log. Can help normalize data and
compress variance of the series.
aeon.transformations.exponent.ExponentTransformer :
Transform input data by raising it to an exponent. Can help compress
variance of series if a fractional exponent is supplied.
aeon.transformations.exponent.SqrtTransformer :
Transform input data by taking its square root. Can help compress
variance of input series.
Examples
--------
>>> import numpy as np
>>> from aeon.transformations.func_transform import FunctionTransformer
>>> transformer = FunctionTransformer(np.log1p, np.expm1)
>>> X = np.array([[0, 1], [2, 3]])
>>> transformer.fit_transform(X)
array([[0. , 0.69314718],
[1.09861229, 1.38629436]])
"""
_tags = {
"input_data_type": "Series",
# what is the abstract type of X: Series, or Panel
"output_data_type": "Series",
# what abstract type is returned: Primitives, Series, Panel
"instancewise": True, # is this an instance-wise transform?
"X_inner_type": ["pd.DataFrame", "pd.Series", "np.ndarray"],
"y_inner_type": "None",
"fit_is_empty": True,
"capability:missing_values": True,
"capability:inverse_transform": True,
}
def __init__(
self,
func=None,
inverse_func=None,
*,
check_inverse=True,
kw_args=None,
inv_kw_args=None,
X_type=None,
):
self.func = func
self.inverse_func = inverse_func
self.check_inverse = check_inverse
self.kw_args = kw_args
self.inv_kw_args = inv_kw_args
self.X_type = X_type
super().__init__()
if X_type is not None:
self.set_tags(X_inner_type=X_type)
def _check_inverse_transform(self, Z):
"""Check that func and inverse_func are each other's inverse."""
Z_round_trip = self.inverse_func(self.func(Z))
if not np.allclose(Z_round_trip, Z, equal_nan=True):
raise UserWarning(
"The provided functions are not strictly"
" inverse of each other. If you are sure you"
" want to proceed regardless, set"
" 'check_inverse=False'."
)
def _transform(self, X, y=None):
"""Transform X and return a transformed version.
private _transform containing the core logic, called from transform
Parameters
----------
X : pd.Series or pd.DataFrame or 1D/2D np.ndarray
Data to be transformed
y : ignored argument for interface compatibility
Additional data, e.g., labels for transformation
Returns
-------
Xt : pd.Series or pd.DataFrame or 1D/2D np.ndarray, same type as X
transformed version of X
"""
if self.check_inverse and not (self.func is None or self.inverse_func is None):
self._check_inverse_transform(X)
Xt = self._apply_function(X, func=self.func, kw_args=self.kw_args)
return Xt
def _inverse_transform(self, X, y=None):
"""Inverse transform X and return an inverse transformed version.
core logic
Parameters
----------
X : pd.Series or pd.DataFrame or 1D/2D np.ndarray
Data to be transformed
y : ignored argument for interface compatibility
Additional data, e.g., labels for transformation
Returns
-------
Xt : pd.Series or pd.DataFrame or 1D/2D np.ndarray, same type as X
inverse transformed version of X
"""
Xt = self._apply_function(X, func=self.inverse_func, kw_args=self.inv_kw_args)
return Xt
def _apply_function(self, Z, func=None, kw_args=None):
if func is None:
func = _identity
return func(Z, **(kw_args if kw_args else {}))
@classmethod
def get_test_params(cls, parameter_set="default"):
"""Return testing parameter settings for the estimator.
Parameters
----------
parameter_set : str, default="default"
Name of the set of test parameters to return, for use in tests. If no
special parameters are defined for a value, will return `"default"` set.
Returns
-------
params : dict or list of dict, default = {}
Parameters to create testing instances of the class
Each dict are parameters to construct an "interesting" test instance, i.e.,
`MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
`create_test_instance` uses the first (or only) dictionary in `params`
"""
# default params, identity transform
params1 = {}
# log-transformer, with exp inverse
params2 = {"func": np.expm1, "inverse_func": np.log1p}
return [params1, params2]