Skip to content

Commit 02a652e

Browse files
Valentin-Laurentjawadhussein462
authored andcommitted
ENH: V1 cqr implmentation (#576)
ENH: V1 cqr implmentation
1 parent f718def commit 02a652e

File tree

3 files changed

+91
-17
lines changed

3 files changed

+91
-17
lines changed

mapie_v1/_utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ def check_if_X_y_different_from_fit(
5454

5555
def make_intervals_single_if_single_alpha(
5656
intervals: NDArray,
57-
alphas: List[float]
57+
alphas: Union[float, List[float]]
5858
) -> NDArray:
59-
if len(alphas) == 1:
59+
if isinstance(alphas, float):
60+
return intervals[:, :, 0]
61+
if isinstance(alphas, list) and len(alphas) == 1:
6062
return intervals[:, :, 0]
6163
return intervals
6264

mapie_v1/integration_tests/tests/test_regression.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from sklearn.ensemble import RandomForestRegressor
1111
from sklearn.linear_model import QuantileRegressor
1212
from sklearn.ensemble import GradientBoostingRegressor
13+
from sklearn.model_selection import train_test_split
1314

1415
from mapie.subsample import Subsample
1516
from mapie._typing import ArrayLike
@@ -306,22 +307,29 @@ def test_intervals_and_predictions_exact_equality_jackknife(params_jackknife):
306307
)
307308
gbr_models.append(estimator_)
308309

310+
sample_weight_train = train_test_split(
311+
X,
312+
y,
313+
sample_weight,
314+
test_size=0.4,
315+
random_state=RANDOM_STATE
316+
)[-2]
309317

310318
params_test_cases_quantile = [
311319
{
312320
"v0": {
313321
"alpha": 0.2,
314322
"cv": "split",
315323
"method": "quantile",
316-
"calib_size": 0.3,
324+
"calib_size": 0.4,
317325
"sample_weight": sample_weight,
318326
"random_state": RANDOM_STATE,
319327
},
320328
"v1": {
321329
"confidence_level": 0.8,
322330
"prefit": False,
323-
"test_size": 0.3,
324-
"fit_params": {"sample_weight": sample_weight},
331+
"test_size": 0.4,
332+
"fit_params": {"sample_weight": sample_weight_train},
325333
"random_state": RANDOM_STATE,
326334
},
327335
},
@@ -330,15 +338,15 @@ def test_intervals_and_predictions_exact_equality_jackknife(params_jackknife):
330338
"estimator": gbr_models,
331339
"cv": "prefit",
332340
"method": "quantile",
333-
"calib_size": 0.3,
341+
"calib_size": 0.2,
334342
"sample_weight": sample_weight,
335343
"optimize_beta": True,
336344
"random_state": RANDOM_STATE,
337345
},
338346
"v1": {
339347
"estimator": gbr_models,
340348
"prefit": True,
341-
"test_size": 0.3,
349+
"test_size": 0.2,
342350
"fit_params": {"sample_weight": sample_weight},
343351
"minimize_interval_width": True,
344352
"random_state": RANDOM_STATE,
@@ -418,12 +426,16 @@ def compare_model_predictions_and_intervals(
418426
v1_params: Dict = {},
419427
prefit: bool = False,
420428
test_size: Optional[float] = None,
429+
sample_weight: Optional[ArrayLike] = None,
421430
random_state: int = 42,
422431
) -> None:
423432

424433
if test_size is not None:
425434
X_train, X_conf, y_train, y_conf = train_test_split_shuffle(
426-
X, y, test_size=test_size, random_state=random_state
435+
X,
436+
y,
437+
test_size=test_size,
438+
random_state=random_state,
427439
)
428440
else:
429441
X_train, X_conf, y_train, y_conf = X, X, y, y

mapie_v1/regression.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
from typing_extensions import Self
44

55
import numpy as np
6-
from sklearn.linear_model import LinearRegression, QuantileRegressor
6+
from sklearn.linear_model import LinearRegression
77
from sklearn.base import RegressorMixin, clone
88
from sklearn.model_selection import BaseCrossValidator
9+
from sklearn.pipeline import Pipeline
910

1011
from mapie.subsample import Subsample
1112
from mapie._typing import ArrayLike, NDArray
1213
from mapie.conformity_scores import BaseRegressionScore
13-
from mapie.regression import MapieRegressor
14+
from mapie.regression import MapieRegressor, MapieQuantileRegressor
1415
from mapie.utils import check_estimator_fit_predict
1516
from mapie_v1.conformity_scores._utils import (
1617
check_and_select_regression_conformity_score,
@@ -904,12 +905,29 @@ class ConformalizedQuantileRegressor:
904905

905906
def __init__(
906907
self,
907-
estimator: RegressorMixin = QuantileRegressor(),
908-
confidence_level: Union[float, List[float]] = 0.9,
909-
conformity_score: Union[str, BaseRegressionScore] = "absolute",
910-
random_state: Optional[Union[int, np.random.RandomState]] = None,
908+
estimator: Optional[
909+
Union[
910+
RegressorMixin,
911+
Pipeline,
912+
List[Union[RegressorMixin, Pipeline]]
913+
]
914+
] = None,
915+
confidence_level: float = 0.9,
916+
prefit: bool = False,
911917
) -> None:
912-
pass
918+
919+
self._alpha = 1 - confidence_level
920+
self.prefit = prefit
921+
922+
cv: str = "prefit" if prefit else "split"
923+
self._mapie_quantile_regressor = MapieQuantileRegressor(
924+
estimator=estimator,
925+
method="quantile",
926+
cv=cv,
927+
alpha=self._alpha,
928+
)
929+
930+
self._sample_weight: Optional[NDArray] = None
913931

914932
def fit(
915933
self,
@@ -937,6 +955,27 @@ def fit(
937955
Self
938956
The fitted ConformalizedQuantileRegressor instance.
939957
"""
958+
959+
if self.prefit:
960+
raise ValueError(
961+
"The estimators are already fitted, the .fit() method should"
962+
" not be called with prefit=True."
963+
)
964+
965+
if fit_params:
966+
fit_params_ = copy.deepcopy(fit_params)
967+
self._sample_weight = fit_params_.pop("sample_weight", None)
968+
else:
969+
fit_params_ = {}
970+
971+
self._mapie_quantile_regressor._initialize_fit_conformalize()
972+
self._mapie_quantile_regressor._fit_estimators(
973+
X=X_train,
974+
y=y_train,
975+
sample_weight=self._sample_weight,
976+
**fit_params_,
977+
)
978+
940979
return self
941980

942981
def conformalize(
@@ -969,6 +1008,14 @@ def conformalize(
9691008
The ConformalizedQuantileRegressor instance with calibrated
9701009
prediction intervals.
9711010
"""
1011+
self.predict_params = predict_params if predict_params else {}
1012+
1013+
self._mapie_quantile_regressor.conformalize(
1014+
X_conf,
1015+
y_conf,
1016+
**self.predict_params
1017+
)
1018+
9721019
return self
9731020

9741021
def predict_set(
@@ -1007,7 +1054,18 @@ def predict_set(
10071054
Prediction intervals with shape `(n_samples, 2)`, with lower
10081055
and upper bounds for each sample.
10091056
"""
1010-
return np.ndarray(0)
1057+
_, intervals = self._mapie_quantile_regressor.predict(
1058+
X,
1059+
optimize_beta=minimize_interval_width,
1060+
allow_infinite_bounds=allow_infinite_bounds,
1061+
symmetry=symmetric_intervals,
1062+
**self.predict_params
1063+
)
1064+
1065+
return make_intervals_single_if_single_alpha(
1066+
intervals,
1067+
self._alpha
1068+
)
10111069

10121070
def predict(
10131071
self,
@@ -1026,7 +1084,9 @@ def predict(
10261084
NDArray
10271085
Array of point predictions with shape `(n_samples,)`.
10281086
"""
1029-
return np.ndarray(0)
1087+
estimator = self._mapie_quantile_regressor
1088+
predictions, _ = estimator.predict(X, **self.predict_params)
1089+
return predictions
10301090

10311091

10321092
class GibbsConformalRegressor:

0 commit comments

Comments
 (0)