summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2016-12-26 22:55:22 +0100
committerSebastian Berg <sebastian@sipsolutions.net>2017-05-07 16:21:30 +0200
commiteb8ead6b63df45b148405bb979e0fbd4a32b4125 (patch)
treec7844059d2f5f271ecf5d675663114218a98f84b /numpy
parent491ddf4cbf18256d1578aea60c58888920895ef0 (diff)
downloadnumpy-eb8ead6b63df45b148405bb979e0fbd4a32b4125.tar.gz
ENH: Add isnat function
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/code_generators/generate_umath.py6
-rw-r--r--numpy/core/code_generators/ufunc_docstrings.py37
-rw-r--r--numpy/core/src/umath/loops.c.src9
-rw-r--r--numpy/core/src/umath/loops.h.src3
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.c26
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.h7
-rw-r--r--numpy/core/tests/test_datetime.py29
7 files changed, 116 insertions, 1 deletions
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index dfba04c18..45476f931 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -799,6 +799,12 @@ defdict = {
None,
TD(inexact, out='?'),
),
+'isnat':
+ Ufunc(1, 1, None,
+ docstrings.get('numpy.core.umath.isnat'),
+ 'PyUFunc_IsNaTTypeResolver',
+ TD(times, out='?'),
+ ),
'isinf':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isinf'),
diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py
index ed9e05b15..c783d4595 100644
--- a/numpy/core/code_generators/ufunc_docstrings.py
+++ b/numpy/core/code_generators/ufunc_docstrings.py
@@ -1690,7 +1690,7 @@ add_newdoc('numpy.core.umath', 'isnan',
See Also
--------
- isinf, isneginf, isposinf, isfinite
+ isinf, isneginf, isposinf, isfinite, isnat
Notes
-----
@@ -1708,6 +1708,41 @@ add_newdoc('numpy.core.umath', 'isnan',
""")
+add_newdoc('numpy.core.umath', 'isnat',
+ """
+ Test element-wise for NaT (not a time) and return result as a boolean array.
+
+ Parameters
+ ----------
+ x : array_like
+ Input array with datetime or timedelta data type.
+
+ Returns
+ -------
+ y : ndarray or bool
+ For scalar input, the result is a new boolean with value True if
+ the input is NaT; otherwise the value is False.
+
+ For array input, the result is a boolean array of the same
+ dimensions as the input and the values are True if the
+ corresponding element of the input is NaT; otherwise the values are
+ False.
+
+ See Also
+ --------
+ isnan, isinf, isneginf, isposinf, isfinite
+
+ Examples
+ --------
+ >>> np.isnat(np.datetime64("NaT"))
+ True
+ >>> np.isnat(np.datetime64("2016-01-01"))
+ False
+ >>> np.isnat(np.array(["NaT", "2016-01-01"], dtype="datetime64[ns]"))
+ array([ True, False], dtype=bool)
+
+ """)
+
add_newdoc('numpy.core.umath', 'left_shift',
"""
Shift the bits of an integer to the left.
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 47faaf180..e88b87b5c 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -1228,6 +1228,15 @@ TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU
*/
NPY_NO_EXPORT void
+@TYPE@_isnat(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP {
+ const @type@ in1 = *(@type@ *)ip1;
+ *((npy_bool *)op1) = (in1 == NPY_DATETIME_NAT);
+ }
+}
+
+NPY_NO_EXPORT void
@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
{
OUTPUT_LOOP {
diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src
index d20b776a0..c1b451c5b 100644
--- a/numpy/core/src/umath/loops.h.src
+++ b/numpy/core/src/umath/loops.h.src
@@ -402,6 +402,9 @@ TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU
*/
NPY_NO_EXPORT void
+@TYPE@_isnat(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
/**begin repeat1
diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c
index 154dbfb2f..0fd3c45c5 100644
--- a/numpy/core/src/umath/ufunc_type_resolution.c
+++ b/numpy/core/src/umath/ufunc_type_resolution.c
@@ -537,6 +537,32 @@ PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc,
}
/*
+ * This function applies special type resolution rules for the isnat
+ * ufunc. This ufunc converts datetime/timedelta -> bool, and is not covered
+ * by the simple unary type resolution.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+NPY_NO_EXPORT int
+PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes)
+{
+ if (!PyTypeNum_ISDATETIME(PyArray_DESCR(operands[0])->type_num)) {
+ PyErr_SetString(PyExc_ValueError,
+ "ufunc 'isnat' is only defined for datetime and timedelta.");
+ return -1;
+ }
+
+ out_dtypes[0] = ensure_dtype_nbo(PyArray_DESCR(operands[0]));
+ out_dtypes[1] = PyArray_DescrFromType(NPY_BOOL);
+
+ return 0;
+}
+
+/*
* Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata
* from the given dtype.
*
diff --git a/numpy/core/src/umath/ufunc_type_resolution.h b/numpy/core/src/umath/ufunc_type_resolution.h
index d20c1e85b..eaf5e91ce 100644
--- a/numpy/core/src/umath/ufunc_type_resolution.h
+++ b/numpy/core/src/umath/ufunc_type_resolution.h
@@ -42,6 +42,13 @@ PyUFunc_AbsoluteTypeResolver(PyUFuncObject *ufunc,
PyArrayObject **operands,
PyObject *type_tup,
PyArray_Descr **out_dtypes);
+
+NPY_NO_EXPORT int
+PyUFunc_IsNaTTypeResolver(PyUFuncObject *ufunc,
+ NPY_CASTING casting,
+ PyArrayObject **operands,
+ PyObject *type_tup,
+ PyArray_Descr **out_dtypes);
NPY_NO_EXPORT int
PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc,
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 12ebd5ae9..48afa728d 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -1918,6 +1918,35 @@ class TestDateTime(TestCase):
a = np.datetime64('2038-01-20T13:21:14')
assert_equal(str(a), '2038-01-20T13:21:14')
+ def test_isnat(self):
+ assert_(np.isnat(np.datetime64('NaT', 'ms')))
+ assert_(np.isnat(np.datetime64('NaT', 'ns')))
+ assert_(not np.isnat(np.datetime64('2038-01-19T03:14:07')))
+
+ assert_(np.isnat(np.timedelta64('NaT', "ms")))
+ assert_(not np.isnat(np.timedelta64(34, "ms")))
+
+ res = np.array([False, False, True])
+ for unit in ['Y', 'M', 'W', 'D',
+ 'h', 'm', 's', 'ms', 'us',
+ 'ns', 'ps', 'fs', 'as']:
+ arr = np.array([123, -321, "NaT"], dtype='<datetime64[%s]' % unit)
+ assert_equal(np.isnat(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='>datetime64[%s]' % unit)
+ assert_equal(np.isnat(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='<timedelta64[%s]' % unit)
+ assert_equal(np.isnat(arr), res)
+ arr = np.array([123, -321, "NaT"], dtype='>timedelta64[%s]' % unit)
+ assert_equal(np.isnat(arr), res)
+
+ def test_isnat_error(self):
+ # Test that only datetime dtype arrays are accepted
+ for t in np.typecodes["All"]:
+ if t in np.typecodes["Datetime"]:
+ continue
+ assert_raises(ValueError, np.isnat, np.zeros(10, t))
+
+
class TestDateTimeData(TestCase):
def test_basic(self):