diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index f0e7893435f2b..549920e230e8a 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -2184,7 +2184,7 @@ def _holder(self): @property def fill_value(self): - return tslibs.iNaT + return np.datetime64("NaT", "ns") def get_values(self, dtype=None): """ @@ -2266,14 +2266,9 @@ def _can_hold_element(self, element): if self.is_datetimetz: return tz_compare(element.tzinfo, self.dtype.tz) return element.tzinfo is None - elif is_integer(element): - return element == tslibs.iNaT return is_valid_nat_for_dtype(element, self.dtype) - def _coerce_values(self, values): - return values.view("i8") - def _try_coerce_args(self, other): """ Coerce other to dtype 'i8'. NaN and NaT convert to @@ -2290,16 +2285,15 @@ def _try_coerce_args(self, other): base-type other """ if is_valid_nat_for_dtype(other, self.dtype): - other = tslibs.iNaT - elif is_integer(other) and other == tslibs.iNaT: - pass + other = np.datetime64("NaT", "ns") elif isinstance(other, (datetime, np.datetime64, date)): other = self._box_func(other) if getattr(other, "tz") is not None: raise TypeError("cannot coerce a Timestamp with a tz on a naive Block") - other = other.asm8.view("i8") + other = other.asm8 elif hasattr(other, "dtype") and is_datetime64_dtype(other): - other = other.astype("i8", copy=False).view("i8") + # TODO: can we get here with non-nano? + pass else: # coercion issues # let higher levels handle @@ -2458,8 +2452,7 @@ def _slice(self, slicer): return self.values[slicer] def _coerce_values(self, values): - # asi8 is a view, needs copy - return _block_shape(values.view("i8"), ndim=self.ndim) + return _block_shape(values, ndim=self.ndim) def _try_coerce_args(self, other): """ @@ -2484,21 +2477,17 @@ def _try_coerce_args(self, other): other = self._holder(other, dtype=self.dtype) elif is_valid_nat_for_dtype(other, self.dtype): - other = tslibs.iNaT - elif is_integer(other) and other == tslibs.iNaT: - pass + other = np.datetime64("NaT", "ns") elif isinstance(other, self._holder): - if other.tz != self.values.tz: + if not tz_compare(other.tz, self.values.tz): raise ValueError("incompatible or non tz-aware value") - other = _block_shape(other.asi8, ndim=self.ndim) + elif isinstance(other, (np.datetime64, datetime, date)): other = tslibs.Timestamp(other) - tz = getattr(other, "tz", None) # test we can have an equal time zone - if tz is None or str(tz) != str(self.values.tz): + if not tz_compare(other.tz, self.values.tz): raise ValueError("incompatible or non tz-aware value") - other = other.value else: raise TypeError(other) @@ -2654,8 +2643,8 @@ def fillna(self, value, **kwargs): def _try_coerce_args(self, other): """ - Coerce values and other to int64, with null values converted to - iNaT. values is always ndarray-like, other may not be + Coerce values and other to datetime64[ns], with null values + converted to datetime64("NaT", "ns"). Parameters ---------- diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py index 260da862a4f2b..4db0f75586ead 100644 --- a/pandas/core/nanops.py +++ b/pandas/core/nanops.py @@ -1360,14 +1360,6 @@ 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: @@ -1399,6 +1391,16 @@ def nanpercentile(values, q, axis, na_value, mask, ndim, interpolation): ------- quantiles : scalar or array """ + if values.dtype.kind in ["m", "M"]: + # need to cast to integer to avoid rounding errors in numpy + result = nanpercentile( + values.view("i8"), q, axis, na_value.view("i8"), mask, ndim, 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) + if not lib.is_scalar(mask) and mask.any(): if ndim == 1: return _nanpercentile_1d( diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index 0cb7db0e47123..756a6159fc7c5 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -1150,6 +1150,7 @@ def test_fancy_index_int_labels_exceptions(self, float_frame): with pytest.raises(KeyError, match=msg): float_frame.ix[:, ["E"]] = 1 + # FIXME: don't leave commented-out # partial setting now allows this GH2578 # pytest.raises(KeyError, float_frame.ix.__setitem__, # (slice(None, None), 'E'), 1) @@ -1676,9 +1677,11 @@ def test_setitem_single_column_mixed_datetime(self): ) assert_series_equal(result, expected) - # set an allowable datetime64 type + # GH#16674 iNaT is treated as an integer when given by the user df.loc["b", "timestamp"] = iNaT - assert isna(df.loc["b", "timestamp"]) + assert not isna(df.loc["b", "timestamp"]) + assert df["timestamp"].dtype == np.object_ + assert df.loc["b", "timestamp"] == iNaT # allow this syntax df.loc["c", "timestamp"] = np.nan diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index b56251aae884e..f87d6dba72e68 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -338,7 +338,7 @@ def test_try_coerce_arg(self): vals = (np.datetime64("2010-10-10"), datetime(2010, 10, 10), date(2010, 10, 10)) for val in vals: coerced = block._try_coerce_args(val) - assert np.int64 == type(coerced) + assert np.datetime64 == type(coerced) assert pd.Timestamp("2010-10-10") == pd.Timestamp(coerced)