Skip to content

Commit

Permalink
MNT deprecate outputs_2d_ attribute of dummy estimators (#14933)
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasHug committed Sep 15, 2019
1 parent 6ee390d commit 84717a9
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 13 deletions.
6 changes: 5 additions & 1 deletion doc/whats_new/v0.22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,16 @@ Changelog
`Adrin Jalali`_.

:mod:`sklearn.dummy`
............................
....................

- |Fix| :class:`dummy.DummyClassifier` now handles checking the existence
of the provided constant in multiouput cases.
:pr:`14908` by :user:`Martina G. Vilas <martinagvilas>`.

- |API| The ``outputs_2d_`` attribute is deprecated in
:class:`dummy.DummyClassifier` and :class:`dummy.DummyRegressor`. It is
equivalent to ``n_outputs > 1``. :pr:`14933` by `Nicolas Hug`_

:mod:`sklearn.ensemble`
.......................

Expand Down
39 changes: 27 additions & 12 deletions sklearn/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .utils.random import random_choice_csc
from .utils.stats import _weighted_percentile
from .utils.multiclass import class_distribution
from .utils import deprecated


class DummyClassifier(MultiOutputMixin, ClassifierMixin, BaseEstimator):
Expand Down Expand Up @@ -120,8 +121,6 @@ def fit(self, X, y, sample_weight=None):
if not self.sparse_output_:
y = np.atleast_1d(y)

self.output_2d_ = y.ndim == 2 and y.shape[1] > 1

if y.ndim == 1:
y = np.reshape(y, (-1, 1))

Expand Down Expand Up @@ -154,7 +153,7 @@ def fit(self, X, y, sample_weight=None):
.format(self.constant, list(self.classes_[k])))
raise ValueError(err_msg)

if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
self.n_classes_ = self.n_classes_[0]
self.classes_ = self.classes_[0]
self.class_prior_ = self.class_prior_[0]
Expand Down Expand Up @@ -185,7 +184,7 @@ def predict(self, X):
classes_ = self.classes_
class_prior_ = self.class_prior_
constant = self.constant
if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
# Get same type even for self.n_outputs_ == 1
n_classes_ = [n_classes_]
classes_ = [classes_]
Expand All @@ -194,7 +193,7 @@ def predict(self, X):
# Compute probability only once
if self.strategy == "stratified":
proba = self.predict_proba(X)
if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
proba = [proba]

if self.sparse_output_:
Expand Down Expand Up @@ -231,7 +230,7 @@ def predict(self, X):
elif self.strategy == "constant":
y = np.tile(self.constant, (n_samples, 1))

if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
y = np.ravel(y)

return y
Expand Down Expand Up @@ -263,7 +262,7 @@ def predict_proba(self, X):
classes_ = self.classes_
class_prior_ = self.class_prior_
constant = self.constant
if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
# Get same type even for self.n_outputs_ == 1
n_classes_ = [n_classes_]
classes_ = [classes_]
Expand Down Expand Up @@ -294,7 +293,7 @@ def predict_proba(self, X):

P.append(out)

if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
P = P[0]

return P
Expand Down Expand Up @@ -355,6 +354,15 @@ def score(self, X, y, sample_weight=None):
X = np.zeros(shape=(len(y), 1))
return super().score(X, y, sample_weight)

@deprecated(
"The outputs_2d_ attribute is deprecated in version 0.22 "
"and will be removed in version 0.24. It is equivalent to "
"n_outputs_ > 1."
)
@property
def outputs_2d_(self):
return self.n_outputs_ != 1


class DummyRegressor(MultiOutputMixin, RegressorMixin, BaseEstimator):
"""
Expand Down Expand Up @@ -429,8 +437,6 @@ def fit(self, X, y, sample_weight=None):
if len(y) == 0:
raise ValueError("y must not be empty.")

self.output_2d_ = y.ndim == 2 and y.shape[1] > 1

if y.ndim == 1:
y = np.reshape(y, (-1, 1))
self.n_outputs_ = y.shape[1]
Expand Down Expand Up @@ -470,7 +476,7 @@ def fit(self, X, y, sample_weight=None):
accept_sparse=['csr', 'csc', 'coo'],
ensure_2d=False, ensure_min_samples=0)

if self.output_2d_ and self.constant.shape[0] != y.shape[1]:
if self.n_outputs_ != 1 and self.constant.shape[0] != y.shape[1]:
raise ValueError(
"Constant target value should have "
"shape (%d, 1)." % y.shape[1])
Expand Down Expand Up @@ -508,7 +514,7 @@ def predict(self, X, return_std=False):
dtype=np.array(self.constant_).dtype)
y_std = np.zeros((n_samples, self.n_outputs_))

if self.n_outputs_ == 1 and not self.output_2d_:
if self.n_outputs_ == 1:
y = np.ravel(y)
y_std = np.ravel(y_std)

Expand Down Expand Up @@ -554,3 +560,12 @@ def score(self, X, y, sample_weight=None):
if X is None:
X = np.zeros(shape=(len(y), 1))
return super().score(X, y, sample_weight)

@deprecated(
"The outputs_2d_ attribute is deprecated in version 0.22 "
"and will be removed in version 0.24. It is equivalent to "
"n_outputs_ > 1."
)
@property
def outputs_2d_(self):
return self.n_outputs_ != 1
9 changes: 9 additions & 0 deletions sklearn/tests/test_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,12 @@ def test_dtype_of_classifier_probas(strategy):
probas = model.fit(X, y).predict_proba(X)

assert probas.dtype == np.float64


@pytest.mark.parametrize("Dummy", (DummyRegressor, DummyClassifier))
def test_outputs_2d_deprecation(Dummy):
X = [[1, 2]]
y = [0]
with pytest.warns(DeprecationWarning,
match="will be removed in version 0.24"):
Dummy().fit(X, y).outputs_2d_

0 comments on commit 84717a9

Please sign in to comment.