Coverage for qdscreen/compat.py: 17%

81 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-17 11:02 +0000

1import sys 

2 

3PY2 = sys.version_info < (3,) 

4if PY2: 4 ↛ 6line 4 didn't jump to line 6, because the condition on line 4 was never true

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 

19else: 

20 # Normal imports on python 3 

21 try: 

22 from sklearn.feature_selection import SelectorMixin 

23 except ImportError: # older sklearn 

24 from sklearn.feature_selection.base import SelectorMixin 

25 

26 from sklearn.base import BaseEstimator 

27 

28 

29try: 

30 BaseEstimator._get_tags 

31except 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 

71try: 

72 BaseEstimator._validate_data 

73except 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 

148try: 

149 BaseEstimator._check_n_features 

150except 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 

184PY2 = sys.version_info < (3, 0) 

185 

186 

187def 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: 196 ↛ 197line 196 didn't jump to line 197, because the condition on line 196 was never true

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 

204def 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__ 

218 def __str__(self): 

219 return self.__unicode__().encode('utf-8') 

220 

221 klass.__str__ = __str__ 

222 return klass