Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2@file 

3@brief Implements a slightly different 

4version of the :epkg:`sklearn:compose:TransformedTargetRegressor`. 

5""" 

6from sklearn.base import BaseEstimator, RegressorMixin, ClassifierMixin, clone 

7from sklearn.exceptions import NotFittedError 

8from sklearn.linear_model import LinearRegression, LogisticRegression 

9from sklearn.metrics import r2_score, accuracy_score 

10from .sklearn_transform_inv import BaseReciprocalTransformer 

11from .sklearn_transform_inv_fct import FunctionReciprocalTransformer, PermutationReciprocalTransformer 

12 

13 

14def _common_get_transform(transformer, is_regression): 

15 if isinstance(transformer, str): 

16 closest = is_regression 

17 if transformer == 'permute': 

18 return PermutationReciprocalTransformer(closest=closest) 

19 else: 

20 return FunctionReciprocalTransformer(transformer) 

21 elif isinstance(transformer, BaseReciprocalTransformer): 

22 return clone(transformer) 

23 raise TypeError("Transformer {} must be a string or on object of type " 

24 "BaseReciprocalTransformer.".format(type(transformer))) 

25 

26 

27class TransformedTargetRegressor2(BaseEstimator, RegressorMixin): 

28 """ 

29 Meta-estimator to regress on a transformed target. 

30 Useful for applying a non-linear transformation in regression 

31 problems. 

32 

33 Parameters 

34 ---------- 

35 regressor : object, default=LinearRegression() 

36 Regressor object such as derived from ``RegressorMixin``. This 

37 regressor will automatically be cloned each time prior to fitting. 

38 transformer : str or object of type @see cl BaseReciprocalTransformer 

39 

40 Attributes 

41 ---------- 

42 regressor_ : object 

43 Fitted regressor. 

44 transformer_ : object 

45 Transformer used in ``fit`` and ``predict``. 

46 

47 Examples 

48 -------- 

49 

50 .. runpython:: 

51 :showcode: 

52 

53 import numpy 

54 from sklearn.linear_model import LinearRegression 

55 from mlinsights.mlmodel import TransformedTargetRegressor2 

56 

57 tt = TransformedTargetRegressor2(regressor=LinearRegression(), 

58 transformer='log') 

59 X = numpy.arange(4).reshape(-1, 1) 

60 y = numpy.exp(2 * X).ravel() 

61 print(tt.fit(X, y)) 

62 print(tt.score(X, y)) 

63 print(tt.regressor_.coef_) 

64 

65 See notebook :ref:`sklearntransformedtargetrst` for a more complete example. 

66 """ 

67 

68 def __init__(self, regressor=None, transformer=None): 

69 self.regressor = regressor 

70 self.transformer = transformer 

71 

72 def fit(self, X, y, sample_weight=None): 

73 """ 

74 Fits the model according to the given training data. 

75 

76 :param X: {array-like, sparse matrix}, shape (n_samples, n_features) 

77 Training vector, where n_samples is the number of samples and 

78 n_features is the number of features. 

79 :param y: array-like, shape (n_samples,) 

80 Target values. 

81 :param sample_weight: array-like, shape (n_samples,) optional 

82 Array of weights that are assigned to individual samples. 

83 If not provided, then each sample is given unit weight. 

84 :return: self, object 

85 """ 

86 self.transformer_ = _common_get_transform(self.transformer, True) 

87 self.transformer_.fit(X, y, sample_weight=sample_weight) 

88 X_trans, y_trans = self.transformer_.transform(X, y) 

89 

90 if self.regressor is None: 

91 self.regressor_ = LinearRegression() 

92 else: 

93 self.regressor_ = clone(self.regressor) 

94 

95 if sample_weight is None: 

96 self.regressor_.fit(X_trans, y_trans) 

97 else: 

98 self.regressor_.fit(X_trans, y_trans, sample_weight=sample_weight) 

99 

100 return self 

101 

102 def predict(self, X): 

103 """ 

104 Predicts using the base regressor, applying inverse. 

105 

106 :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) 

107 Samples. 

108 :return: y_hat : array, shape = (n_samples,) 

109 Predicted values. 

110 """ 

111 if not hasattr(self, 'regressor_'): 

112 raise NotFittedError( # pragma: no cover 

113 "This instance {} is not fitted yet. Call 'fit' with " 

114 "appropriate arguments before using this method.".format( 

115 type(self))) 

116 X_trans, _ = self.transformer_.transform(X, None) 

117 pred = self.regressor_.predict(X_trans) 

118 

119 inv = self.transformer_.get_fct_inv() 

120 _, pred_inv = inv.transform(X_trans, pred) 

121 return pred_inv 

122 

123 def score(self, X, y, sample_weight=None): 

124 """ 

125 Scores the model with 

126 :epkg:`sklearn:metrics:r2_score`. 

127 """ 

128 yp = self.predict(X) 

129 return r2_score(y, yp, sample_weight=sample_weight) 

130 

131 def _more_tags(self): 

132 return {'poor_score': True, 'no_validation': True} 

133 

134 

135class TransformedTargetClassifier2(BaseEstimator, ClassifierMixin): 

136 """ 

137 Meta-estimator to classify on a transformed target. 

138 Useful for applying permutation transformation in classification 

139 problems. 

140 

141 Parameters 

