8000 MAINT Remove scipy<1.6 specific code (#29461) · scikit-learn/scikit-learn@b0f86e7 · GitHub
[go: up one dir, main page]

Skip to content

Commit b0f86e7

Browse files
authored
MAINT Remove scipy<1.6 specific code (#29461)
1 parent e7af195 commit b0f86e7

12 files changed

+34
-141
lines changed

examples/linear_model/plot_quantile_regression.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,6 @@
109109
#
110110
# We will use the quantiles at 5% and 95% to find the outliers in the training
111111
# sample beyond the central 90% interval.
112-
from sklearn.utils.fixes import parse_version, sp_version
113-
114-
# This is line is to avoid incompatibility if older SciPy version.
115-
# You should use `solver="highs"` with recent version of SciPy.
116-
solver = "highs" if sp_version >= parse_version("1.6.0") else "interior-point"
117112

118113
# %%
119114
from sklearn.linear_model import QuantileRegressor
@@ -122,7 +117,7 @@
122117
predictions = {}
123118
out_bounds_predictions = np.zeros_like(y_true_mean, dtype=np.bool_)
124119
for quantile in quantiles:
125-
qr = QuantileRegressor(quantile=quantile, alpha=0, solver=solver)
120+
qr = QuantileRegressor(quantile=quantile, alpha=0)
126121
y_pred = qr.fit(X, y_normal).predict(X)
127122
predictions[quantile] = y_pred
128123

@@ -184,7 +179,7 @@
184179
predictions = {}
185180
out_bounds_predictions = np.zeros_like(y_true_mean, dtype=np.bool_)
186181
for quantile in quantiles:
187-
qr = QuantileRegressor(quantile=quantile, alpha=0, solver=solver)
182+
qr = QuantileRegressor(quantile=quantile, alpha=0)
188183
y_pred = qr.fit(X, y_pareto).predict(X)
189184
predictions[quantile] = y_pred
190185

@@ -254,7 +249,7 @@
254249
from sklearn.metrics import mean_absolute_error, mean_squared_error
255250

256251
linear_regression = LinearRegression()
257-
quantile_regression = QuantileRegressor(quantile=0.5, alpha=0, solver=solver)
252+
quantile_regression = QuantileRegressor(quantile=0.5, alpha=0)
258253

259254
y_pred_lr = linear_regression.fit(X, y_pareto).predict(X)
260255
y_pred_qr = quantile_regression.fit(X, y_pareto).predict(X)

sklearn/kernel_approximation.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,9 @@
88

99
import numpy as np
1010
import scipy.sparse as sp
11+
from scipy.fft import fft, ifft
1112
from scipy.linalg import svd
1213

13-
try:
14-
from scipy.fft import fft, ifft
15-
except ImportError: # scipy < 1.4
16-
from scipy.fftpack import fft, ifft
17-
1814
from .base import (
1915
BaseEstimator,
2016
ClassNamePrefixFeaturesOutMixin,

sklearn/linear_model/_quantile.py

+8-21
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class QuantileRegressor(LinearModel, RegressorMixin, BaseEstimator):
4747
Method used by :func:`scipy.optimize.linprog` to solve the linear
4848
programming formulation.
4949
50-
From `scipy>=1.6.0`, it is recommended to use the highs methods because
50+
It is recommended to use the highs methods because
5151
they are the fastest ones. Solvers "highs-ds", "highs-ipm" and "highs"
5252
support sparse input data and, in fact, always convert to sparse csc.
5353
@@ -100,8 +100,7 @@ class QuantileRegressor(LinearModel, RegressorMixin, BaseEstimator):
100100
>>> X = rng.randn(n_samples, n_features)
101101
>>> # the two following lines are optional in practice
102102
>>> from sklearn.utils.fixes import sp_version, parse_version
103-
>>> solver = "highs" if sp_version >= parse_version("1.6.0") else "interior-point"
104-
>>> reg = QuantileRegressor(quantile=0.8, solver=solver).fit(X, y)
103+
>>> reg = QuantileRegressor(quantile=0.8).fit(X, y)
105104
>>> np.mean(y <= reg.predict(X))
106105
0.8
107106
"""
@@ -180,30 +179,18 @@ def fit(self, X, y, sample_weight=None):
180179
# So we rescale the penalty term, which is equivalent.
181180
alpha = np.sum(sample_weight) * self.alpha
182181

183-
if self.solver in (
184-
"highs-ds",
185-
"highs-ipm",
186-
"highs",
187-
) and sp_version < parse_version("1.6.0"):
182+
if self.solver == "interior-point" and sp_version >= parse_version("1.11.0"):
188183
raise ValueError(
189-
f"Solver {self.solver} is only available "
190-
f"with scipy>=1.6.0, got {sp_version}"
191-
)
192-
else:
193-
solver = self.solver
194-
195-
if solver == "interior-point" and sp_version >= parse_version("1.11.0"):
196-
raise ValueError(
197-
f"Solver {solver} is not anymore available in SciPy >= 1.11.0."
184+
f"Solver {self.solver} is not anymore available in SciPy >= 1.11.0."
198185
)
199186

200-
if sparse.issparse(X) and solver not in ["highs", "highs-ds", "highs-ipm"]:
187+
if sparse.issparse(X) and self.solver not in ["highs", "highs-ds", "highs-ipm"]:
201188
raise ValueError(
202189
f"Solver {self.solver} does not support sparse X. "
203190
"Use solver 'highs' for example."
204191
)
205192
# make default solver more stable
206-
if self.solver_options is None and solver == "interior-point":
193+
if self.solver_options is None and self.solver == "interior-point":
207194
solver_options = {"lstsq": True}
208195
else:
209196
solver_options = self.solver_options
@@ -246,7 +233,7 @@ def fit(self, X, y, sample_weight=None):
246233
c[0] = 0
247234
c[n_params] = 0
248235

249-
if solver in ["highs", "highs-ds", "highs-ipm"]:
236+
if self.solver in ["highs", "highs-ds", "highs-ipm"]:
250237
# Note that highs methods always use a sparse CSC memory layout internally,
251238
# even for optimization problems parametrized using dense numpy arrays.
252239
# Therefore, we work with CSC matrices as early as possible to limit
@@ -271,7 +258,7 @@ def fit(self, X, y, sample_weight=None):
271258
c=c,
272259
A_eq=A_eq,
273260
b_eq=b_eq,
274-
method=solver,
261+
method=self.solver,
275262
options=solver_options,
276263
)
277264
solution = result.x

sklearn/linear_model/tests/test_quantile.py

+13-49
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ def X_y_data():
2626
return X, y
2727

2828

29-
@pytest.fixture
30-
def default_solver():
31-
return "highs" if sp_version >= parse_version("1.6.0") else "interior-point"
32-
33-
3429
@pytest.mark.skipif(
3530
parse_version(sp_version.base_version) >= parse_version("1.11"),
3631
reason="interior-point solver is not available in SciPy 1.11",
@@ -47,18 +42,6 @@ def test_incompatible_solver_for_sparse_input(X_y_data, solver, csc_container):
4742
QuantileRegressor(solver=solver).fit(X_sparse, y)
4843

4944

50-
@pytest.mark.parametrize("solver", ("highs-ds", "highs-ipm", "highs"))
51-
@pytest.mark.skipif(
52-
sp_version >= parse_version("1.6.0"),
53-
reason="Solvers are available as of scipy 1.6.0",
54-
)
55-
def test_too_new_solver_methods_raise_error(X_y_data, solver):
56-
"""Test that highs solver raises for scipy<1.6.0."""
57-
X, y = X_y_data
58-
with pytest.raises(ValueError, match="scipy>=1.6.0"):
59-
QuantileRegressor(solver=solver).fit(X, y)
60-
61-
6245
@pytest.mark.parametrize(
6346
"quantile, alpha, intercept, coef",
6447
[
@@ -74,13 +57,11 @@ def test_too_new_solver_methods_raise_error(X_y_data, solver):
7457
[0.5, 100, 2, 0],
7558
],
7659
)
77-
def test_quantile_toy_example(quantile, alpha, intercept, coef, default_solver):
60+
def test_quantile_toy_example(quantile, alpha, intercept, coef):
7861
# test how different parameters affect a small intuitive example
7962
X = [[0], [1], [1]]
8063
y = [1, 2, 11]
81-
model = QuantileRegressor(
82-
quantile=quantile, alpha=alpha, solver=default_solver
83-
).fit(X, y)
64+
model = QuantileRegressor(quantile=quantile, alpha=alpha).fit(X, y)
8465
assert_allclose(model.intercept_, intercept, atol=1e-2)
8566
if coef is not None:
8667
assert_allclose(model.coef_[0], coef, atol=1e-2)
@@ -90,15 +71,13 @@ def test_quantile_toy_example(quantile, alpha, intercept, coef, default_solver):
9071

9172

9273
@pytest.mark.parametrize("fit_intercept", [True, False])
93-
def test_quantile_equals_huber_for_low_epsilon(fit_intercept, default_solver):
74+
def test_quantile_equals_huber_for_low_epsilon(fit_intercept):
9475
X, y = make_regression(n_samples=100, n_features=20, random_state=0, noise=1.0)
9576
alpha = 1e-4
9677
huber = HuberRegressor(
9778
epsilon=1 + 1e-4, alpha=alpha, fit_intercept=fit_intercept
9879
).fit(X, y)
99-
quant = QuantileRegressor(
100-
alpha=alpha, fit_intercept=fit_intercept, solver=default_solver
101-
).fit(X, y)
80+
quant = QuantileRegressor(alpha=alpha, fit_intercept=fit_intercept).fit(X, y)
10281
assert_allclose(huber.coef_, quant.coef_, atol=1e-1)
10382
if fit_intercept:
10483
assert huber.intercept_ == approx(quant.intercept_, abs=1e-1)
@@ -107,39 +86,31 @@ def test_quantile_equals_huber_for_low_epsilon(fit_intercept, default_solver):
10786

10887

10988
@pytest.mark.parametrize("q", [0.5, 0.9, 0.05])
110-
def test_quantile_estimates_calibration(q, default_solver):
89+
def test_quantile_estimates_calibration(q):
11190
# Test that model estimates percentage of points below the prediction
11291
X, y = make_regression(n_samples=1000, n_features=20, random_state=0, noise=1.0)
113-
quant = QuantileRegressor(
114-
quantile=q,
115-
alpha=0,
116-
solver=default_solver,
117-
).fit(X, y)
92+
quant = QuantileRegressor(quantile=q, alpha=0).fit(X, y)
11893
assert np.mean(y < quant.predict(X)) == approx(q, abs=1e-2)
11994

12095

121-
def test_quantile_sample_weight(default_solver):
96+
def test_quantile_sample_weight():
12297
# test that with unequal sample weights we still estimate weighted fraction
12398
n = 1000
12499
X, y = make_regression(n_samples=n, n_features=5, random_state=0, noise=10.0)
125100
weight = np.ones(n)
126101
# when we increase weight of upper observations,
127102
# estimate of quantile should go up
128103
weight[y > y.mean()] = 100
129-
quant = QuantileRegressor(quantile=0.5, alpha=1e-8, solver=default_solver)
104+
quant = QuantileRegressor(quantile=0.5, alpha=1e-8)
130105
quant.fit(X, y, sample_weight=weight)
131106
fraction_below = np.mean(y < quant.predict(X))
132107
assert fraction_below > 0.5
133108
weighted_fraction_below = np.average(y < quant.predict(X), weights=weight)
134109
assert weighted_fraction_below == approx(0.5, abs=3e-2)
135110

136111

137-
@pytest.mark.skipif(
138-
sp_version < parse_version("1.6.0"),
139-
reason="The `highs` solver is available from the 1.6.0 scipy version",
140-
)
141112
@pytest.mark.parametrize("quantile", [0.2, 0.5, 0.8])
142-
def test_asymmetric_error(quantile, default_solver):
113+
def test_asymmetric_error(quantile):
143114
"""Test quantile regression for asymmetric distributed targets."""
144115
n_samples = 1000
145116
rng = np.random.RandomState(42)
@@ -164,7 +135,6 @@ def test_asymmetric_error(quantile, default_solver):
164135
model = QuantileRegressor(
165136
quantile=quantile,
166137
alpha=0,
167-
solver=default_solver,
168138
).fit(X, y)
169139
# This test can be made to pass with any solver but in the interest
170140
# of sparing continuous integration resources, the test is performed
@@ -199,7 +169,7 @@ def func(coef):
199169

200170

201171
@pytest.mark.parametrize("quantile", [0.2, 0.5, 0.8])
202-
def test_equivariance(quantile, default_solver):
172+
def test_equivariance(quantile):
203173
"""Test equivariace of quantile regression.
204174
205175
See Koenker (2005) Quantile Regression, Chapter 2.2.3.
@@ -216,7 +186,7 @@ def test_equivariance(quantile, default_solver):
216186
)
217187
# make y asymmetric
218188
y += rng.exponential(scale=100, size=y.shape)
219-
params = dict(alpha=0, solver=default_solver)
189+
params = dict(alpha=0)
220190
model1 = QuantileRegressor(quantile=quantile, **params).fit(X, y)
221191

222192
# coef(q; a*y, X) = a * coef(q; y, X)
@@ -264,23 +234,17 @@ def test_linprog_failure():
264234

265235

266236
@skip_if_32bit
267-
@pytest.mark.skipif(
268-
sp_version <= parse_version("1.6.0"),
269-
reason="Solvers are available as of scipy 1.6.0",
270-
)
271237
@pytest.mark.parametrize(
272238
"sparse_container", CSC_CONTAINERS + CSR_CONTAINERS + COO_CONTAINERS
273239
)
274240
@pytest.mark.parametrize("solver", ["highs", "highs-ds", "highs-ipm"])
275241
@pytest.mark.parametrize("fit_intercept", [True, False])
276-
def test_sparse_input(sparse_container, solver, fit_intercept, default_solver):
242+
def test_sparse_input(sparse_container, solver, fit_intercept):
277243
"""Test that sparse and dense X give same results."""
278244
X, y = make_regression(n_samples=100, n_features=20, random_state=1, noise=1.0)
279245
X_sparse = sparse_container(X)
280246
alpha = 1e-4
281-
quant_dense = QuantileRegressor(
282-
alpha=alpha, fit_intercept=fit_intercept, solver=default_solver
283-
).fit(X, y)
247+
quant_dense = QuantileRegressor(alpha=alpha, fit_intercept=fit_intercept).fit(X, y)
284248
quant_sparse = QuantileRegressor(
285249
alpha=alpha, fit_intercept=fit_intercept, solver=solver
286250
).fit(X_sparse, y)

sklearn/metrics/_plot/tests/test_precision_recall_display.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import numpy as np
44
import pytest
5+
from scipy.integrate import trapezoid
56

67
from sklearn.compose import make_column_transformer
78
from sklearn.datasets import load_breast_cancer, make_classification
@@ -16,7 +17,6 @@
1617
from sklearn.pipeline import make_pipeline
1718
from sklearn.preprocessing import StandardScaler
1819
from sklearn.utils import shuffle
19-
from sklearn.utils.fixes import trapezoid
2020

2121
# TODO: Remove when https://github.com/numpy/numpy/issues/14397 is resolved
2222
pytestmark = pytest.mark.filterwarnings(

sklearn/metrics/_plot/tests/test_roc_curve_display.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
< 10000 /code>
11
import numpy as np
22
import pytest
33
from numpy.testing import assert_allclose
4+
from scipy.integrate import trapezoid
45

56
from sklearn.compose import make_column_transformer
67
from sklearn.datasets import load_breast_cancer, load_iris
@@ -11,7 +12,6 @@
1112
from sklearn.pipeline import make_pipeline
1213
from sklearn.preprocessing import StandardScaler
1314
from sklearn.utils import shuffle
14-
from sklearn.utils.fixes import trapezoid
1515

1616

1717
@pytest.fixture(scope="module")

sklearn/metrics/_ranking.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from numbers import Integral, Real
1717

1818
import numpy as np
19+
from scipy.integrate import trapezoid
1920
from scipy.sparse import csr_matrix, issparse
2021
from scipy.stats import rankdata
2122

@@ -30,7 +31,6 @@
3031
from ..utils._encode import _encode, _unique
3132
from ..utils._param_validation import Hidden, Interval, StrOptions, validate_params
3233
from ..utils.extmath import stable_cumsum
33-
from ..utils.fixes import trapezoid
3434
from ..utils.multiclass import type_of_target
3535
from ..utils.sparsefuncs import count_nonzero
3636
from ..utils.validation import _check_pos_label_consistency, _check_sample_weight

0 commit comments

Comments
 (0)
0