Skip to content

Commit da1043d

Browse files
authored
test - handle RuntimeWarnings in unit tests (#802)
* capture and test expected RuntimeWarning * capture and test MAPIE warnings since it is expected * filter (ignore) RuntimeWarning because it is tested by another test * rename test_not_all_label_in_calib() into test_y_pred_shape_in_calib() for clarity * simplify tests in test_classification.py * debug test_sum_proba_to_one_fit : increase LogisticRegression max_iter and ravel simulated y
1 parent 18cca36 commit da1043d

File tree

6 files changed

+43
-38
lines changed

6 files changed

+43
-38
lines changed

mapie/metrics/classification.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
from typing import cast, Union
1+
from typing import Union, cast
22

33
import numpy as np
44
from numpy.typing import ArrayLike, NDArray
55
from sklearn.utils import column_or_1d
66

77
from mapie.utils import (
8-
_check_arrays_length,
9-
_check_array_nan,
108
_check_array_inf,
9+
_check_array_nan,
1110
_check_array_shape_classification,
11+
_check_arrays_length,
1212
_check_nb_sets_sizes,
1313
_check_number_bins,
1414
)

mapie/tests/risk_control/test_risk_control.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def test_compute_precision() -> None:
6666
np.testing.assert_equal(precision, test_precision)
6767

6868

69+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
6970
def test_recall_with_zero_sum_is_equal_nan() -> None:
7071
"""Test compute_recall with nan values"""
7172
y_toy = np.zeros((4, 3))

mapie/tests/test_classification.py

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77
import pandas as pd
88
import pytest
9+
from numpy.typing import ArrayLike, NDArray
910
from sklearn.base import ClassifierMixin
1011
from sklearn.compose import ColumnTransformer
1112
from sklearn.datasets import make_classification
@@ -25,18 +26,17 @@
2526
from sklearn.utils.validation import check_is_fitted
2627
from typing_extensions import TypedDict
2728

28-
from numpy.typing import ArrayLike, NDArray
2929
from mapie.classification import _MapieClassifier
3030
from mapie.conformity_scores import (
31-
LACConformityScore,
32-
RAPSConformityScore,
3331
APSConformityScore,
3432
BaseClassificationScore,
35-
TopKConformityScore,
33+
LACConformityScore,
3634
NaiveConformityScore,
35+
RAPSConformityScore,
36+
TopKConformityScore,
3737
)
38-
from mapie.utils import check_proba_normalized
3938
from mapie.metrics.classification import classification_coverage_score
39+
from mapie.utils import check_proba_normalized
4040

4141
random_state = 42
4242

@@ -1407,6 +1407,7 @@ def test_image_cumulated_scores(X: Dict[str, ArrayLike]) -> None:
14071407
np.testing.assert_allclose(y_ps[:, :, 0], cumclf.y_pred_sets)
14081408

14091409

1410+
@pytest.mark.filterwarnings("ignore:: UserWarning")
14101411
@pytest.mark.parametrize("y_pred_proba", Y_PRED_PROBA_WRONG)
14111412
def test_sum_proba_to_one_fit(y_pred_proba: NDArray) -> None:
14121413
"""
@@ -1501,7 +1502,8 @@ def test_pipeline_compatibility(strategy: str) -> None:
15011502
"x_num": X,
15021503
}
15031504
)
1504-
y = np.random.randint(0, 4, size=(100, 1)) # 3 classes
1505+
# 3 classes
1506+
y = np.random.randint(0, 4, size=(100, 1)).ravel()
15051507
numeric_preprocessor = Pipeline(
15061508
[
15071509
("imputer", SimpleImputer(strategy="mean")),
@@ -1516,7 +1518,7 @@ def test_pipeline_compatibility(strategy: str) -> None:
15161518
("num", numeric_preprocessor, ["x_num"]),
15171519
]
15181520
)
1519-
pipe = make_pipeline(preprocessor, LogisticRegression())
1521+
pipe = make_pipeline(preprocessor, LogisticRegression(max_iter=500))
15201522
pipe.fit(X, y)
15211523
mapie = _MapieClassifier(estimator=pipe, **STRATEGIES[strategy][0])
15221524
mapie.fit(X, y)
@@ -1578,23 +1580,20 @@ def test_error_raps_cv_not_prefit(cv: Union[int, None]) -> None:
15781580
mapie.fit(X_toy, y_toy)
15791581

15801582

1581-
def test_not_all_label_in_calib() -> None:
1583+
def test_y_pred_shape_in_calib() -> None:
15821584
"""
15831585
Test that the true label cumsumed probabilities
15841586
have the correct shape.
15851587
"""
15861588
clf = LogisticRegression()
15871589
clf.fit(X, y)
1588-
indices_remove = np.where(y != 2)
1589-
X_mapie = X[indices_remove]
1590-
y_mapie = y[indices_remove]
15911590
mapie_clf = _MapieClassifier(
15921591
estimator=clf,
15931592
conformity_score=APSConformityScore(),
15941593
cv="prefit",
15951594
random_state=random_state,
15961595
)
1597-
mapie_clf.fit(X_mapie, y_mapie)
1596+
mapie_clf.fit(X, y)
15981597
y_pred, y_pss = mapie_clf.predict(X, alpha=0.5)
15991598
assert y_pred.shape == (len(X),)
16001599
assert y_pss.shape == (len(X), len(np.unique(y)), 1)
@@ -1626,16 +1625,13 @@ def test_n_classes_prefit() -> None:
16261625
"""
16271626
clf = LogisticRegression()
16281627
clf.fit(X, y)
1629-
indices_remove = np.where(y != 2)
1630-
X_mapie = X[indices_remove]
1631-
y_mapie = y[indices_remove]
16321628
mapie_clf = _MapieClassifier(
16331629
estimator=clf,
16341630
conformity_score=APSConformityScore(),
16351631
cv="prefit",
16361632
random_state=random_state,
16371633
)
1638-
mapie_clf.fit(X_mapie, y_mapie)
1634+
mapie_clf.fit(X, y)
16391635
assert mapie_clf.n_classes_ == len(np.unique(y))
16401636

16411637

@@ -1646,16 +1642,13 @@ def test_classes_prefit() -> None:
16461642
"""
16471643
clf = LogisticRegression()
16481644
clf.fit(X, y)
1649-
indices_remove = np.where(y != 2)
1650-
X_mapie = X[indices_remove]
1651-
y_mapie = y[indices_remove]
16521645
mapie_clf = _MapieClassifier(
16531646
estimator=clf,
16541647
conformity_score=APSConformityScore(),
16551648
cv="prefit",
16561649
random_state=random_state,
16571650
)
1658-
mapie_clf.fit(X_mapie, y_mapie)
1651+
mapie_clf.fit(X, y)
16591652
assert (mapie_clf.classes_ == np.unique(y)).all()
16601653

16611654

@@ -1666,13 +1659,10 @@ def test_classes_encoder_same_than_model() -> None:
16661659
"""
16671660
clf = LogisticRegression()
16681661
clf.fit(X, y)
1669-
indices_remove = np.where(y != 2)
1670-
X_mapie = X[indices_remove]
1671-
y_mapie = y[indices_remove]
16721662
mapie_clf = _MapieClassifier(
16731663
estimator=clf, conformity_score=APSConformityScore(), cv="prefit"
16741664
)
1675-
mapie_clf.fit(X_mapie, y_mapie)
1665+
mapie_clf.fit(X, y)
16761666
assert (mapie_clf.label_encoder_.classes_ == np.unique(y)).all()
16771667

16781668

mapie/tests/test_metrics.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
import numpy as np
88
import pytest
99
from numpy.random import RandomState
10+
from numpy.typing import ArrayLike, NDArray
1011
from typing_extensions import TypedDict
1112

12-
from numpy.typing import ArrayLike, NDArray
13-
from mapie.metrics.calibration import spiegelhalter_p_value
1413
from mapie.metrics.calibration import (
1514
add_jitter,
1615
cumulative_differences,
@@ -23,23 +22,24 @@
2322
kuiper_statistic,
2423
length_scale,
2524
sort_xy_by_y,
25+
spiegelhalter_p_value,
2626
spiegelhalter_statistic,
2727
top_label_ece,
2828
)
2929
from mapie.metrics.classification import (
30-
classification_mean_width_score,
3130
classification_coverage_score,
31+
classification_mean_width_score,
3232
classification_ssc,
3333
classification_ssc_score,
3434
)
3535
from mapie.metrics.regression import (
36-
regression_mean_width_score,
36+
coverage_width_based,
37+
hsic,
3738
regression_coverage_score,
39+
regression_mean_width_score,
40+
regression_mwi_score,
3841
regression_ssc,
3942
regression_ssc_score,
40-
hsic,
41-
coverage_width_based,
42-
regression_mwi_score,
4343
)
4444

4545
y_toy = np.array([5, 7.5, 9.5, 10.5, 12.5])
@@ -263,8 +263,14 @@ def test_classification_same_length() -> None:
263263

264264
def test_classification_valid_input_shape() -> None:
265265
"""Test that valid inputs shape raise no error."""
266-
classification_ssc(y_true_class, y_pred_set_2alphas)
267-
classification_ssc_score(y_true_class, y_pred_set_2alphas)
266+
with pytest.warns(
267+
RuntimeWarning, match=r".*empty slice.*|.*invalid value encountered in divide.*"
268+
):
269+
classification_ssc(y_true_class, y_pred_set_2alphas)
270+
with pytest.warns(
271+
RuntimeWarning, match=r".*empty slice.*|.*invalid value encountered in divide.*"
272+
):
273+
classification_ssc_score(y_true_class, y_pred_set_2alphas)
268274
classification_mean_width_score(y_pred_set_2alphas)
269275

270276

@@ -410,39 +416,45 @@ def test_invalid_splits_classification_ssc_score(num_bins: int) -> None:
410416

411417

412418
@pytest.mark.parametrize("num_bins", [3, 2, None])
419+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
413420
def test_valid_splits_classification_ssc(num_bins: int) -> None:
414-
"""Test that valid number of bins for ssc raise no error."""
421+
"""Test that valid number of bins for ssc raise no ValueError."""
415422
classification_ssc(y_true_class, y_pred_set_2alphas, num_bins=num_bins)
416423

417424

418425
@pytest.mark.parametrize("num_bins", [3, 2, None])
426+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
419427
def test_valid_splits_classification_ssc_score(num_bins: int) -> None:
420-
"""Test that valid number of bins for ssc raise no error."""
428+
"""Test that valid number of bins for ssc raise no ValueError."""
421429
classification_ssc_score(y_true_class, y_pred_set_2alphas, num_bins=num_bins)
422430

423431

424432
@pytest.mark.parametrize("params", [*SSC_CLASSIF])
433+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
425434
def test_classification_ssc_return_shape(params: str) -> None:
426435
"""Test that the arrays returned by ssc metrics have the correct shape."""
427436
cond_cov = classification_ssc(y_true_class, **SSC_CLASSIF[params])
428437
assert cond_cov.shape == SSC_CLASSIF_COVERAGES[params].shape
429438

430439

431440
@pytest.mark.parametrize("params", [*SSC_CLASSIF])
441+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
432442
def test_classification_ssc_score_return_shape(params: str) -> None:
433443
"""Test that the arrays returned by ssc metrics have the correct shape."""
434444
cond_cov_min = classification_ssc_score(y_true_class, **SSC_CLASSIF[params])
435445
assert cond_cov_min.shape == SSC_CLASSIF_COVERAGES_SCORE[params].shape
436446

437447

438448
@pytest.mark.parametrize("params", [*SSC_CLASSIF])
449+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
439450
def test_classification_ssc_coverage_values(params: str) -> None:
440451
"""Test that the conditional coverage values returned are correct."""
441452
cond_cov = classification_ssc(y_true_class, **SSC_CLASSIF[params])
442453
np.testing.assert_allclose(cond_cov, SSC_CLASSIF_COVERAGES[params])
443454

444455

445456
@pytest.mark.parametrize("params", [*SSC_CLASSIF])
457+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
446458
def test_classification_ssc_score_coverage_values(params: str) -> None:
447459
"""Test that the conditional coverage values returned are correct."""
448460
cond_cov_min = classification_ssc_score(y_true_class, **SSC_CLASSIF[params])

mapie/tests/test_regression.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ def test_results_prefit() -> None:
721721
np.testing.assert_allclose(coverage, COVERAGES["prefit"], rtol=1e-2)
722722

723723

724+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
724725
def test_not_enough_resamplings() -> None:
725726
"""
726727
Test that a warning is raised if at least one conformity score is nan.

mapie/tests/test_time_series_regression.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ def test_results_prefit() -> None:
303303
np.testing.assert_allclose(coverage, COVERAGES["prefit"], rtol=1e-2)
304304

305305

306+
@pytest.mark.filterwarnings("ignore:: RuntimeWarning")
306307
def test_not_enough_resamplings() -> None:
307308
"""Test that a warning is raised if at least one residual is nan."""
308309
with pytest.warns(UserWarning, match=r"WARNING: at least one point of*"):

0 commit comments

Comments
 (0)