summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHameer Abbasi <hameerabbasi@yahoo.com>2019-05-10 19:14:35 -0700
committerHameer Abbasi <hameerabbasi@yahoo.com>2019-05-11 15:03:48 -0700
commit12fb1015511ac3804d0785bb0d1fe539385548ad (patch)
treea7ba6590987c2e7a048347c1682f1012e10d6cfb
parent634d66d5c5461c6c1480e97c1d3fbc4c4ea96a00 (diff)
downloadnumpy-12fb1015511ac3804d0785bb0d1fe539385548ad.tar.gz
ENH: Radix sort
-rw-r--r--.gitignore1
-rw-r--r--doc/release/1.17.0-notes.rst11
-rw-r--r--numpy/core/_add_newdocs.py4
-rw-r--r--numpy/core/defchararray.py2
-rw-r--r--numpy/core/fromnumeric.py16
-rw-r--r--numpy/core/setup.py1
-rw-r--r--numpy/core/src/common/npy_sort.h.src11
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src13
-rw-r--r--numpy/core/src/multiarray/conversion_utils.c9
-rw-r--r--numpy/core/src/multiarray/item_selection.c12
-rw-r--r--numpy/core/src/npysort/radixsort.c.src229
-rw-r--r--numpy/core/tests/test_multiarray.py35
-rw-r--r--numpy/ma/core.py14
13 files changed, 313 insertions, 45 deletions
diff --git a/.gitignore b/.gitignore
index a31d6ea44..8e96d4154 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,6 +142,7 @@ numpy/core/src/npysort/binsearch.c
numpy/core/src/npysort/heapsort.c
numpy/core/src/npysort/mergesort.c
numpy/core/src/npysort/quicksort.c
+numpy/core/src/npysort/radixsort.c
numpy/core/src/npysort/selection.c
numpy/core/src/npysort/timsort.c
numpy/core/src/npysort/sort.c
diff --git a/doc/release/1.17.0-notes.rst b/doc/release/1.17.0-notes.rst
index 71ad17673..72a45823d 100644
--- a/doc/release/1.17.0-notes.rst
+++ b/doc/release/1.17.0-notes.rst
@@ -110,6 +110,9 @@ random data. The algorithm is stable and requires O(n/2) working space. For
details of the algorithm, refer to
`CPython listsort.txt <https://github.com/python/cpython/blob/3.7/Objects/listsort.txt>`_.
+In addition, for very small dtypes, radix sort is used instead of timsort. In
+general, we attempt to use the fastest possible implementation.
+
``np.unpackbits`` now accepts a ``count`` parameter
---------------------------------------------------
``count`` allows subsetting the number of bits that will be unpacked up-front,
@@ -171,6 +174,14 @@ maintains `O(N log N)` run time complexity instead of deteriorating towards
`O(N*N)` for prime lengths. Also, accuracy for real-valued FFTs with near-prime
lengths has improved and is on par with complex-valued FFTs.
+Performance improvements for integer sorts
+------------------------------------------
+
+``sort``, ``argsort``, ``ndarray.sort`` and ``ndarray.argsort`` now use radix
+sort as the default stable sort for integers and booleans. This is faster than
+the old default, mergesort, in the vast majority of cases.
+
+
Further improvements to ``ctypes`` support in ``np.ctypeslib``
--------------------------------------------------------------
A new `numpy.ctypeslib.as_ctypes_type` function has been added, which can be
diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py
index 52ab9c994..3e9d05674 100644
--- a/numpy/core/_add_newdocs.py
+++ b/numpy/core/_add_newdocs.py
@@ -2614,7 +2614,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('argmin',
add_newdoc('numpy.core.multiarray', 'ndarray', ('argsort',
"""
- a.argsort(axis=-1, kind='quicksort', order=None)
+ a.argsort(axis=-1, kind=None, order=None)
Returns the indices that would sort this array.
@@ -3800,7 +3800,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('setflags',
add_newdoc('numpy.core.multiarray', 'ndarray', ('sort',
"""
- a.sort(axis=-1, kind='quicksort', order=None)
+ a.sort(axis=-1, kind=None, order=None)
Sort an array in-place. Refer to `numpy.sort` for full documentation.
diff --git a/numpy/core/defchararray.py b/numpy/core/defchararray.py
index 3fd7d14c4..d7ecce1b4 100644
--- a/numpy/core/defchararray.py
+++ b/numpy/core/defchararray.py
@@ -2124,7 +2124,7 @@ class chararray(ndarray):
def __rmod__(self, other):
return NotImplemented
- def argsort(self, axis=-1, kind='quicksort', order=None):
+ def argsort(self, axis=-1, kind=None, order=None):
"""
Return the indices that sort the array lexicographically.
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index b4d721940..7024ac237 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -824,7 +824,7 @@ def _sort_dispatcher(a, axis=None, kind=None, order=None):
@array_function_dispatch(_sort_dispatcher)
-def sort(a, axis=-1, kind='quicksort', order=None):
+def sort(a, axis=-1, kind=None, order=None):
"""
Return a sorted copy of an array.
@@ -837,8 +837,8 @@ def sort(a, axis=-1, kind='quicksort', order=None):
sorting. The default is -1, which sorts along the last axis.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
Sorting algorithm. The default is 'quicksort'. Note that both 'stable'
- and 'mergesort' use timsort under the covers and, in general, the
- actual implementation will vary with data type. The 'mergesort' option
+ and 'mergesort' use timsort or radix sort under the covers and, in general,
+ the actual implementation will vary with data type. The 'mergesort' option
is retained for backwards compatibility.
.. versionchanged:: 1.15.0.
@@ -914,7 +914,8 @@ def sort(a, axis=-1, kind='quicksort', order=None):
'stable' automatically choses the best stable sorting algorithm
for the data type being sorted. It, along with 'mergesort' is
- currently mapped to timsort. API forward compatibility currently limits the
+ currently mapped to timsort or radix sort depending on the
+ data type. API forward compatibility currently limits the
ability to select the implementation and it is hardwired for the different
data types.
@@ -925,7 +926,8 @@ def sort(a, axis=-1, kind='quicksort', order=None):
mergesort. It is now used for stable sort while quicksort is still the
default sort if none is chosen. For details of timsort, refer to
`CPython listsort.txt <https://github.com/python/cpython/blob/3.7/Objects/listsort.txt>`_.
-
+ 'mergesort' and 'stable' are mapped to radix sort for integer data types. Radix sort is an
+ O(n) sort instead of O(n log n).
Examples
--------
@@ -974,7 +976,7 @@ def _argsort_dispatcher(a, axis=None, kind=None, order=None):
@array_function_dispatch(_argsort_dispatcher)
-def argsort(a, axis=-1, kind='quicksort', order=None):
+def argsort(a, axis=-1, kind=None, order=None):
"""
Returns the indices that would sort an array.
@@ -997,8 +999,6 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
.. versionchanged:: 1.15.0.
The 'stable' option was added.
-
-
order : str or list of str, optional
When `a` is an array with fields defined, this argument specifies
which fields to compare first, second, etc. A single field can
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index 45b4fb3c7..81d140d5e 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -710,6 +710,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'npysort', 'mergesort.c.src'),
join('src', 'npysort', 'timsort.c.src'),
join('src', 'npysort', 'heapsort.c.src'),
+ join('src', 'npysort', 'radixsort.c.src'),
join('src', 'common', 'npy_partition.h.src'),
join('src', 'npysort', 'selection.c.src'),
join('src', 'common', 'npy_binsearch.h.src'),
diff --git a/numpy/core/src/common/npy_sort.h.src b/numpy/core/src/common/npy_sort.h.src
index 521f0fee5..16a105499 100644
--- a/numpy/core/src/common/npy_sort.h.src
+++ b/numpy/core/src/common/npy_sort.h.src
@@ -44,6 +44,17 @@ int atimsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
/**end repeat**/
+/**begin repeat
+ *
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong#
+ */
+
+int radixsort_@suff@(void *vec, npy_intp cnt, void *null);
+int aradixsort_@suff@(void *vec, npy_intp *ind, npy_intp cnt, void *null);
+
+/**end repeat**/
+
/*
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 49819ca4a..c2b932d8d 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -4405,6 +4405,7 @@ static PyArray_Descr @from@_Descr = {
* npy_half, npy_float, npy_double, npy_longdouble,
* npy_cfloat, npy_cdouble, npy_clongdouble,
* PyObject *, npy_datetime, npy_timedelta#
+ * #rsort = 1*5, 0*16#
* #NAME = Bool,
* Byte, UByte, Short, UShort, Int, UInt,
* Long, ULong, LongLong, ULongLong,
@@ -4461,12 +4462,20 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
{
quicksort_@suff@,
heapsort_@suff@,
- timsort_@suff@
+ #if @rsort@
+ radixsort_@suff@
+ #else
+ timsort_@suff@
+ #endif
},
{
aquicksort_@suff@,
aheapsort_@suff@,
- atimsort_@suff@
+ #if @rsort@
+ aradixsort_@suff@
+ #else
+ atimsort_@suff@
+ #endif
},
#else
{
diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c
index 9afcfd86d..a370874a6 100644
--- a/numpy/core/src/multiarray/conversion_utils.c
+++ b/numpy/core/src/multiarray/conversion_utils.c
@@ -393,6 +393,11 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
char *str;
PyObject *tmp = NULL;
+ if (obj == Py_None) {
+ *sortkind = NPY_QUICKSORT;
+ return NPY_SUCCEED;
+ }
+
if (PyUnicode_Check(obj)) {
obj = tmp = PyUnicode_AsASCIIString(obj);
if (obj == NULL) {
@@ -401,6 +406,8 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
}
*sortkind = NPY_QUICKSORT;
+
+
str = PyBytes_AsString(obj);
if (!str) {
Py_XDECREF(tmp);
@@ -424,7 +431,7 @@ PyArray_SortkindConverter(PyObject *obj, NPY_SORTKIND *sortkind)
* That maintains backwards compatibility while
* allowing other types of stable sorts to be used.
*/
- *sortkind = NPY_STABLESORT;
+ *sortkind = NPY_MERGESORT;
}
else if (str[0] == 's' || str[0] == 'S') {
/*
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index 6065c1df4..f4d2513ca 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -1126,7 +1126,7 @@ fail:
NPY_NO_EXPORT int
PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
{
- PyArray_SortFunc *sort;
+ PyArray_SortFunc *sort = NULL;
int n = PyArray_NDIM(op);
if (check_and_adjust_axis(&axis, n) < 0) {
@@ -1143,6 +1143,7 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
}
sort = PyArray_DESCR(op)->f->sort[which];
+
if (sort == NULL) {
if (PyArray_DESCR(op)->f->compare) {
switch (which) {
@@ -1284,16 +1285,11 @@ NPY_NO_EXPORT PyObject *
PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which)
{
PyArrayObject *op2;
- PyArray_ArgSortFunc *argsort;
+ PyArray_ArgSortFunc *argsort = NULL;
PyObject *ret;
- if (which < 0 || which >= NPY_NSORTS) {
- PyErr_SetString(PyExc_ValueError,
- "not a valid sort kind");
- return NULL;
- }
-
argsort = PyArray_DESCR(op)->f->argsort[which];
+
if (argsort == NULL) {
if (PyArray_DESCR(op)->f->compare) {
switch (which) {
diff --git a/numpy/core/src/npysort/radixsort.c.src b/numpy/core/src/npysort/radixsort.c.src
new file mode 100644
index 000000000..c1435bd96
--- /dev/null
+++ b/numpy/core/src/npysort/radixsort.c.src
@@ -0,0 +1,229 @@
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "npy_sort.h"
+#include "npysort_common.h"
+#include <stdlib.h>
+
+/*
+ *****************************************************************************
+ ** INTEGER SORTS **
+ *****************************************************************************
+ */
+
+
+/**begin repeat
+ *
+ * #TYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
+ * LONGLONG, ULONGLONG#
+ * #suff = bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ * longlong, ulonglong#
+ * #type = npy_ubyte, npy_ubyte, npy_ubyte, npy_ushort, npy_ushort, npy_uint,
+ * npy_uint, npy_ulong, npy_ulong, npy_ulonglong, npy_ulonglong#
+ * #sign = 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0#
+ * #floating = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0#
+ */
+
+// Reference: https://github.com/eloj/radix-sorting#-key-derivation
+#if @sign@
+ // Floating-point is currently disabled.
+ // Floating-point tests succeed for double and float on macOS but not on Windows/Linux.
+ // Basic sorting tests succeed but others relying on sort fail.
+ // Possibly related to floating-point normalisation or multiple NaN reprs? Not sure.
+ #if @floating@
+ // For floats, we invert the key if the sign bit is set, else we invert the sign bit.
+ #define KEY_OF(x) ((x) ^ (-((x) >> (sizeof(@type@) * 8 - 1)) | ((@type@)1 << (sizeof(@type@) * 8 - 1))))
+ #else
+ // For signed ints, we flip the sign bit so the negatives are below the positives.
+ #define KEY_OF(x) ((x) ^ ((@type@)1 << (sizeof(@type@) * 8 - 1)))
+ #endif
+#else
+ // For unsigned ints, the key is as-is
+ #define KEY_OF(x) (x)
+#endif
+
+static inline npy_ubyte
+nth_byte_@suff@(@type@ key, npy_intp l) {
+ return (key >> (l << 3)) & 0xFF;
+}
+
+@type@*
+radixsort0_@suff@(@type@ *arr, @type@ *aux, npy_intp num)
+{
+ npy_intp cnt[sizeof(@type@)][1 << 8] = { { 0 } };
+ npy_intp i, l;
+ @type@ key0 = KEY_OF(arr[0]);
+ npy_intp ncols = 0;
+ npy_ubyte cols[sizeof(@type@)];
+
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ cnt[l][nth_byte_@suff@(k, l)]++;
+ }
+ }
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ if (cnt[l][nth_byte_@suff@(key0, l)] != num) {
+ cols[ncols++] = l;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp a = 0;
+ for (i = 0; i < 256; i++) {
+ npy_intp b = cnt[cols[l]][i];
+ cnt[cols[l]][i] = a;
+ a += b;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ @type@* temp;
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+ npy_intp dst = cnt[cols[l]][nth_byte_@suff@(k, cols[l])]++;
+ aux[dst] = arr[i];
+ }
+
+ temp = aux;
+ aux = arr;
+ arr = temp;
+ }
+
+ return arr;
+}
+
+int
+radixsort_@suff@(void *start, npy_intp num, void *NPY_UNUSED(varr))
+{
+ void *sorted;
+ @type@ *aux;
+ @type@ *arr = start;
+ @type@ k1, k2;
+ npy_bool all_sorted = 1;
+
+ if (num < 2) {
+ return 0;
+ }
+
+ k1 = KEY_OF(arr[0]);
+ for (npy_intp i = 1; i < num; i++) {
+ k2 = KEY_OF(arr[i]);
+ if (k1 > k2) {
+ all_sorted = 0;
+ break;
+ }
+ k1 = k2;
+ }
+
+ if (all_sorted) {
+ return 0;
+ }
+
+ aux = malloc(num * sizeof(@type@));
+ if (aux == NULL) {
+ return -NPY_ENOMEM;
+ }
+
+ sorted = radixsort0_@suff@(start, aux, num);
+ if (sorted != start) {
+ memcpy(start, sorted, num * sizeof(@type@));
+ }
+
+ free(aux);
+ return 0;
+}
+
+npy_intp*
+aradixsort0_@suff@(@type@ *arr, npy_intp *aux, npy_intp *tosort, npy_intp num)
+{
+ npy_intp cnt[sizeof(@type@)][1 << 8] = { { 0 } };
+ npy_intp i, l;
+ @type@ key0 = KEY_OF(arr[0]);
+ npy_intp ncols = 0;
+ npy_ubyte cols[sizeof(@type@)];
+
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[i]);
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ cnt[l][nth_byte_@suff@(k, l)]++;
+ }
+ }
+
+ for (l = 0; l < sizeof(@type@); l++) {
+ if (cnt[l][nth_byte_@suff@(key0, l)] != num) {
+ cols[ncols++] = l;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp a = 0;
+ for (i = 0; i < 256; i++) {
+ npy_intp b = cnt[cols[l]][i];
+ cnt[cols[l]][i] = a;
+ a += b;
+ }
+ }
+
+ for (l = 0; l < ncols; l++) {
+ npy_intp* temp;
+ for (i = 0; i < num; i++) {
+ @type@ k = KEY_OF(arr[tosort[i]]);
+ npy_intp dst = cnt[cols[l]][nth_byte_@suff@(k, cols[l])]++;
+ aux[dst] = tosort[i];
+ }
+
+ temp = aux;
+ aux = tosort;
+ tosort = temp;
+ }
+
+ return tosort;
+}
+
+int
+aradixsort_@suff@(void *start, npy_intp* tosort, npy_intp num, void *NPY_UNUSED(varr))
+{
+ npy_intp *sorted;
+ npy_intp *aux;
+ @type@ *arr = start;
+ @type@ k1, k2;
+ npy_bool all_sorted = 1;
+
+ if (num < 2) {
+ return 0;
+ }
+
+ k1 = KEY_OF(arr[0]);
+ for (npy_intp i = 1; i < num; i++) {
+ k2 = KEY_OF(arr[i]);
+ if (k1 > k2) {
+ all_sorted = 0;
+ break;
+ }
+ k1 = k2;
+ }
+
+ if (all_sorted) {
+ return 0;
+ }
+
+ aux = malloc(num * sizeof(npy_intp));
+ if (aux == NULL) {
+ return -NPY_ENOMEM;
+ }
+
+ sorted = aradixsort0_@suff@(start, aux, tosort, num);
+ if (sorted != tosort) {
+ memcpy(tosort, sorted, num * sizeof(npy_intp));
+ }
+
+ free(aux);
+ return 0;
+}
+
+#undef KEY_OF
+
+/**end repeat**/
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index b29daa675..f5d06588e 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -1616,16 +1616,17 @@ class TestMethods(object):
# of sorted items must be greater than ~50 to check the actual
# algorithm because quick and merge sort fall over to insertion
# sort for small arrays.
- a = np.arange(101)
- b = a[::-1].copy()
- for kind in self.sort_kinds:
- msg = "scalar sort, kind=%s" % kind
- c = a.copy()
- c.sort(kind=kind)
- assert_equal(c, a, msg)
- c = b.copy()
- c.sort(kind=kind)
- assert_equal(c, a, msg)
+ for dtype in [np.int32, np.uint32, np.float32]:
+ a = np.arange(101, dtype=dtype)
+ b = a[::-1].copy()
+ for kind in self.sort_kinds:
+ msg = "scalar sort, kind=%s, dtype=%s" % (kind, dtype)
+ c = a.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
+ c = b.copy()
+ c.sort(kind=kind)
+ assert_equal(c, a, msg)
# test complex sorts. These use the same code as the scalars
# but the compare function differs.
@@ -1881,12 +1882,14 @@ class TestMethods(object):
# of sorted items must be greater than ~50 to check the actual
# algorithm because quick and merge sort fall over to insertion
# sort for small arrays.
- a = np.arange(101)
- b = a[::-1].copy()
- for kind in self.sort_kinds:
- msg = "scalar argsort, kind=%s" % kind
- assert_equal(a.copy().argsort(kind=kind), a, msg)
- assert_equal(b.copy().argsort(kind=kind), b, msg)
+
+ for dtype in [np.int32, np.uint32, np.float32]:
+ a = np.arange(101, dtype=dtype)
+ b = a[::-1].copy()
+ for kind in self.sort_kinds:
+ msg = "scalar argsort, kind=%s, dtype=%s" % (kind, dtype)
+ assert_equal(a.copy().argsort(kind=kind), a, msg)
+ assert_equal(b.copy().argsort(kind=kind), b, msg)
# test complex argsorts. These use the same code as the scalars
# but the compare function differs.
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index cd0b4e0a6..7ed099722 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -5354,7 +5354,7 @@ class MaskedArray(ndarray):
out.__setmask__(self._mask)
return out
- def argsort(self, axis=np._NoValue, kind='quicksort', order=None,
+ def argsort(self, axis=np._NoValue, kind=None, order=None,
endwith=True, fill_value=None):
"""
Return an ndarray of indices that sort the array along the
@@ -5374,7 +5374,7 @@ class MaskedArray(ndarray):
Until then, the axis should be given explicitly when
``arr.ndim > 1``, to avoid a FutureWarning.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
- Sorting algorithm.
+ The sorting algorithm used.
order : list, optional
When `a` is an array with fields defined, this argument specifies
which fields to compare first, second, etc. Not all fields need be
@@ -5516,7 +5516,7 @@ class MaskedArray(ndarray):
d = self.filled(fill_value).view(ndarray)
return d.argmax(axis, out=out)
- def sort(self, axis=-1, kind='quicksort', order=None,
+ def sort(self, axis=-1, kind=None, order=None,
endwith=True, fill_value=None):
"""
Sort the array, in-place
@@ -5529,7 +5529,7 @@ class MaskedArray(ndarray):
Axis along which to sort. If None, the array is flattened before
sorting. The default is -1, which sorts along the last axis.
kind : {'quicksort', 'mergesort', 'heapsort', 'stable'}, optional
- Sorting algorithm. Default is 'quicksort'.
+ The sorting algorithm used.
order : list, optional
When `a` is a structured array, this argument specifies which fields
to compare first, second, and so on. This list does not need to
@@ -5537,7 +5537,7 @@ class MaskedArray(ndarray):
endwith : {True, False}, optional
Whether missing values (if any) should be treated as the largest values
(True) or the smallest values (False)
- When the array contains unmasked values at the same extremes of the
+ When the array contains unmasked values sorting at the same extremes of the
datatype, the ordering of these values and the masked values is
undefined.
fill_value : {var}, optional
@@ -6734,7 +6734,7 @@ def power(a, b, third=None):
argmin = _frommethod('argmin')
argmax = _frommethod('argmax')
-def argsort(a, axis=np._NoValue, kind='quicksort', order=None, endwith=True, fill_value=None):
+def argsort(a, axis=np._NoValue, kind=None, order=None, endwith=True, fill_value=None):
"Function version of the eponymous method."
a = np.asanyarray(a)
@@ -6749,7 +6749,7 @@ def argsort(a, axis=np._NoValue, kind='quicksort', order=None, endwith=True, fil
return a.argsort(axis=axis, kind=kind, order=order)
argsort.__doc__ = MaskedArray.argsort.__doc__
-def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+def sort(a, axis=-1, kind=None, order=None, endwith=True, fill_value=None):
"Function version of the eponymous method."
a = np.array(a, copy=True, subok=True)
if axis is None: