diff --git a/pandas/_libs/missing.pyx b/pandas/_libs/missing.pyx index c7f06bc5d7d4f..e922a5d1c3b27 100644 --- a/pandas/_libs/missing.pyx +++ b/pandas/_libs/missing.pyx @@ -12,8 +12,7 @@ cimport pandas._libs.util as util from pandas._libs.tslibs.np_datetime cimport ( get_timedelta64_value, get_datetime64_value) -from pandas._libs.tslibs.nattype cimport checknull_with_nat -from pandas._libs.tslibs.nattype import NaT +from pandas._libs.tslibs.nattype cimport checknull_with_nat, c_NaT cdef float64_t INF = np.inf cdef float64_t NEGINF = -INF @@ -27,7 +26,7 @@ cdef inline bint _check_all_nulls(object val): if isinstance(val, (float, complex)): res = val != val - elif val is NaT: + elif val is c_NaT: res = 1 elif val is None: res = 1 @@ -67,7 +66,7 @@ cpdef bint checknull(object val): return val != val # and val != INF and val != NEGINF elif util.is_datetime64_object(val): return get_datetime64_value(val) == NPY_NAT - elif val is NaT: + elif val is c_NaT: return True elif util.is_timedelta64_object(val): return get_timedelta64_value(val) == NPY_NAT @@ -106,7 +105,7 @@ cpdef bint checknull_old(object val): return val != val or val == INF or val == NEGINF elif util.is_datetime64_object(val): return get_datetime64_value(val) == NPY_NAT - elif val is NaT: + elif val is c_NaT: return True elif util.is_timedelta64_object(val): return get_timedelta64_value(val) == NPY_NAT @@ -190,7 +189,7 @@ def isnaobj_old(ndarray arr): result = np.zeros(n, dtype=np.uint8) for i in range(n): val = arr[i] - result[i] = val is NaT or _check_none_nan_inf_neginf(val) + result[i] = val is c_NaT or _check_none_nan_inf_neginf(val) return result.view(np.bool_) diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index c7765a2c2b89c..38401cab57f5d 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -2,7 +2,7 @@ # flake8: noqa from .conversion import normalize_date, localize_pydatetime, tz_convert_single -from .nattype import NaT, iNaT +from .nattype import NaT, iNaT, is_null_datetimelike from .np_datetime import OutOfBoundsDatetime from .period import Period, IncompatibleFrequency from .timestamps import Timestamp diff --git a/pandas/_libs/tslibs/nattype.pxd b/pandas/_libs/tslibs/nattype.pxd index f649518e969be..ee8d5ca3d861c 100644 --- a/pandas/_libs/tslibs/nattype.pxd +++ b/pandas/_libs/tslibs/nattype.pxd @@ -17,4 +17,4 @@ cdef _NaT c_NaT cdef bint checknull_with_nat(object val) -cdef bint is_null_datetimelike(object val) +cpdef bint is_null_datetimelike(object val) diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 604599f895476..df083f27ad653 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -686,7 +686,7 @@ cdef inline bint checknull_with_nat(object val): return val is None or util.is_nan(val) or val is c_NaT -cdef inline bint is_null_datetimelike(object val): +cpdef bint is_null_datetimelike(object val): """ Determine if we have a null for a timedelta/datetime (or integer versions) diff --git a/pandas/core/dtypes/missing.py b/pandas/core/dtypes/missing.py index b22cb1050f140..3c6d3f212342b 100644 --- a/pandas/core/dtypes/missing.py +++ b/pandas/core/dtypes/missing.py @@ -10,9 +10,9 @@ _NS_DTYPE, _TD_DTYPE, ensure_object, is_bool_dtype, is_complex_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_datetimelike, is_datetimelike_v_numeric, is_dtype_equal, is_extension_array_dtype, - is_float_dtype, is_integer, is_integer_dtype, is_object_dtype, - is_period_dtype, is_scalar, is_string_dtype, is_string_like_dtype, - is_timedelta64_dtype, needs_i8_conversion, pandas_dtype) + is_float_dtype, is_integer_dtype, is_object_dtype, is_period_dtype, + is_scalar, is_string_dtype, is_string_like_dtype, is_timedelta64_dtype, + needs_i8_conversion, pandas_dtype) from .generic import ( ABCDatetimeArray, ABCExtensionArray, ABCGeneric, ABCIndexClass, ABCMultiIndex, ABCSeries, ABCTimedeltaArray) @@ -339,22 +339,6 @@ def notna(obj): notnull = notna -def is_null_datelike_scalar(other): - """ test whether the object is a null datelike, e.g. Nat - but guard against passing a non-scalar """ - if other is NaT or other is None: - return True - elif is_scalar(other): - - # a timedelta - if hasattr(other, 'dtype'): - return other.view('i8') == iNaT - elif is_integer(other) and other == iNaT: - return True - return isna(other) - return False - - def _isna_compat(arr, fill_value=np.nan): """ Parameters diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index f88114e1c9e20..721215538af37 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -8,7 +8,7 @@ import numpy as np from pandas._libs import internals as libinternals, lib, tslib, tslibs -from pandas._libs.tslibs import Timedelta, conversion +from pandas._libs.tslibs import Timedelta, conversion, is_null_datetimelike import pandas.compat as compat from pandas.compat import range, zip from pandas.util._validators import validate_bool_kwarg @@ -31,7 +31,7 @@ ABCDataFrame, ABCDatetimeIndex, ABCExtensionArray, ABCIndexClass, ABCSeries) from pandas.core.dtypes.missing import ( - _isna_compat, array_equivalent, is_null_datelike_scalar, isna, notna) + _isna_compat, array_equivalent, isna, notna) import pandas.core.algorithms as algos from pandas.core.arrays import ( @@ -2077,10 +2077,6 @@ def get_values(self, dtype=None): return values return self.values - @property - def asi8(self): - return self.values.view('i8') - class DatetimeBlock(DatetimeLikeBlockMixin, Block): __slots__ = () @@ -2162,7 +2158,7 @@ def _try_coerce_args(self, values, other): if isinstance(other, bool): raise TypeError - elif is_null_datelike_scalar(other): + elif is_null_datetimelike(other): other = tslibs.iNaT elif isinstance(other, (datetime, np.datetime64, date)): other = self._box_func(other) @@ -2175,18 +2171,16 @@ def _try_coerce_args(self, values, other): else: # coercion issues # let higher levels handle - raise TypeError + raise TypeError(other) return values, other def _try_coerce_result(self, result): """ reverse of try_coerce_args """ if isinstance(result, np.ndarray): - if result.dtype.kind in ['i', 'f', 'O']: - try: - result = result.astype('M8[ns]') - except ValueError: - pass + if result.dtype.kind in ['i', 'f']: + result = result.astype('M8[ns]') + elif isinstance(result, (np.integer, np.float, np.datetime64)): result = self._box_func(result) return result @@ -2364,8 +2358,7 @@ def _try_coerce_args(self, values, other): # add the tz back other = self._holder(other, dtype=self.dtype) - elif (is_null_datelike_scalar(other) or - (lib.is_scalar(other) and isna(other))): + elif is_null_datetimelike(other): other = tslibs.iNaT elif isinstance(other, self._holder): if other.tz != self.values.tz: @@ -2380,17 +2373,19 @@ def _try_coerce_args(self, values, other): raise ValueError("incompatible or non tz-aware value") other = other.value else: - raise TypeError + raise TypeError(other) return values, other def _try_coerce_result(self, result): """ reverse of try_coerce_args """ if isinstance(result, np.ndarray): - if result.dtype.kind in ['i', 'f', 'O']: + if result.dtype.kind in ['i', 'f']: result = result.astype('M8[ns]') + elif isinstance(result, (np.integer, np.float, np.datetime64)): result = self._box_func(result) + if isinstance(result, np.ndarray): # allow passing of > 1dim if its trivial @@ -2531,20 +2526,16 @@ def _try_coerce_args(self, values, other): if isinstance(other, bool): raise TypeError - elif is_null_datelike_scalar(other): + elif is_null_datetimelike(other): other = tslibs.iNaT - elif isinstance(other, Timedelta): - other = other.value - elif isinstance(other, timedelta): - other = Timedelta(other).value - elif isinstance(other, np.timedelta64): + elif isinstance(other, (timedelta, np.timedelta64)): other = Timedelta(other).value elif hasattr(other, 'dtype') and is_timedelta64_dtype(other): other = other.astype('i8', copy=False).view('i8') else: # coercion issues # let higher levels handle - raise TypeError + raise TypeError(other) return values, other @@ -2552,11 +2543,13 @@ def _try_coerce_result(self, result): """ reverse of try_coerce_args / try_operate """ if isinstance(result, np.ndarray): mask = isna(result) - if result.dtype.kind in ['i', 'f', 'O']: + if result.dtype.kind in ['i', 'f']: result = result.astype('m8[ns]') result[mask] = tslibs.iNaT + elif isinstance(result, (np.integer, np.float)): result = self._box_func(result) + return result def should_store(self, value): diff --git a/pandas/core/sparse/series.py b/pandas/core/sparse/series.py index 4ea4531c53c72..db4d3e876dec5 100644 --- a/pandas/core/sparse/series.py +++ b/pandas/core/sparse/series.py @@ -16,9 +16,9 @@ from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, Substitution -from pandas.core.dtypes.common import is_scalar +from pandas.core.dtypes.common import is_integer, is_scalar from pandas.core.dtypes.generic import ABCSeries, ABCSparseSeries -from pandas.core.dtypes.missing import is_integer, isna, notna +from pandas.core.dtypes.missing import isna, notna from pandas.core import generic from pandas.core.arrays import SparseArray diff --git a/pandas/tests/tslibs/test_api.py b/pandas/tests/tslibs/test_api.py index fb9355dfed645..de937d1a4c526 100644 --- a/pandas/tests/tslibs/test_api.py +++ b/pandas/tests/tslibs/test_api.py @@ -23,6 +23,7 @@ def test_namespace(): api = ['NaT', 'iNaT', + 'is_null_datetimelike', 'OutOfBoundsDatetime', 'Period', 'IncompatibleFrequency',