Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MRG] API Deprecates probA_ and probB_ in OneClassSVM and svm.SVR #15558

Merged
merged 12 commits into from Jan 28, 2020
7 changes: 7 additions & 0 deletions doc/whats_new/v0.23.rst
Expand Up @@ -171,6 +171,13 @@ Changelog
- |Efficiency| :class:`preprocessing.OneHotEncoder` is now faster at
transforming. :pr:`15762` by `Thomas Fan`_.

:mod:`sklearn.svm`
..................

- |API| :class:`svm.SVR` and :class:`svm.OneClassSVM` attributes, `probA_` and
`probB_`, are now deprecated as they were not useful. :pr:`15558` by
`Thomas Fan`_.

:mod:`sklearn.tree`
...................

Expand Down
26 changes: 17 additions & 9 deletions sklearn/svm/_base.py
Expand Up @@ -246,8 +246,8 @@ def _dense_fit(self, X, y, sample_weight, solver_type, kernel,
# we don't pass **self.get_params() to allow subclasses to
# add other parameters to __init__
self.support_, self.support_vectors_, self._n_support, \
self.dual_coef_, self.intercept_, self.probA_, \
self.probB_, self.fit_status_ = libsvm.fit(
self.dual_coef_, self.intercept_, self._probA, \
self._probB, self.fit_status_ = libsvm.fit(
X, y,
svm_type=solver_type, sample_weight=sample_weight,
class_weight=self.class_weight_, kernel=kernel, C=self.C,
Expand All @@ -270,7 +270,7 @@ def _sparse_fit(self, X, y, sample_weight, solver_type, kernel,

self.support_, self.support_vectors_, dual_coef_data, \
self.intercept_, self._n_support, \
self.probA_, self.probB_, self.fit_status_ = \
self._probA, self._probB, self.fit_status_ = \
libsvm_sparse.libsvm_sparse_train(
X.shape[1], X.data, X.indices, X.indptr, y, solver_type,
kernel_type, self.degree, self._gamma, self.coef0, self.tol,
Expand Down Expand Up @@ -334,7 +334,7 @@ def _dense_predict(self, X):
return libsvm.predict(
X, self.support_, self.support_vectors_, self._n_support,
self._dual_coef_, self._intercept_,
self.probA_, self.probB_, svm_type=svm_type, kernel=kernel,
self._probA, self._probB, svm_type=svm_type, kernel=kernel,
degree=self.degree, coef0=self.coef0, gamma=self._gamma,
cache_size=self.cache_size)

Expand All @@ -359,7 +359,7 @@ def _sparse_predict(self, X):
C, self.class_weight_,
self.nu, self.epsilon, self.shrinking,
self.probability, self._n_support,
self.probA_, self.probB_)
self._probA, self._probB)

def _compute_kernel(self, X):
"""Return the data transformed by a callable kernel"""
Expand Down Expand Up @@ -413,7 +413,7 @@ def _dense_decision_function(self, X):
return libsvm.decision_function(
X, self.support_, self.support_vectors_, self._n_support,
self._dual_coef_, self._intercept_,
self.probA_, self.probB_,
self._probA, self._probB,
svm_type=LIBSVM_IMPL.index(self._impl),
kernel=kernel, degree=self.degree, cache_size=self.cache_size,
coef0=self.coef0, gamma=self._gamma)
Expand All @@ -438,7 +438,7 @@ def _sparse_decision_function(self, X):
self.C, self.class_weight_,
self.nu, self.epsilon, self.shrinking,
self.probability, self._n_support,
self.probA_, self.probB_)
self._probA, self._probB)

def _validate_for_predict(self, X):
check_is_fitted(self)
Expand Down Expand Up @@ -691,7 +691,7 @@ def _dense_predict_proba(self, X):
pprob = libsvm.predict_proba(
X, self.support_, self.support_vectors_, self._n_support,
self._dual_coef_, self._intercept_,
self.probA_, self.probB_,
self._probA, self._probB,
svm_type=svm_type, kernel=kernel, degree=self.degree,
cache_size=self.cache_size, coef0=self.coef0, gamma=self._gamma)

Expand All @@ -717,7 +717,7 @@ def _sparse_predict_proba(self, X):
self.C, self.class_weight_,
self.nu, self.epsilon, self.shrinking,
self.probability, self._n_support,
self.probA_, self.probB_)
self._probA, self._probB)

def _get_coef(self):
if self.dual_coef_.shape[0] == 1:
Expand All @@ -734,6 +734,14 @@ def _get_coef(self):

return coef

@property
def probA_(self):
return self._probA

@property
def probB_(self):
return self._probB


def _get_liblinear_solver_type(multi_class, penalty, loss, dual):
"""Find the liblinear magic number for the solver.
Expand Down
29 changes: 29 additions & 0 deletions sklearn/svm/_classes.py
Expand Up @@ -8,6 +8,7 @@
from ..utils import check_X_y
from ..utils.validation import _num_samples
from ..utils.multiclass import check_classification_targets
from ..utils.deprecation import deprecated


class LinearSVC(BaseEstimator, LinearClassifierMixin,
Expand Down Expand Up @@ -968,6 +969,20 @@ def __init__(self, kernel='rbf', degree=3, gamma='scale',
shrinking=shrinking, probability=False, cache_size=cache_size,
class_weight=None, max_iter=max_iter, random_state=None)

@deprecated(
"The probA_ attribute is deprecated in version 0.23 and will be "
"removed in version 0.25.")
@property
def probA_(self):
return self._probA

@deprecated(
"The probB_ attribute is deprecated in version 0.23 and will be "
"removed in version 0.25.")
@property
def probB_(self):
return self._probB


class NuSVR(RegressorMixin, BaseLibSVM):
"""Nu Support Vector Regression.
Expand Down Expand Up @@ -1287,3 +1302,17 @@ def predict(self, X):
"""
y = super().predict(X)
return np.asarray(y, dtype=np.intp)

@deprecated(
"The probA_ attribute is deprecated in version 0.23 and will be "
"removed in version 0.25.")
@property
def probA_(self):
return self._probA

@deprecated(
"The probB_ attribute is deprecated in version 0.23 and will be "
"removed in version 0.25.")
@property
def probB_(self):
return self._probB
15 changes: 15 additions & 0 deletions sklearn/svm/tests/test_svm.py
Expand Up @@ -1233,3 +1233,18 @@ def test_n_support_oneclass_svr():
assert reg.n_support_ == reg.support_vectors_.shape[0]
assert reg.n_support_.size == 1
assert reg.n_support_ == 4


# TODO: Remove in 0.25 when probA_ and probB_ are deprecated
@pytest.mark.parametrize("SVMClass, data", [
(svm.OneClassSVM, (X, )),
(svm.SVR, (X, Y))
])
@pytest.mark.parametrize("deprecated_prob", ["probA_", "probB_"])
def test_svm_probA_proB_deprecated(SVMClass, data, deprecated_prob):
clf = SVMClass().fit(*data)

msg = ("The {} attribute is deprecated in version 0.23 and will be "
"removed in version 0.25.").format(deprecated_prob)
with pytest.warns(FutureWarning, match=msg):
getattr(clf, deprecated_prob)