Skip to content

Commit

Permalink
[numpy] add op median (apache#17084)
Browse files Browse the repository at this point in the history
* part

* wrapper

* sanity
  • Loading branch information
Yiyan66 authored and shuo-ouyang committed Aug 7, 2020
1 parent 64d93ca commit 917aada
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 4 deletions.
2 changes: 1 addition & 1 deletion 3rdparty/mkldnn
51 changes: 50 additions & 1 deletion python/mxnet/ndarray/numpy/_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'matmul',
'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', 'histogram',
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'argsort', 'all', 'any', 'sort',
'tensordot', 'eye', 'linspace',
'tensordot', 'eye', 'linspace', 'median',
'logspace', 'expand_dims', 'tile', 'arange', 'array_split', 'split', 'hsplit', 'vsplit', 'dsplit',
'concatenate', 'append', 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack',
'average', 'mean', 'maximum', 'fmax', 'minimum', 'fmin', 'around', 'round', 'round_', 'flatnonzero',
Expand Down Expand Up @@ -6923,6 +6923,55 @@ def percentile(a, q, axis=None, out=None, overwrite_input=None, interpolation='l
return _api_internal.percentile(a, q, axis, interpolation, keepdims, out)


@set_module('mxnet.ndarray.numpy')
def median(a, axis=None, out=None, overwrite_input=None, keepdims=False):
r"""
Compute the median along the specified axis.
Returns the median of the array elements.
Parameters
----------
a : array_like
Input array or object that can be converted to an array.
axis : {int, sequence of int, None}, optional
Axis or axes along which the medians are computed. The default
is to compute the median along a flattened version of the array.
A sequence of axes is supported since version 1.9.0.
out : ndarray, optional
Alternative output array in which to place the result. It must
have the same shape and buffer length as the expected output,
but the type (of the output) will be cast if necessary.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the original `arr`.
Returns
-------
median : ndarray
A new array holding the result. If the input contains integers
or floats smaller than ``float32``, then the output data-type is
``np.float32``. Otherwise, the data-type of the output is the
same as that of the input. If `out` is specified, that array is
returned instead.
See Also
--------
mean, percentile
Examples
--------
>>> a = np.array([[10, 7, 4], [3, 2, 1]])
>>> a
array([[10, 7, 4],
[ 3, 2, 1]])
>>> np.median(a)
3.5
>>> np.median(a, axis=0)
array([6.5, 4.5, 2.5])
>>> np.median(a, axis=1)
array([7., 2.])
"""
return quantile(a=a, q=0.5, axis=axis, out=out, overwrite_input=overwrite_input,
interpolation='midpoint', keepdims=keepdims)


@set_module('mxnet.ndarray.numpy')
def quantile(a, q, axis=None, out=None, overwrite_input=None, interpolation='linear', keepdims=False): # pylint: disable=too-many-arguments
"""
Expand Down
51 changes: 50 additions & 1 deletion python/mxnet/numpy/multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from . import fallback


__all__ = ['ndarray', 'empty', 'empty_like', 'array', 'shape',
__all__ = ['ndarray', 'empty', 'empty_like', 'array', 'shape', 'median',
'zeros', 'zeros_like', 'ones', 'ones_like', 'full', 'full_like', 'all', 'any', 'broadcast_to',
'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'fmod', 'power', 'bitwise_not',
'delete',
Expand Down Expand Up @@ -8981,6 +8981,55 @@ def percentile(a, q, axis=None, out=None, overwrite_input=None, interpolation='l
interpolation=interpolation, keepdims=keepdims)


@set_module('mxnet.numpy')
def median(a, axis=None, out=None, overwrite_input=None, keepdims=False):
r"""
Compute the median along the specified axis.
Returns the median of the array elements.
Parameters
----------
a : array_like
Input array or object that can be converted to an array.
axis : {int, sequence of int, None}, optional
Axis or axes along which the medians are computed. The default
is to compute the median along a flattened version of the array.
A sequence of axes is supported since version 1.9.0.
out : ndarray, optional
Alternative output array in which to place the result. It must
have the same shape and buffer length as the expected output,
but the type (of the output) will be cast if necessary.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the original `arr`.
Returns
-------
median : ndarray
A new array holding the result. If the input contains integers
or floats smaller than ``float32``, then the output data-type is
``np.float32``. Otherwise, the data-type of the output is the
same as that of the input. If `out` is specified, that array is
returned instead.
See Also
--------
mean, percentile
Examples
--------
>>> a = np.array([[10, 7, 4], [3, 2, 1]])
>>> a
array([[10, 7, 4],
[ 3, 2, 1]])
>>> np.median(a)
3.5
>>> np.median(a, axis=0)
array([6.5, 4.5, 2.5])
>>> np.median(a, axis=1)
array([7., 2.])
"""
return _mx_nd_np.median(a, axis=axis, overwrite_input=overwrite_input,
keepdims=keepdims, out=out)


@set_module('mxnet.numpy')
def quantile(a, q, axis=None, out=None, overwrite_input=None, interpolation='linear', keepdims=False): # pylint: disable=too-many-arguments
"""
Expand Down
1 change: 1 addition & 0 deletions python/mxnet/numpy_dispatch_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def _run_with_array_ufunc_proto(*args, **kwargs):
'shares_memory',
'may_share_memory',
'quantile',
'median',
'percentile',
'diff',
'ediff1d',
Expand Down
39 changes: 38 additions & 1 deletion python/mxnet/symbol/numpy/_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
'delete', 'add', 'broadcast_to', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'fmod',
'power', 'arctan2',
'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'fabs', 'exp',
'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'matmul',
'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'matmul', 'median',
'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', 'histogram', 'insert',
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'argsort', 'sort', 'tensordot', 'eye', 'linspace',
'logspace', 'expand_dims', 'tile', 'arange', 'array_split', 'split', 'hsplit', 'vsplit', 'dsplit',
Expand Down Expand Up @@ -6163,6 +6163,43 @@ def percentile(a, q, axis=None, out=None, overwrite_input=None, interpolation='l
keepdims=keepdims, q_scalar=None, out=out)


@set_module('mxnet.symbol.numpy')
def median(a, axis=None, out=None, overwrite_input=None, keepdims=False):
r"""
Compute the median along the specified axis.
Returns the median of the array elements.
Parameters
----------
a : _Symbol
Input array or object that can be converted to an array.
axis : {int, sequence of int, None}, optional
Axis or axes along which the medians are computed. The default
is to compute the median along a flattened version of the array.
A sequence of axes is supported since version 1.9.0.
out : _Symbol, optional
Alternative output array in which to place the result. It must
have the same shape and buffer length as the expected output,
but the type (of the output) will be cast if necessary.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
in the result as dimensions with size one. With this option,
the result will broadcast correctly against the original `arr`.
Returns
-------
median : _Symbol
A new array holding the result. If the input contains integers
or floats smaller than ``float32``, then the output data-type is
``np.float32``. Otherwise, the data-type of the output is the
same as that of the input. If `out` is specified, that array is
returned instead.
See Also
--------
mean, percentile
"""
return quantile(a=a, q=0.5, axis=axis, out=out, overwrite_input=overwrite_input,
interpolation='midpoint', keepdims=keepdims)


@set_module('mxnet.symbol.numpy')
def quantile(a, q, axis=None, out=None, overwrite_input=None, interpolation='linear', keepdims=False): # pylint: disable=too-many-arguments
"""
Expand Down
9 changes: 9 additions & 0 deletions tests/python/unittest/test_numpy_interoperability.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ def _add_workload_diagonal():
OpArgMngr.add_workload('diagonal', B, 0, 2, 1)


def _add_workload_median(array_pool):
OpArgMngr.add_workload('median', array_pool['4x1'])
OpArgMngr.add_workload('median', array_pool['4x1'], axis=0, keepdims=True)
OpArgMngr.add_workload('median', np.array([[1, 2, 3], [4, 5, 6]]))
OpArgMngr.add_workload('median', np.array([[1, 2, 3], [4, 5, 6]]), axis=0)
OpArgMngr.add_workload('median', np.array([[1, 2, 3], [4, 5, 6]]), axis=1)


def _add_workload_quantile():
x1 = np.arange(8) * 0.5
x2 = np.arange(100.)
Expand Down Expand Up @@ -2915,6 +2923,7 @@ def _prepare_workloads():
_add_workload_diff()
_add_workload_ediff1d()
_add_workload_quantile()
_add_workload_median(array_pool)
_add_workload_percentile()
_add_workload_resize()
_add_workload_full_like(array_pool)
Expand Down
40 changes: 40 additions & 0 deletions tests/python/unittest/test_numpy_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -7388,6 +7388,46 @@ def test_np_share_memory():
assert not op(np.ones((5, 0), dtype=dt), np.ones((0, 3, 0), dtype=adt))


def test_np_median():
class TestMedian(HybridBlock):
def __init__(self, axis=None, keepdims=False):
super(TestMedian, self).__init__()
self._axis = axis
self._keepdims = keepdims

def hybrid_forward(self, F, a):
return F.np.median(a, axis=self._axis, keepdims=self._keepdims)

flags = [True, False]
dtypes = ['float16', 'float32', 'float64']
qtypes = ['float32', 'float64']
tensor_shapes = [
((2, 3), None),
((2, 3, 4, 5), 3),
((2, 3, 4), (0, 2)),
((2, 3, 4), 1)
]

for hybridize, keepdims, (a_shape, axis), dtype in \
itertools.product(flags, flags, tensor_shapes, dtypes):
atol = 3e-4 if dtype == 'float16' else 1e-4
rtol = 3e-2 if dtype == 'float16' else 1e-2
test_median = TestMedian(axis=axis, keepdims=keepdims)
if hybridize:
test_median.hybridize()
a = np.random.uniform(-1.0, 1.0, size=a_shape)
np_out = _np.median(a.asnumpy(), axis=axis, keepdims=keepdims)
mx_out = test_median(a)

assert mx_out.shape == np_out.shape
assert_almost_equal(mx_out.asnumpy(), np_out, atol=atol, rtol=rtol)

mx_out = np.median(a, axis=axis, keepdims=keepdims)
np_out = _np.median(a.asnumpy(), axis=axis, keepdims=keepdims)

assert_almost_equal(mx_out.asnumpy(), np_out, atol=atol, rtol=rtol)


@with_seed()
@use_np
def test_np_quantile():
Expand Down

0 comments on commit 917aada

Please sign in to comment.