diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2016-12-26 22:55:22 +0100 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2017-05-07 16:21:30 +0200 |
commit | eb8ead6b63df45b148405bb979e0fbd4a32b4125 (patch) | |
tree | c7844059d2f5f271ecf5d675663114218a98f84b /numpy | |
parent | 491ddf4cbf18256d1578aea60c58888920895ef0 (diff) | |
download | numpy-eb8ead6b63df45b148405bb979e0fbd4a32b4125.tar.gz |
ENH: Add isnat function
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 6 | ||||
-rw-r--r-- | numpy/core/code_generators/ufunc_docstrings.py | 37 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 9 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.h.src | 3 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.c | 26 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.h | 7 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 29 |
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): |