diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 897a82f9a1968..c0d3368c652ec 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -2597,6 +2597,7 @@ class TimeDeltaBlock(DatetimeLikeBlockMixin, IntBlock): is_timedelta = True _can_hold_na = True is_numeric = False + fill_value = np.timedelta64("NaT", "ns") def __init__(self, values, placement, ndim=None): if values.dtype != _TD_DTYPE: @@ -2617,15 +2618,11 @@ def _box_func(self): def _can_hold_element(self, element): tipo = maybe_infer_dtype_type(element) if tipo is not None: - # TODO: remove the np.int64 support once coerce_values and - # _try_coerce_args both coerce to m8[ns] and not i8. - return issubclass(tipo.type, (np.timedelta64, np.int64)) + return issubclass(tipo.type, np.timedelta64) elif element is NaT: return True elif isinstance(element, (timedelta, np.timedelta64)): return True - elif is_integer(element): - return element == tslibs.iNaT return is_valid_nat_for_dtype(element, self.dtype) def fillna(self, value, **kwargs): @@ -2645,9 +2642,6 @@ def fillna(self, value, **kwargs): value = Timedelta(value, unit="s") return super().fillna(value, **kwargs) - def _coerce_values(self, values): - return values.view("i8") - def _try_coerce_args(self, other): """ Coerce values and other to int64, with null values converted to @@ -2663,13 +2657,12 @@ def _try_coerce_args(self, other): """ if is_valid_nat_for_dtype(other, self.dtype): - other = tslibs.iNaT - elif is_integer(other) and other == tslibs.iNaT: - pass + other = np.timedelta64("NaT", "ns") elif isinstance(other, (timedelta, np.timedelta64)): - other = Timedelta(other).value + other = Timedelta(other).to_timedelta64() elif hasattr(other, "dtype") and is_timedelta64_dtype(other): - other = other.astype("i8", copy=False).view("i8") + # TODO: can we get here with non-nano dtype? + pass else: # coercion issues # let higher levels handle @@ -2683,7 +2676,7 @@ def _try_coerce_result(self, result): mask = isna(result) if result.dtype.kind in ["i", "f"]: result = result.astype("m8[ns]") - result[mask] = tslibs.iNaT + result[mask] = np.timedelta64("NaT", "ns") elif isinstance(result, (np.integer, np.float)): result = self._box_func(result) diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index ce14cb22a88ce..aa255d03f9db7 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -1362,6 +1362,14 @@ def _nanpercentile_1d(values, mask, q, na_value, interpolation): quantiles : scalar or array """ # mask is Union[ExtensionArray, ndarray] + if values.dtype.kind == "m": + # need to cast to integer to avoid rounding errors in numpy + result = _nanpercentile_1d(values.view("i8"), mask, q, na_value, interpolation) + + # Note: we have to do do `astype` and not view because in general we + # have float result at this point, not i8 + return result.astype(values.dtype) + values = values[~mask] if len(values) == 0: diff --git a/pandas/tests/series/test_missing.py b/pandas/tests/series/test_missing.py index f8a44b7f5639e..adb23fc6b94ea 100644 --- a/pandas/tests/series/test_missing.py +++ b/pandas/tests/series/test_missing.py @@ -780,9 +780,11 @@ def test_timedelta64_nan(self): td1[0] = td[0] assert not isna(td1[0]) + # GH#16674 iNaT is treated as an integer when given by the user td1[1] = iNaT - assert isna(td1[1]) - assert td1[1].value == iNaT + assert not isna(td1[1]) + assert td1.dtype == np.object_ + assert td1[1] == iNaT td1[1] = td[1] assert not isna(td1[1]) @@ -792,6 +794,7 @@ def test_timedelta64_nan(self): td1[2] = td[2] assert not isna(td1[2]) + # FIXME: don't leave commented-out # boolean setting # this doesn't work, not sure numpy even supports it # result = td[(td>np.timedelta64(timedelta(days=3))) &