142 ---------- 

143 classifier : object, default=LogisticRegression() 

144 Classifier object such as derived from ``ClassifierMixin``. This 

145 classifier will automatically be cloned each time prior to fitting. 

146 transformer : str or object of type @see cl BaseReciprocalTransformer 

147 

148 Attributes 

149 ---------- 

150 classifier_ : object 

151 Fitted classifier. 

152 transformer_ : object 

153 Transformer used in ``fit``, ``predict``, ``decision_function``, 

154 ``predict_proba``. 

155 

156 Examples 

157 -------- 

158 

159 .. runpython:: 

160 :showcode: 

161 

162 import numpy 

163 from sklearn.linear_model import LogisticRegression 

164 from mlinsights.mlmodel import TransformedTargetClassifier2 

165 

166 tt = TransformedTargetClassifier2(classifier=LogisticRegression(), 

167 transformer='permute') 

168 X = numpy.arange(4).reshape(-1, 1) 

169 y = numpy.array([0, 1, 0, 1]) 

170 print(tt.fit(X, y)) 

171 print(tt.score(X, y)) 

172 print(tt.classifier_.coef_) 

173 

174 See notebook :ref:`sklearntransformedtargetrst` for a more complete example. 

175 """ 

176 

177 def __init__(self, classifier=None, transformer=None): 

178 self.classifier = classifier 

179 self.transformer = transformer 

180 

181 def fit(self, X, y, sample_weight=None): 

182 """ 

183 Fits the model according to the given training data. 

184 

185 :param X: {array-like, sparse matrix}, shape (n_samples, n_features) 

186 Training vector, where n_samples is the number of samples and 

187 n_features is the number of features. 

188 :param y: array-like, shape (n_samples,) 

189 Target values. 

190 :param sample_weight: array-like, shape (n_samples,) optional 

191 Array of weights that are assigned to individual samples. 

192 If not provided, then each sample is given unit weight. 

193 :return: self, object 

194 """ 

195 self.transformer_ = _common_get_transform(self.transformer, False) 

196 self.transformer_.fit(X, y, sample_weight=sample_weight) 

197 X_trans, y_trans = self.transformer_.transform(X, y) 

198 

199 if self.classifier is None: 

200 self.classifier_ = LogisticRegression() 

201 else: 

202 self.classifier_ = clone(self.classifier) 

203 

204 if sample_weight is None: 

205 self.classifier_.fit(X_trans, y_trans) 

206 else: 

207 self.classifier_.fit(X_trans, y_trans, sample_weight=sample_weight) 

208 

209 return self 

210 

211 def _check_is_fitted(self): 

212 if not hasattr(self, 'classifier_'): 

213 raise NotFittedError( 

214 "This instance {} is not fitted yet. Call 'fit' with " 

215 "appropriate arguments before using this method.".format( 

216 type(self))) 

217 

218 @property 

219 def classes_(self): 

220 """ 

221 Returns the classes. 

222 """ 

223 self._check_is_fitted() 

224 inv = self.transformer_.get_fct_inv() 

225 _, pred_inv = inv.transform(None, self.classifier_.classes_) 

226 return pred_inv 

227 

228 def _apply(self, X, method): 

229 """ 

230 Calls *predict*, *predict_proba* or *decision_function* 

231 using the base classifier, applying inverse. 

232 

233 :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) 

234 Samples. 

235 :return: y_hat, array, shape = (n_samples,) 

236 Predicted values. 

237 """ 

238 self._check_is_fitted() 

239 if not hasattr(self.classifier_, method): 

240 raise RuntimeError("Unable to find method '{}' in model {}.".format( 

241 method, type(self.classifier_))) 

242 meth = getattr(self.classifier_, method) 

243 X_trans, _ = self.transformer_.transform(X, None) 

244 pred = meth(X_trans) 

245 inv = self.transformer_.get_fct_inv() 

246 _, pred_inv = inv.transform(X_trans, pred) 

247 return pred_inv 

248 

249 def predict(self, X): 

250 """ 

251 Predicts using the base classifier, applying inverse. 

252 

253 :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) 

254 Samples. 

255 :return: y_hat, array, shape = (n_samples,) 

256 Predicted values. 

257 """ 

258 return self._apply(X, 'predict') 

259 

260 def predict_proba(self, X): 

261 """ 

262 Predicts using the base classifier, applying inverse. 

263 

264 :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) 

265 Samples. 

266 :return: predict probabilities, array, shape = (n_samples, n_classes) 

267 Predicted values. 

268 """ 

269 return self._apply(X, 'predict_proba') 

270 

271 def decision_function(self, X): 

272 """ 

273 Predicts using the base classifier, applying inverse. 

274 

275 :param X: {array-like, sparse matrix}, shape = (n_samples, n_features) 

276 Samples. 

277 :return: raw score : array, shape = (n_samples, ?) 

278 """ 

279 return self._apply(X, 'decision_function') 

280 

281 def score(self, X, y, sample_weight=None): 

282 """ 

283 Scores the model with 

284 :epkg:`sklearn:metrics:accuracy_score`. 

285 """ 

286 yp = self.predict(X) 

287 return accuracy_score(y, yp, sample_weight=sample_weight) 

288 

289 def _more_tags(self): 

290 return {'poor_score': True, 'no_validation': True}