Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

[numpy] add op median #17084

Merged
merged 3 commits into from
Apr 2, 2020
Merged
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
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 @@ -6926,6 +6926,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 @@ -7376,6 +7376,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)

Yiyan66 marked this conversation as resolved.
Show resolved Hide resolved

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