Skip to content

Commit 0885712

Browse files
AnneBeyerogrisel
andauthored
MNT Rename DetCurveDisplay parameters estimator_name and **kwargs (scikit-learn#34443)
Co-authored-by: Olivier Grisel <olivier.grisel@ensta.org>
1 parent 6b9e392 commit 0885712

5 files changed

Lines changed: 97 additions & 23 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- In :class:`metrics.DetCurveDisplay` the `estimator_name` parameter is deprecated in
2+
favour of `name` and will be removed in 1.12. The `**kwargs` parameter of
3+
:meth:`metrics.DetCurveDisplay.plot`, :meth:`metrics.DetCurveDisplay.from_estimator`
4+
and :meth:`metrics.DetCurveDisplay.from_predictions` is also deprecated in favour of
5+
`curve_kwargs` and will be removed in 1.12.
6+
By :user:`AnneBeyer`.

examples/model_selection/plot_det.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,10 @@
9898
ax_det.grid(linestyle="--")
9999

100100
for name, clf in classifiers.items():
101-
(color, linestyle) = (
102-
("black", "--") if name == "Non-informative baseline" else (None, None)
101+
curve_kwargs = (
102+
dict(color="black", linestyle="--")
103+
if name == "Non-informative baseline"
104+
else None
103105
)
104106
clf.fit(X_train, y_train)
105107
RocCurveDisplay.from_estimator(
@@ -108,10 +110,15 @@
108110
y_test,
109111
ax=ax_roc,
110112
name=name,
111-
curve_kwargs=dict(color=color, linestyle=linestyle),
113+
curve_kwargs=curve_kwargs,
112114
)
113115
DetCurveDisplay.from_estimator(
114-
clf, X_test, y_test, ax=ax_det, name=name, color=color, linestyle=linestyle
116+
clf,
117+
X_test,
118+
y_test,
119+
ax=ax_det,
120+
name=name,
121+
curve_kwargs=curve_kwargs,
115122
)
116123

117124
plt.legend()

sklearn/metrics/_plot/det_curve.py

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sklearn.metrics._ranking import det_curve
88
from sklearn.utils._plotting import (
99
_BinaryClassifierCurveDisplayMixin,
10+
_deprecate_estimator_name,
1011
_deprecate_y_pred_parameter,
1112
)
1213

@@ -33,13 +34,23 @@ class DetCurveDisplay(_BinaryClassifierCurveDisplayMixin):
3334
fnr : ndarray
3435
False negative rate.
3536
36-
estimator_name : str, default=None
37-
Name of estimator. If None, the estimator name is not shown.
37+
name : str, default=None
38+
Name for labeling the legend entry. If None, the estimator name is not shown.
39+
40+
.. versionchanged:: 1.10
41+
`estimator_name` was deprecated in favor of `name`.
3842
3943
pos_label : int, float, bool or str, default=None
4044
The label of the positive class. If not `None`, this value is displayed in
4145
the x- and y-axes labels.
4246
47+
estimator_name : str, default=None
48+
Name of estimator. If None, the estimator name is not shown.
49+
50+
.. deprecated:: 1.10
51+
`estimator_name` is deprecated and will be removed in 1.12. Use `name`
52+
instead.
53+
4354
Attributes
4455
----------
4556
line_ : matplotlib Artist
@@ -73,17 +84,19 @@ class DetCurveDisplay(_BinaryClassifierCurveDisplayMixin):
7384
>>> y_score = clf.decision_function(X_test)
7485
>>> fpr, fnr, _ = det_curve(y_test, y_score)
7586
>>> display = DetCurveDisplay(
76-
... fpr=fpr, fnr=fnr, estimator_name="SVC"
87+
... fpr=fpr, fnr=fnr, name="SVC"
7788
... )
7889
>>> display.plot()
7990
<...>
8091
>>> plt.show()
8192
"""
8293

83-
def __init__(self, *, fpr, fnr, estimator_name=None, pos_label=None):
94+
def __init__(
95+
self, *, fpr, fnr, name=None, pos_label=None, estimator_name="deprecated"
96+
):
8497
self.fpr = fpr
8598
self.fnr = fnr
86-
self.estimator_name = estimator_name
99+
self.name = _deprecate_estimator_name(estimator_name, name, "1.10")
87100
self.pos_label = pos_label
88101

89102
@classmethod
@@ -99,6 +112,7 @@ def from_estimator(
99112
pos_label=None,
100113
name=None,
101114
ax=None,
115+
curve_kwargs=None,
102116
**kwargs,
103117
):
104118
"""Plot DET curve given an estimator and data.
@@ -151,9 +165,18 @@ def from_estimator(
151165
Axes object to plot on. If `None`, a new figure and axes is
152166
created.
153167
168+
curve_kwargs : dict, default=None
169+
Keywords arguments to be passed to matplotlib's `plot` function.
170+
171+
.. versionadded:: 1.10
172+
154173
**kwargs : dict
155174
Additional keywords arguments passed to matplotlib `plot` function.
156175
176+
.. deprecated:: 1.10
177+
`**kwargs` is deprecated and will be removed in 1.12. Pass matplotlib
178+
arguments to `curve_kwargs` as a dictionary instead.
179+
157180
Returns
158181
-------
159182
display : :class:`~sklearn.metrics.DetCurveDisplay`
@@ -197,6 +220,7 @@ def from_estimator(
197220
drop_intermediate=drop_intermediate,
198221
name=name,
199222
ax=ax,
223+
curve_kwargs=curve_kwargs,
200224
pos_label=pos_label,
201225
**kwargs,
202226
)
@@ -212,6 +236,7 @@ def from_predictions(
212236
pos_label=None,
213237
name=None,
214238
ax=None,
239+
curve_kwargs=None,
215240
y_pred="deprecated",
216241
**kwargs,
217242
):
@@ -260,6 +285,11 @@ class or non-thresholded decision values (as returned by
260285
Axes object to plot on. If `None`, a new figure and axes is
261286
created.
262287
288+
curve_kwargs : dict, default=None
289+
Keywords arguments to be passed to matplotlib's `plot` function.
290+
291+
.. versionadded:: 1.10
292+
263293
y_pred : array-like of shape (n_samples,)
264294
Target scores, can either be probability estimates of the positive
265295
class or non-thresholded decision values (as returned by
@@ -272,6 +302,10 @@ class or non-thresholded decision values (as returned by
272302
**kwargs : dict
273303
Additional keywords arguments passed to matplotlib `plot` function.
274304
305+
.. deprecated:: 1.10
306+
`**kwargs` is deprecated and will be removed in 1.12. Pass matplotlib
307+
arguments to `curve_kwargs` as a dictionary instead.
308+
275309
Returns
276310
-------
277311
display : :class:`~sklearn.metrics.DetCurveDisplay`
@@ -316,13 +350,13 @@ class or non-thresholded decision values (as returned by
316350
viz = cls(
317351
fpr=fpr,
318352
fnr=fnr,
319-
estimator_name=name,
353+
name=name,
320354
pos_label=pos_label_validated,
321355
)
322356

323-
return viz.plot(ax=ax, name=name, **kwargs)
357+
return viz.plot(ax=ax, name=name, curve_kwargs=curve_kwargs, **kwargs)
324358

325-
def plot(self, ax=None, *, name=None, **kwargs):
359+
def plot(self, ax=None, *, name=None, curve_kwargs=None, **kwargs):
326360
"""Plot visualization.
327361
328362
Parameters
@@ -332,21 +366,38 @@ def plot(self, ax=None, *, name=None, **kwargs):
332366
created.
333367
334368
name : str, default=None
335-
Name of DET curve for labeling. If `None`, use `estimator_name` if
336-
it is not `None`, otherwise no labeling is shown.
369+
Name of DET curve for labeling. If `None`, use `name` provided at
370+
`DetCurveDisplay` initialization, otherwise no labeling is shown.
371+
372+
curve_kwargs : dict, default=None
373+
Keywords arguments to be passed to matplotlib's `plot` function.
374+
375+
.. versionadded:: 1.10
337376
338377
**kwargs : dict
339378
Additional keywords arguments passed to matplotlib `plot` function.
340379
380+
.. deprecated:: 1.10
381+
`**kwargs` is deprecated and will be removed in 1.12. Pass matplotlib
382+
arguments to `curve_kwargs` as a dictionary instead.
383+
341384
Returns
342385
-------
343386
display : :class:`~sklearn.metrics.DetCurveDisplay`
344387
Object that stores computed values.
345388
"""
346389
self.ax_, self.figure_, name = self._validate_plot_params(ax=ax, name=name)
347390

348-
line_kwargs = {} if name is None else {"label": name}
349-
line_kwargs.update(**kwargs)
391+
_, legend_metric = self._get_legend_metric(curve_kwargs, 1, None)
392+
(curve_kwargs,) = self._validate_curve_kwargs(
393+
1,
394+
name,
395+
legend_metric,
396+
"",
397+
curve_kwargs=curve_kwargs,
398+
removed_version="1.12",
399+
**kwargs,
400+
)
350401

351402
# We have the following bounds:
352403
# sp.stats.norm.ppf(0.0) = -np.inf
@@ -359,7 +410,7 @@ def plot(self, ax=None, *, name=None, **kwargs):
359410
(self.line_,) = self.ax_.plot(
360411
sp.stats.norm.ppf(self.fpr),
361412
sp.stats.norm.ppf(self.fnr),
362-
**line_kwargs,
413+
**curve_kwargs,
363414
)
364415
info_pos_label = (
365416
f" (Positive label: {self.pos_label})" if self.pos_label is not None else ""
@@ -369,7 +420,7 @@ def plot(self, ax=None, *, name=None, **kwargs):
369420
ylabel = "False Negative Rate" + info_pos_label
370421
self.ax_.set(xlabel=xlabel, ylabel=ylabel)
371422

372-
if "label" in line_kwargs:
423+
if curve_kwargs.get("label") is not None:
373424
self.ax_.legend(loc="lower right")
374425

375426
ticks = [0.001, 0.01, 0.05, 0.20, 0.5, 0.80, 0.95, 0.99, 0.999]

sklearn/metrics/_plot/tests/test_common_curve_display.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ def test_display_curve_name_overwritten_by_plot_multiple_calls(
261261
pytest.skip(f"`from_cv_results` not implemented in {Display}")
262262

263263
# TODO: Clean-up once `estimator_name` deprecated in all displays
264-
if Display in (PrecisionRecallDisplay, RocCurveDisplay):
264+
if Display in (PrecisionRecallDisplay, RocCurveDisplay, DetCurveDisplay):
265265
assert disp.name == clf_name
266266
else:
267267
assert disp.estimator_name == clf_name
@@ -306,7 +306,7 @@ def test_display_curve_not_fitted_errors(pyplot, data_binary, clf, Display):
306306
disp = Display.from_estimator(model, X, y)
307307
assert model.__class__.__name__ in disp.line_.get_label()
308308
# TODO: Clean-up once `estimator_name` deprecated in all displays
309-
if Display in (PrecisionRecallDisplay, RocCurveDisplay):
309+
if Display in (PrecisionRecallDisplay, RocCurveDisplay, DetCurveDisplay):
310310
assert disp.name == model.__class__.__name__
311311
else:
312312
assert disp.estimator_name == model.__class__.__name__
@@ -802,6 +802,11 @@ def test_display_from_cv_results_curve_kwargs_default_kwargs(
802802
PrecisionRecallDisplay,
803803
{"precision": np.array([1, 0.5, 0]), "recall": np.array([0, 0.5, 1])},
804804
),
805+
# TODO(1.12): Remove
806+
(
807+
DetCurveDisplay,
808+
{"fpr": np.array([0, 0.5, 1]), "fnr": np.array([1, 0.5, 0])},
809+
),
805810
],
806811
)
807812
def test_display_estimator_name_deprecation(pyplot, Display, display_kwargs):
@@ -818,6 +823,11 @@ def test_display_estimator_name_deprecation(pyplot, Display, display_kwargs):
818823
PrecisionRecallDisplay,
819824
{"precision": np.array([1, 0.5, 0]), "recall": np.array([0, 0.5, 1])},
820825
),
826+
# TODO(1.12): Remove
827+
(
828+
DetCurveDisplay,
829+
{"fpr": np.array([0, 0.5, 1]), "fnr": np.array([1, 0.5, 0])},
830+
),
821831
],
822832
)
823833
@pytest.mark.parametrize(

sklearn/metrics/_plot/tests/test_det_curve_display.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def test_det_curve_display(
4545

4646
common_kwargs = {
4747
"name": lr.__class__.__name__,
48-
"alpha": 0.8,
48+
"curve_kwargs": {"alpha": 0.8},
4949
"sample_weight": sample_weight,
5050
"drop_intermediate": drop_intermediate,
5151
"pos_label": pos_label,
@@ -66,7 +66,7 @@ def test_det_curve_display(
6666
assert_allclose(disp.fpr, fpr, atol=1e-7)
6767
assert_allclose(disp.fnr, fnr, atol=1e-7)
6868

69-
assert disp.estimator_name == "LogisticRegression"
69+
assert disp.name == "LogisticRegression"
7070

7171
# cannot fail thanks to pyplot fixture
7272
import matplotlib as mpl
@@ -109,7 +109,7 @@ def test_det_curve_display_default_name(
109109
else:
110110
disp = DetCurveDisplay.from_predictions(y, y_score)
111111

112-
assert disp.estimator_name == expected_clf_name
112+
assert disp.name == expected_clf_name
113113
assert disp.line_.get_label() == expected_clf_name
114114

115115

0 commit comments

Comments
 (0)