summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2009-08-03 17:31:58 +0000
committerCharles Harris <charlesr.harris@gmail.com>2009-08-03 17:31:58 +0000
commit6951bcd1d14a391e2ac2c88ea1a65c9973ab5968 (patch)
tree5034ee7de17d1cffc0b359902c7c2dfafb9512e4 /numpy
parent6d91277b97e2dd0fbad849ae38b9ccc26aa17eed (diff)
downloadnumpy-6951bcd1d14a391e2ac2c88ea1a65c9973ab5968.tar.gz
Clarify logic for nan comparisons.
Some coding style cleanups in arraytypes.c.src. Add documentation to release notes for 1.4.0. Add documentation of sort order to the sort docstring.
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/fromnumeric.py22
-rw-r--r--numpy/core/src/multiarray/arraytypes.c.src130
2 files changed, 113 insertions, 39 deletions
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index 176ef3e6f..f7f584d3d 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -449,6 +449,22 @@ def sort(a, axis=-1, kind='quicksort', order=None):
the last axis is faster and uses less space than sorting along
any other axis.
+ The sort order for complex numbers is lexicographic. If both the real
+ and imaginary parts are non-nan then the order is determined by the
+ real parts except when they are equal, in which case the order is
+ determined by the imaginary parts.
+
+ Previous to numpy 1.4.0 sorting real and complex arrays containing nan
+ values led to undefined behaviour. In numpy versions >= 1.4.0 nan
+ values are sorted to the end. The extended sort order is:
+
+ Real: [R, nan]
+ Complex: [R + Rj, R + nanj, nan + Rj, nan + nanj]
+
+ where R is a non-nan real value. Complex values with the same nan
+ placements are sorted according to the non-nan part if it exists.
+ Non-nan values are sorted as before.
+
Examples
--------
>>> a = np.array([[1,4],[3,1]])
@@ -528,6 +544,9 @@ def argsort(a, axis=-1, kind='quicksort', order=None):
-----
See `sort` for notes on the different sorting algorithms.
+ As of Numpy 1.4.0 argsort works with real/complex arrays containing
+ nan values. The enhanced sort order is documented in the numpy.sort.
+
Examples
--------
One dimensional array:
@@ -664,6 +683,9 @@ def searchsorted(a, v, side='left'):
-----
Binary search is used to find the required insertion points.
+ As of Numpy 1.4.0 searchsorted works with real/complex arrays containing
+ nan values. The enhanced sort order is documented in the numpy.sort.
+
Examples
--------
>>> np.searchsorted([1,2,3,4,5], 3)
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src
index 88d04f099..58bad98e1 100644
--- a/numpy/core/src/multiarray/arraytypes.c.src
+++ b/numpy/core/src/multiarray/arraytypes.c.src
@@ -1747,7 +1747,22 @@ VOID_nonzero (char *ip, PyArrayObject *ap)
#undef __ALIGNED
-/****************** compare **********************************/
+/*
+ *****************************************************************************
+ ** COMPARISON FUNCTIONS **
+ *****************************************************************************
+ */
+
+/* boolean type */
+
+static int
+BOOL_compare(Bool *ip1, Bool *ip2, PyArrayObject *NPY_UNUSED(ap))
+{
+ return (*ip1 ? (*ip2 ? 0 : 1) : (*ip2 ? -1 : 0));
+}
+
+
+/* integer types */
/**begin repeat
* #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
@@ -1768,11 +1783,31 @@ static int
/**end repeat**/
+/* float types */
+
+/*
+ * The real/complex comparison functions are compatible with the new sort
+ * order for nans introduced in numpy 1.4.0. All nan values now compare
+ * larger than non-nan values and are sorted to the end. The comparison
+ * order is:
+ *
+ * Real: [R, nan]
+ * Complex: [R + Rj, R + nanj, nan + Rj, nan + nanj]
+ *
+ * where complex values with the same nan placements are sorted according
+ * to the non-nan part if it exists. If both the real and imaginary parts
+ * of complex types are non-nan the order is the same as the real parts
+ * unless they happen to be equal, in which case the order is that of the
+ * imaginary parts.
+ */
+
/**begin repeat
* #TYPE = FLOAT, DOUBLE, LONGDOUBLE#
* #type = float, double, longdouble#
*/
+#define LT(a,b) ((a) < (b) || ((b) != (b) && (a) ==(a)))
+
static int
@TYPE@_compare(@type@ *pa, @type@ *pb)
{
@@ -1780,10 +1815,10 @@ static int
const @type@ b = *pb;
int ret;
- if (a < b || (b != b && a == a)) {
+ if (LT(a,b)) {
ret = -1;
}
- else if (a > b || (a != a && b == b)) {
+ else if (LT(b,a)) {
ret = 1;
}
else {
@@ -1810,19 +1845,19 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb)
ret = 1;
}
}
- else if (ar > br) {
- if (bi != bi && ai == ai) {
- ret = -1;
+ else if (br < ar) {
+ if (bi == bi || ai != ai) {
+ ret = 1;
}
else {
- ret = 1;
+ ret = -1;
}
}
else if (ar == br || (ar != ar && br != br)) {
- if (ai < bi || (bi != bi && ai == ai)) {
+ if (LT(ai,bi)) {
ret = -1;
}
- else if (ai > bi || (ai != ai && bi == bi)) {
+ else if (LT(bi,ai)) {
ret = 1;
}
else {
@@ -1838,26 +1873,32 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb)
return ret;
}
+
+#undef LT
+
/**end repeat**/
-static int
-BOOL_compare(Bool *ip1, Bool *ip2, PyArrayObject *NPY_UNUSED(ap))
-{
- return (*ip1 ? (*ip2 ? 0 : 1) : (*ip2 ? -1 : 0));
-}
+/* object type */
static int
OBJECT_compare(PyObject **ip1, PyObject **ip2, PyArrayObject *NPY_UNUSED(ap))
{
if ((*ip1 == NULL) || (*ip2 == NULL)) {
- if (ip1 == ip2) return 1;
- if (ip1 == NULL) return -1;
+ if (ip1 == ip2) {
+ return 1;
+ }
+ if (ip1 == NULL) {
+ return -1;
+ }
return 1;
}
return PyObject_Compare(*ip1, *ip2);
}
+
+/* string type */
+
static int
STRING_compare(char *ip1, char *ip2, PyArrayObject *ap)
{
@@ -1874,33 +1915,38 @@ STRING_compare(char *ip1, char *ip2, PyArrayObject *ap)
return 0;
}
-/* taken from Python */
+
+/* unicode type */
+
static int
UNICODE_compare(PyArray_UCS4 *ip1, PyArray_UCS4 *ip2,
PyArrayObject *ap)
{
- int itemsize=ap->descr->elsize;
- PyArray_UCS4 c1, c2;
-
- if (itemsize < 0) return 0;
+ int itemsize = ap->descr->elsize;
+ if (itemsize < 0) {
+ return 0;
+ }
while(itemsize-- > 0) {
- c1 = *ip1++;
- c2 = *ip2++;
-
- if (c1 != c2)
+ PyArray_UCS4 c1 = *ip1++;
+ PyArray_UCS4 c2 = *ip2++;
+ if (c1 != c2) {
return (c1 < c2) ? -1 : 1;
+ }
}
return 0;
}
-/* If fields are defined, then compare on first field and if equal
- compare on second field. Continue until done or comparison results
- in not_equal.
- Must align data passed on to sub-comparisons.
-*/
+/* void type */
+/*
+ * If fields are defined, then compare on first field and if equal
+ * compare on second field. Continue until done or comparison results
+ * in not_equal.
+ *
+ * Must align data passed on to sub-comparisons.
+ */
static int
VOID_compare(char *ip1, char *ip2, PyArrayObject *ap)
{
@@ -1908,17 +1954,18 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap)
PyObject *names, *key;
PyObject *tup, *title;
char *nip1, *nip2;
- int i, offset, res=0;
+ int i, offset, res = 0;
- if (!PyArray_HASFIELDS(ap))
+ if (!PyArray_HASFIELDS(ap)) {
return STRING_compare(ip1, ip2, ap);
-
+ }
descr = ap->descr;
- /* Compare on the first-field. If equal, then
- compare on the second-field, etc.
+ /*
+ * Compare on the first-field. If equal, then
+ * compare on the second-field, etc.
*/
names = descr->names;
- for (i=0; i<PyTuple_GET_SIZE(names); i++) {
+ for (i = 0; i < PyTuple_GET_SIZE(names); i++) {
key = PyTuple_GET_ITEM(names, i);
tup = PyDict_GetItem(descr->fields, key);
if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset,
@@ -1932,15 +1979,18 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap)
if (((intp)(nip1) % new->alignment) != 0) {
/* create buffer and copy */
nip1 = _pya_malloc(new->elsize);
- if (nip1 == NULL) goto finish;
+ if (nip1 == NULL) {
+ goto finish;
+ }
memcpy(nip1, ip1+offset, new->elsize);
}
if (((intp)(nip2) % new->alignment) != 0) {
/* copy data to a buffer */
nip2 = _pya_malloc(new->elsize);
if (nip2 == NULL) {
- if (nip1 != ip1+offset)
+ if (nip1 != ip1+offset) {
_pya_free(nip1);
+ }
goto finish;
}
memcpy(nip2, ip2+offset, new->elsize);
@@ -1955,7 +2005,9 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap)
_pya_free(nip2);
}
}
- if (res != 0) break;
+ if (res != 0) {
+ break;
+ }
}
finish: