1 import sys
2
3 PY2 = sys.version_info < (3,)
4 if PY2:
5 # Python 2 is not happy with our package having the same name, we need dynamic import
6 import importlib
7
8 try:
9 # try old sklearn first, we are on python 2
10 sklearn_ft_base = importlib.import_module("sklearn.feature_selection.base")
11 except ImportError:
12 sklearn_ft_base = importlib.import_module("sklearn.feature_selection")
13
14 SelectorMixin = sklearn_ft_base.SelectorMixin
15
16 sklearn_base = importlib.import_module("sklearn.base")
17 BaseEstimator = sklearn_base.BaseEstimator
18
19 else:
20 # Normal imports on python 3
21 try:
22 from sklearn.feature_selection import SelectorMixin
23 except ImportError: # older sklearn
-
F401
'sklearn.feature_selection.base.SelectorMixin' imported but unused
24 from sklearn.feature_selection.base import SelectorMixin
25
26 from sklearn.base import BaseEstimator
27
28
29 try:
30 BaseEstimator._get_tags
31 except AttributeError:
32 import inspect
33
34 _DEFAULT_TAGS = {
35 'non_deterministic': False,
36 'requires_positive_X': False,
37 'requires_positive_y': False,
38 'X_types': ['2darray'],
39 'poor_score': False,
40 'no_validation': False,
41 'multioutput': False,
42 "allow_nan": False,
43 'stateless': False,
44 'multilabel': False,
45 '_skip_test': False,
46 '_xfail_checks': False,
47 'multioutput_only': False,
48 'binary_only': False,
49 'requires_fit': True,
50 'requires_y': False,
51 }
52
53 def _more_tags(self):
54 return _DEFAULT_TAGS
55
56 def _get_tags(self):
57 collected_tags = {}
58 for base_class in reversed(inspect.getmro(self.__class__)):
59 if hasattr(base_class, '_more_tags'):
60 # need the if because mixins might not have _more_tags
61 # but might do redundant work in estimators
62 # (i.e. calling more tags on BaseEstimator multiple times)
63 more_tags = base_class._more_tags(self)
64 collected_tags.update(more_tags)
65 return collected_tags
66
67 # set the missing methods
68 BaseEstimator._more_tags = _more_tags
69 BaseEstimator._get_tags = _get_tags
70
71 try:
72 BaseEstimator._validate_data
73 except AttributeError:
74 if PY2:
75 # Python 2 is not happy with our package having the same name, we need dynamic import
76 import importlib
77
78 sklearn_ut_val = importlib.import_module("sklearn.utils.validation")
79 check_X_y = sklearn_ut_val.check_X_y
80 check_array = sklearn_ut_val.check_array
81 else:
82 # normal import on python 3
83 from sklearn.utils.validation import check_X_y, check_array
84
85 def _validate_data(self, X, y=None, reset=True,
86 validate_separately=False, **check_params):
87 """Validate input data and set or check the `n_features_in_` attribute.
88
89 Parameters
90 ----------
91 X : {array-like, sparse matrix, dataframe} of shape \
92 (n_samples, n_features)
93 The input samples.
94 y : array-like of shape (n_samples,), default=None
95 The targets. If None, `check_array` is called on `X` and
96 `check_X_y` is called otherwise.
97 reset : bool, default=True
98 Whether to reset the `n_features_in_` attribute.
99 If False, the input will be checked for consistency with data
100 provided when reset was last True.
101 validate_separately : False or tuple of dicts, default=False
102 Only used if y is not None.
103 If False, call validate_X_y(). Else, it must be a tuple of kwargs
104 to be used for calling check_array() on X and y respectively.
105 **check_params : kwargs
106 Parameters passed to :func:`sklearn.utils.check_array` or
107 :func:`sklearn.utils.check_X_y`. Ignored if validate_separately
108 is not False.
109
110 Returns
111 -------
112 out : {ndarray, sparse matrix} or tuple of these
113 The validated input. A tuple is returned if `y` is not None.
114 """
115
116 if y is None:
117 # note: for some reason our patch above does not add this tag,
118 # there is still a KeyError if we use self._get_tags()['requires_y']
119 if self._get_tags().get('requires_y', False):
120 raise ValueError(
121 "This {} estimator "
122 "requires y to be passed, but the target y is None."
123 ).__format__(self.__class__.__name__)
124 X = check_array(X, **check_params)
125 out = X
126 else:
127 if validate_separately:
128 # We need this because some estimators validate X and y
129 # separately, and in general, separately calling check_array()
130 # on X and y isn't equivalent to just calling check_X_y()
131 # :(
132 check_X_params, check_y_params = validate_separately
133 X = check_array(X, **check_X_params)
134 y = check_array(y, **check_y_params)
135 else:
136 X, y = check_X_y(X, y, **check_params)
137 out = X, y
138
139 if check_params.get('ensure_2d', True):
140 self._check_n_features(X, reset=reset)
141
142 return out
143
144 # set the missing method
145 BaseEstimator._validate_data = _validate_data
146
147
148 try:
149 BaseEstimator._check_n_features
150 except AttributeError:
151 def _check_n_features(self, X, reset):
152 """Set the `n_features_in_` attribute, or check against it.
153
154 Parameters
155 ----------
156 X : {ndarray, sparse matrix} of shape (n_samples, n_features)
157 The input samples.
158 reset : bool
159 If True, the `n_features_in_` attribute is set to `X.shape[1]`.
160 Else, the attribute must already exist and the function checks
161 that it is equal to `X.shape[1]`.
162 """
163 n_features = X.shape[1]
164
165 if reset:
166 self.n_features_in_ = n_features
167 else:
168 if not hasattr(self, 'n_features_in_'):
169 raise RuntimeError(
170 "The reset parameter is False but there is no "
171 "n_features_in_ attribute. Is this estimator fitted?"
172 )
173 if n_features != self.n_features_in_:
174 raise ValueError(
175 'X has {} features, but this {} is expecting {} features '
176 'as input.'.format(n_features, self.__class__.__name__,
177 self.n_features_in_)
178 )
179
180 # set the missing method
181 BaseEstimator._check_n_features = _check_n_features
182
183
184 PY2 = sys.version_info < (3, 0)
185
186
187 def encode_if_py2(fun):
188 """
189 A decorator to use typically on __str__ and __repr__ methods if they return unicode literal string, so that
190 under python 2 their result is encoded into utf-8 to avoir a
191 UnicodeEncodeError: ... codec can't encode characters in position ...
192
193 :param fun:
194 :return:
195 """
196 if PY2:
197 def new_fun(*args, **kwargs):
198 return fun(*args, **kwargs).encode("utf-8")
199 return new_fun
200 else:
201 return fun
202
203
204 def python_2_unicode_compatible(klass):
205 """
206 A decorator that defines __unicode__ and __str__ methods under Python 2.
207 Under Python 3 it does nothing.
208
209 To support Python 2 and 3 with a single code base, define a __str__ method
210 returning text and apply this decorator to the class.
211 """
212 if PY2:
213 if '__str__' not in klass.__dict__:
214 raise ValueError("@python_2_unicode_compatible cannot be applied "
215 "to %s because it doesn't define __str__()." %
216 klass.__name__)
217 klass.__unicode__ = klass.__str__
-
E306
Expected 1 blank line before a nested definition, found 0
218 def __str__(self):
219 return self.__unicode__().encode('utf-8')
220
221 klass.__str__ = __str__
222 return klass