Skip to content

Fix for #6931: isotonic y_min/y_max bounds #6928

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions sklearn/isotonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def check_increasing(x, y):


def isotonic_regression(y, sample_weight=None, y_min=None, y_max=None,
increasing=True):
increasing=True, pre_sorted=True):
"""Solve the isotonic regression model::

min sum w[i] (y[i] - y_[i]) ** 2
Expand Down Expand Up @@ -110,6 +110,9 @@ def isotonic_regression(y, sample_weight=None, y_min=None, y_max=None,
Whether to compute ``y_`` is increasing (if set to True) or decreasing
(if set to False)

pre_sorted : boolean, optional, default: True
Whether the input `y` is pre-sorted, e.g., from within a model class.

Returns
-------
y_ : list of floating-point values
Expand All @@ -129,17 +132,23 @@ def isotonic_regression(y, sample_weight=None, y_min=None, y_max=None,
y = y[::-1]
sample_weight = sample_weight[::-1]

# Ensure that sample is sorted if not pre_sort
if not pre_sorted:
_y_sort_index = np.argsort(y)
y = np.copy(y[_y_sort_index])
sample_weight = np.copy(sample_weight[_y_sort_index])

if y_min is not None or y_max is not None:
y = np.copy(y)
sample_weight = np.copy(sample_weight)
# Only create a copy if pre-sorted
if pre_sorted:
y = np.copy(y)
sample_weight = np.copy(sample_weight)

# upper bound on the cost function
C = np.dot(sample_weight, y * y) * 10
if y_min is not None:
y[0] = y_min
sample_weight[0] = C
y[y < y_min] = y_min
if y_max is not None:
y[-1] = y_max
sample_weight[-1] = C
y[y > y_max] = y_max

solution = np.empty(len(y))
y_ = _isotonic_regression(y, sample_weight, solution)
Expand Down
12 changes: 12 additions & 0 deletions sklearn/tests/test_isotonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,18 @@ def test_isotonic_duplicate_min_entry():
assert_true(all_predictions_finite)


def test_isotonic_ymin_ymax():
# Test case as reported in Issue #6921
x = np.array([1.26336413, 1.31853693, -0.57200917, 0.3072928, -0.70686507,
-0.17614937, -1.59943059, 1.05908504, 1.3958263, 1.90580318,
0.20992272, 0.02836316, -0.08092235, 0.44438247, 0.01791253,
-0.3771914, -0.89577538, -0.37726249, -1.32687569, 0.18013201])
y = isotonic_regression(x, y_min=0, y_max=0.1)

assert(np.all(y >= 0))
assert(np.all(y <= 0.1))


def test_isotonic_zero_weight_loop():
# Test from @ogrisel's issue:
# https://github.com/scikit-learn/scikit-learn/issues/4297
Expand Down