diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 028472ad93e52..706b089e929a9 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -37,7 +37,7 @@ ABCPeriodIndex, ABCSeries, ) -from pandas.core.dtypes.missing import isna, notna +from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna, notna from pandas.core.algorithms import take, value_counts from pandas.core.arrays.base import ExtensionArray, _extension_array_shared_docs @@ -883,7 +883,7 @@ def _validate_insert_value(self, value): ) left_insert = value.left right_insert = value.right - elif is_scalar(value) and isna(value): + elif is_valid_nat_for_dtype(value, self.left.dtype): # GH#18295 left_insert = right_insert = value else: diff --git a/pandas/core/dtypes/missing.py b/pandas/core/dtypes/missing.py index d2e4974741b88..0b4aab0ac9d88 100644 --- a/pandas/core/dtypes/missing.py +++ b/pandas/core/dtypes/missing.py @@ -608,6 +608,9 @@ def is_valid_nat_for_dtype(obj, dtype: DtypeObj) -> bool: return not isinstance(obj, np.timedelta64) if dtype.kind == "m": return not isinstance(obj, np.datetime64) + if dtype.kind in ["i", "u", "f", "c"]: + # Numeric + return obj is not NaT and not isinstance(obj, (np.datetime64, np.timedelta64)) # must be PeriodDType return not isinstance(obj, (np.datetime64, np.timedelta64)) diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 026f128bae4be..49a70600c09fa 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -28,7 +28,7 @@ ABCSeries, ABCUInt64Index, ) -from pandas.core.dtypes.missing import isna +from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna from pandas.core import algorithms import pandas.core.common as com @@ -164,7 +164,12 @@ def is_all_dates(self) -> bool: def insert(self, loc: int, item): # treat NA values as nans: if is_scalar(item) and isna(item): - item = self._na_value + if is_valid_nat_for_dtype(item, self.dtype): + item = self._na_value + else: + # NaT, np.datetime64("NaT"), np.timedelta64("NaT") + return self.astype(object).insert(loc, item) + return super().insert(loc, item) def _union(self, other, sort): diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index 42849e0bbb5c7..734c98af3d058 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -204,11 +204,20 @@ def test_insert(self, data): # GH 18295 (test missing) na_idx = IntervalIndex([np.nan], closed=data.closed) - for na in (np.nan, pd.NaT, None): + for na in [np.nan, None, pd.NA]: expected = data[:1].append(na_idx).append(data[1:]) result = data.insert(1, na) tm.assert_index_equal(result, expected) + if data.left.dtype.kind not in ["m", "M"]: + # trying to insert pd.NaT into a numeric-dtyped Index should cast/raise + msg = "can only insert Interval objects and NA into an IntervalIndex" + with pytest.raises(ValueError, match=msg): + result = data.insert(1, pd.NaT) + else: + result = data.insert(1, pd.NaT) + tm.assert_index_equal(result, expected) + def test_is_unique_interval(self, closed): """ Interval specific tests for is_unique in addition to base class tests diff --git a/pandas/tests/indexes/ranges/test_range.py b/pandas/tests/indexes/ranges/test_range.py index 172cd4a106ac1..899c8cbc0425d 100644 --- a/pandas/tests/indexes/ranges/test_range.py +++ b/pandas/tests/indexes/ranges/test_range.py @@ -100,10 +100,14 @@ def test_insert(self): # GH 18295 (test missing) expected = Float64Index([0, np.nan, 1, 2, 3, 4]) - for na in (np.nan, pd.NaT, None): + for na in [np.nan, None, pd.NA]: result = RangeIndex(5).insert(1, na) tm.assert_index_equal(result, expected) + result = RangeIndex(5).insert(1, pd.NaT) + expected = pd.Index([0, pd.NaT, 1, 2, 3, 4], dtype=object) + tm.assert_index_equal(result, expected) + def test_delete(self): idx = RangeIndex(5, name="Foo") diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index bbd72b2ac5d60..e1623a14c333e 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -84,10 +84,14 @@ def test_index_groupby(self): expected = {ex_keys[0]: idx[[0, 5]], ex_keys[1]: idx[[1, 4]]} tm.assert_dict_equal(idx.groupby(to_groupby), expected) - def test_insert(self, nulls_fixture): + def test_insert_na(self, nulls_fixture): # GH 18295 (test missing) index = self.create_index() - expected = Float64Index([index[0], np.nan] + list(index[1:])) + + if nulls_fixture is pd.NaT: + expected = Index([index[0], pd.NaT] + list(index[1:]), dtype=object) + else: + expected = Float64Index([index[0], np.nan] + list(index[1:])) result = index.insert(1, nulls_fixture) tm.assert_index_equal(result, expected)