Skip to content

Commit d870bb8

Browse files
committed
FIX: coverare issue by changing inheritance
1 parent 0aa8b32 commit d870bb8

File tree

2 files changed

+26
-261
lines changed

2 files changed

+26
-261
lines changed
Lines changed: 9 additions & 258 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
from abc import ABCMeta, abstractmethod
2-
from typing import Tuple
32

4-
import numpy as np
53
from sklearn.utils import deprecated
64

7-
from mapie.conformity_scores.interface import BaseConformityScore
8-
from mapie.estimator.regressor import EnsembleRegressor
9-
from mapie._compatibility import np_nanquantile
5+
from mapie.conformity_scores.regression import BaseConformityScore
106
from mapie._machine_precision import EPSILON
117
from mapie._typing import NDArray
128

@@ -84,14 +80,19 @@ def get_signed_conformity_scores(
8480
Signed conformity scores.
8581
"""
8682

83+
@abstractmethod
8784
def get_conformity_scores(
8885
self,
8986
y: NDArray,
9087
y_pred: NDArray,
9188
**kwargs
9289
) -> NDArray:
9390
"""
94-
Get the conformity score considering the symmetrical property if so.
91+
Placeholder for ``get_conformity_scores``.
92+
Subclasses should implement this method!
93+
94+
Compute the sample conformity scores given the predicted and
95+
observed targets.
9596
9697
Parameters
9798
----------
@@ -106,63 +107,6 @@ def get_conformity_scores(
106107
NDArray of shape (n_samples,)
107108
Conformity scores.
108109
"""
109-
conformity_scores = \
110-
self.get_signed_conformity_scores(y, y_pred, **kwargs)
111-
if self.consistency_check:
112-
self.check_consistency(y, y_pred, conformity_scores, **kwargs)
113-
if self.sym:
114-
conformity_scores = np.abs(conformity_scores)
115-
return conformity_scores
116-
117-
def check_consistency(
118-
self,
119-
y: NDArray,
120-
y_pred: NDArray,
121-
conformity_scores: NDArray,
122-
**kwargs
123-
) -> None:
124-
"""
125-
Check consistency between the following methods:
126-
``get_estimation_distribution`` and ``get_signed_conformity_scores``
127-
128-
The following equality should be verified:
129-
``self.get_estimation_distribution(
130-
y_pred, self.get_conformity_scores(y, y_pred, **kwargs), **kwargs
131-
) == y``
132-
133-
Parameters
134-
----------
135-
y: NDArray of shape (n_samples,)
136-
Observed target values.
137-
138-
y_pred: NDArray of shape (n_samples,)
139-
Predicted target values.
140-
141-
conformity_scores: NDArray of shape (n_samples,)
142-
Conformity scores.
143-
144-
Raises
145-
------
146-
ValueError
147-
If the two methods are not consistent.
148-
"""
149-
score_distribution = self.get_estimation_distribution(
150-
y_pred, conformity_scores, **kwargs
151-
)
152-
abs_conformity_scores = np.abs(np.subtract(score_distribution, y))
153-
max_conf_score = np.max(abs_conformity_scores)
154-
if max_conf_score > self.eps:
155-
raise ValueError(
156-
"The two functions get_conformity_scores and "
157-
"get_estimation_distribution of the BaseRegressionScore class "
158-
"are not consistent. "
159-
"The following equation must be verified: "
160-
"self.get_estimation_distribution(y_pred, "
161-
"self.get_conformity_scores(y, y_pred)) == y. "
162-
f"The maximum conformity score is {max_conf_score}. "
163-
"The eps attribute may need to be increased if you are "
164-
"sure that the two methods are consistent."
165-
)
166110

167111
@abstractmethod
168112
def get_estimation_distribution(
@@ -192,199 +136,7 @@ def get_estimation_distribution(
192136
Observed values.
193137
"""
194138

195-
@staticmethod
196-
def _beta_optimize(
197-
alpha_np: NDArray,
198-
upper_bounds: NDArray,
199-
lower_bounds: NDArray,
200-
) -> NDArray:
201-
"""
202-
Minimize the width of the PIs, for a given difference of quantiles.
203-
204-
Parameters
205-
----------
206-
alpha_np: NDArray
207-
The quantiles to compute.
208-
209-
upper_bounds: NDArray of shape (n_samples,)
210-
The array of upper values.
211-
212-
lower_bounds: NDArray of shape (n_samples,)
213-
The array of lower values.
214-
215-
Returns
216-
-------
217-
NDArray of shape (n_samples,)
218-
Array of betas minimizing the differences
219-
``(1-alpha+beta)-quantile - beta-quantile``.
220-
"""
221-
beta_np = np.full(
222-
shape=(len(lower_bounds), len(alpha_np)),
223-
fill_value=np.nan,
224-
dtype=float,
225-
)
226-
227-
for ind_alpha, _alpha in enumerate(alpha_np):
228-
betas = np.linspace(
229-
_alpha / (len(lower_bounds) + 1),
230-
_alpha,
231-
num=len(lower_bounds),
232-
endpoint=True,
233-
)
234-
one_alpha_beta = np_nanquantile(
235-
upper_bounds.astype(float),
236-
1 - _alpha + betas,
237-
axis=1,
238-
method="higher",
239-
)
240-
beta = np_nanquantile(
241-
lower_bounds.astype(float),
242-
betas,
243-
axis=1,
244-
method="lower",
245-
)
246-
beta_np[:, ind_alpha] = betas[
247-
np.argmin(one_alpha_beta - beta, axis=0)
248-
]
249-
250-
return beta_np
251-
252-
def get_bounds(
253-
self,
254-
X: NDArray,
255-
alpha_np: NDArray,
256-
estimator: EnsembleRegressor,
257-
conformity_scores: NDArray,
258-
ensemble: bool = False,
259-
method: str = 'base',
260-
optimize_beta: bool = False,
261-
allow_infinite_bounds: bool = False
262-
) -> Tuple[NDArray, NDArray, NDArray]:
263-
"""
264-
Compute bounds of the prediction intervals from the observed values,
265-
the estimator of type ``EnsembleRegressor`` and the conformity scores.
266-
267-
Parameters
268-
----------
269-
X: NDArray of shape (n_samples, n_features)
270-
Observed feature values.
271-
272-
alpha_np: NDArray of shape (n_alpha,)
273-
NDArray of floats between ``0`` and ``1``, represents the
274-
uncertainty of the confidence interval.
275-
276-
estimator: EnsembleRegressor
277-
Estimator that is fitted to predict y from X.
278-
279-
conformity_scores: NDArray of shape (n_samples,)
280-
Conformity scores.
281-
282-
ensemble: bool
283-
Boolean determining whether the predictions are ensembled or not.
284-
285-
By default ``False``.
286-
287-
method: str
288-
Method to choose for prediction interval estimates.
289-
The ``"plus"`` method implies that the quantile is calculated
290-
after estimating the bounds, whereas the other methods
291-
(among the ``"naive"``, ``"base"`` or ``"minmax"`` methods,
292-
for example) do the opposite.
293-
294-
By default ``base``.
295-
296-
optimize_beta: bool
297-
Whether to optimize the PIs' width or not.
298-
299-
By default ``False``.
300-
301-
allow_infinite_bounds: bool
302-
Allow infinite prediction intervals to be produced.
303-
304-
By default ``False``.
305-
306-
Returns
307-
-------
308-
Tuple[NDArray, NDArray, NDArray]
309-
- The predictions itself. (y_pred) of shape (n_samples,).
310-
- The lower bounds of the prediction intervals of shape
311-
(n_samples, n_alpha).
312-
- The upper bounds of the prediction intervals of shape
313-
(n_samples, n_alpha).
314-
315-
Raises
316-
------
317-
ValueError
318-
If beta optimisation with symmetrical conformity score function.
319-
"""
320-
if self.sym and optimize_beta:
321-
raise ValueError(
322-
"Beta optimisation cannot be used with " +
323-
"symmetrical conformity score function."
324-
)
325-
326-
y_pred, y_pred_low, y_pred_up = estimator.predict(X, ensemble)
327-
signed = -1 if self.sym else 1
328-
329-
if optimize_beta:
330-
beta_np = self._beta_optimize(
331-
alpha_np,
332-
conformity_scores.reshape(1, -1),
333-
conformity_scores.reshape(1, -1),
334-
)
335-
else:
336-
beta_np = alpha_np / 2
337-
338-
if method == "plus":
339-
alpha_low = alpha_np if self.sym else beta_np
340-
alpha_up = 1 - alpha_np if self.sym else 1 - alpha_np + beta_np
341-
342-
conformity_scores_low = self.get_estimation_distribution(
343-
y_pred_low, signed * conformity_scores, X=X
344-
)
345-
conformity_scores_up = self.get_estimation_distribution(
346-
y_pred_up, conformity_scores, X=X
347-
)
348-
bound_low = self.get_quantile(
349-
conformity_scores_low, alpha_low, axis=1, reversed=True,
350-
unbounded=allow_infinite_bounds
351-
)
352-
bound_up = self.get_quantile(
353-
conformity_scores_up, alpha_up, axis=1,
354-
unbounded=allow_infinite_bounds
355-
)
356-
357-
else:
358-
if self.sym:
359-
alpha_ref = 1 - alpha_np
360-
quantile_ref = self.get_quantile(
361-
conformity_scores[..., np.newaxis], alpha_ref, axis=0
362-
)
363-
quantile_low, quantile_up = -quantile_ref, quantile_ref
364-
365-
else:
366-
alpha_low, alpha_up = beta_np, 1 - alpha_np + beta_np
367-
368-
quantile_low = self.get_quantile(
369-
conformity_scores[..., np.newaxis],
370-
alpha_low, axis=0, reversed=True,
371-
unbounded=allow_infinite_bounds
372-
)
373-
quantile_up = self.get_quantile(
374-
conformity_scores[..., np.newaxis],
375-
alpha_up, axis=0,
376-
unbounded=allow_infinite_bounds
377-
)
378-
379-
bound_low = self.get_estimation_distribution(
380-
y_pred_low, quantile_low, X=X
381-
)
382-
bound_up = self.get_estimation_distribution(
383-
y_pred_up, quantile_up, X=X
384-
)
385-
386-
return y_pred, bound_low, bound_up
387-
139+
@abstractmethod
388140
def predict_set(
389141
self,
390142
X: NDArray,
@@ -408,7 +160,6 @@ def predict_set(
408160
409161
Returns:
410162
--------
411-
The output structure depend on the ``get_bounds`` method.
163+
The output structure depend on the subclass.
412164
The prediction sets for each sample and each alpha level.
413165
"""
414-
return self.get_bounds(X=X, alpha_np=alpha_np, **kwargs)

mapie/tests/test_common.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def __init__(self) -> None:
255255
def get_signed_conformity_scores(
256256
self, y: ArrayLike, y_pred: ArrayLike, **kwargs
257257
) -> NDArray:
258-
return np.subtract(y, y_pred)
258+
pass
259259

260260
def get_estimation_distribution(
261261
self, y_pred: ArrayLike, conformity_scores: ArrayLike, **kwargs
@@ -265,9 +265,23 @@ def get_estimation_distribution(
265265
conformity scores to make the estimated distribution
266266
inconsistent with the conformity score.
267267
"""
268-
return np.add(y_pred, conformity_scores) + 1
268+
pass
269269

270-
DummyConformityScore()
270+
def get_conformity_scores(
271+
self, y: ArrayLike, y_pred: ArrayLike, **kwargs
272+
) -> NDArray:
273+
pass
274+
275+
def predict_set(
276+
self, y_pred: ArrayLike, alpha: float, **kwargs
277+
) -> Tuple[NDArray, NDArray]:
278+
pass
279+
280+
dcs = DummyConformityScore()
281+
dcs.get_signed_conformity_scores(y_toy, y_toy)
282+
dcs.get_estimation_distribution(y_toy, y_toy)
283+
dcs.get_conformity_scores(y_toy, y_toy)
284+
dcs.predict_set(y_toy, 0.5)
271285

272286

273287
def test_warning_when_import_from_old_get_true_label_position():

0 commit comments

Comments
 (0)