8000 ENH: Add np.scale function by kernc · Pull Request #8196 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Add np.scale function #8196

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
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
ENH: Add np.rescale function
  • Loading branch information
kernc committed Mar 30, 2018
commit b131eaa4ceb8d9981c1ee4ea96ca2d6c7eb4de35
4 changes: 4 additions & 0 deletions doc/release/1.15.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ New functions

* `np.histogram_bin_edges`, a function to get the edges of the bins used by a histogram
without needing to calculate the histogram.
* New ``np.rescale(arr, out_min, out_max, in_min, in_max, axis)`` function
linearly scales the values of the array from interval ``[in_min, in_max]``
to ``[out_min, out_max]``.


Deprecations
============
Expand Down
1 change: 1 addition & 0 deletions doc/source/reference/routines.math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,4 @@ Miscellaneous
real_if_close

interp
rescale
76 changes: 75 additions & 1 deletion numpy/lib/function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
'bincount', 'digitize', 'cov', 'corrcoef',
'msort', 'median', 'sinc', 'hamming', 'hanning', 'bartlett',
'blackman', 'kaiser', 'trapz', 'i0', 'add_newdoc', 'add_docstring',
'meshgrid', 'delete', 'insert', 'append', 'interp', 'add_newdoc_ufunc'
'meshgrid', 'delete', 'insert', 'append', 'interp', 'add_newdoc_ufunc',
'rescale',
]


Expand Down Expand Up @@ -4397,3 +4398,76 @@ def append(arr, values, axis=None):
values = ravel(values)
axis = arr.ndim-1
return concatenate((arr, values), axis=axis)


def rescale(arr, out_min=0, out_max=1, in_min=None, in_max=None,
"""Linearly scale non-NaN values in array to the specified interval.

.. versionadded:: 1.15.0

Parameters
----------
arr : array_like
Input array.
out_min : scalar or array_like, optional
The minimum value in the output array (default: 0). If array-like,
its shape must be broadcastable to `arr`.
out_max : scalar or array_like, optional
The maximum value in the output array (default: 1). If array-like,
its shape must be broadcastable to `arr`.
in_min : scalar or array_like, optional
The minimum value in the input array to map to `out_min` (default
is `axis`-respecting minimum of `arr`). If array-like, its shape
must be broadcastable to `arr`.
in_max : scalar or array_like, optional
The maximum value in the input array to map to `out_max` (default
is `axis`-respecting maximum of `arr`). If array-like, its shape
must be broadcastable to `arr`.
axis : int, optional
Axis along which to scale `arr`. If `None`, scaling is done over
all axes.
out : array_like, optional
Array of the same shape as `arr`, in which to store the result.

Returns
-------
scaled_array : ndarray
Output array with the same shape as `arr` and all non-NaN values
scaled from interval [in_min, in_max] to [out_min, out_max].

Raises
------
ValueError
When parameters are incompatible / unbroadcastable.

Examples
--------
>>> a = np.arange(6).reshape((2, 3))
>>> np.rescale(a)
array([[ 0. , 0.2, 0.4],
[ 0.6, 0.8, 1. ]])
>>> np.rescale(a, -1, 1, axis=1)
array([[-1., 0., 1.],
[-1., 0., 1.]])
>>> np.rescale(a, -1, a.max(axis=1, keepdims=True), axis=1)
array([[-1. , 0.5, 2. ],
[-1. , 2. , 5. ]])
>>> np.rescale(a, 0, 1, 1, 3)
array([[-0.5, 0. , 0.5],
[ 1. , 1.5, 2. ]])
"""
arr = asanyarray(arr)
out_min = asanyarray(out_min)
out_max = asanyarray(out_max)

if in_min is None:
in_min = np.nanmin(arr, axis=axis, keepdims=True)
if in_max is None:
in_max = np.nanmax(arr, axis=axis, keepdims=True)

res = (arr - in_min) / (in_max - in_min) * (out_max - out_min) + out_min

if out is None:
return res
out[...] = res
return out
80 changes: 79 additions & 1 deletion numpy/lib/tests/test_function_base.py
DA1F
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
delete, diff, digitize, extract, flipud, gradient, hamming, hanning,
histogram, histogramdd, i0, insert, interp, kaiser, meshgrid, msort,
piecewise, place, rot90, select, setxor1d, sinc, split, trapz, trim_zeros,
unwrap, unique, vectorize
unwrap, unique, vectorize, rescale
)

from numpy.compat import long
Expand Down Expand Up @@ -2975,6 +2975,84 @@ def test_keepdims(self):
(1, 1, 7, 1))


class TestRescale(object):
def setup(self):
self.arr = np.array([[0, 1, 2, 3],
[5, 4, 3, 2]], dtype=float)
self.arr3d = np.arange(8, dtype=float).reshape((2, 2, 2))

def test_basic(self):
assert_equal(rescale(self.arr),
[[0, .2, .4, .6],
[1, .8, .6, .4]])
assert_equal(rescale(self.arr, self.arr.min(), self.arr.max()),
self.arr)

def test_min_max(self):
assert_almost_equal(rescale(self.arr, -1, 2),
[[-1, -.4, .2, .8],
[2, 1.4, .8, .2]])
assert_almost_equal(rescale(self.arr, 1, 0),
[[1, .8, .6, .4],
[0, .2, .4, .6]])
assert_equal(rescale(self.arr, [1, 0, 1, 2], [2, 1, 1, 3], axis=0),
[[1, 0, 1, 3],
[2, 1, 1, 2]])
assert_equal(rescale(self.arr, [[0], [0]], [[1], [1]], axis=1),
[[0, 1/3, 2/3, 1],
[1, 2/3, 1/3, 0]])
assert_equal(rescale(self.arr3d, out_max=3.5),
(self.arr3d / 2).reshape((2, 2, 2)))

assert_equal(rescale(self.arr, in_min=1, in_max=2, axis=1),
[[-1, 0, 1, 2],
[ 4, 3, 2, 1]])

def test_axis(self):
assert_equal(rescale(self.arr, axis=0),
[[0, 0, 0, 1],
[1, 1, 1, 0]])
assert_equal(rescale(self.arr, axis=1),
[[0, 1/3, 2/3, 1],
[1, 2/3, 1/3, 0]])
assert_equal(rescale(self.arr3d, axis=2),
[[[0, 1],
[0, 1]],
[[0, 1],
[0, 1]]])

def test_out(self):
out = np.zeros_like(self.arr)
assert_equal(rescale(self.arr, self.arr.min(), self.arr.max(), out=out),
self.arr)

def test_nan(self):
arr = np.array([[1, 2],
[1, 1]])
with np.errstate(all='ignore'):
assert_equal(rescale(arr, axis=0),
[[np.nan, 1],
[np.nan, 0]])
assert_equal(rescale(arr, axis=1),
[[0, 1],
[np.nan, np.nan]])

with np.errstate(all='warn'):
assert_warns(RuntimeWarning,
lambda: rescale(arr, axis=0))

arr = np.array([0, np.nan, 2])
assert_equal(rescale(arr), [0, np.nan, 1])

def test_exceptions(self):
assert_raises(ValueError,
lambda: rescale(self.arr, [0, 1], [1, 2]))
assert_raises(ValueError,
lambda: rescale(self.arr, [0, 1], [1, 2], axis=0))
assert_raises(ValueError,
lambda: rescale(self.arr, axis=2))


class TestAdd_newdoc_ufunc(object):

def test_ufunc_arg(self):
Expand Down
0