Skip to content

Commit 234f5ac

Browse files
authored
BUG: FooIndex.insert casting datetimelike NAs incorrectly (#36374)
1 parent 970517e commit 234f5ac

File tree

6 files changed

+33
-8
lines changed

6 files changed

+33
-8
lines changed

pandas/core/arrays/interval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
ABCPeriodIndex,
3838
ABCSeries,
3939
)
40-
from pandas.core.dtypes.missing import isna, notna
40+
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna, notna
4141

4242
from pandas.core.algorithms import take, value_counts
4343
from pandas.core.arrays.base import ExtensionArray, _extension_array_shared_docs
@@ -883,7 +883,7 @@ def _validate_insert_value(self, value):
883883
)
884884
left_insert = value.left
885885
right_insert = value.right
886-
elif is_scalar(value) and isna(value):
886+
elif is_valid_nat_for_dtype(value, self.left.dtype):
887887
# GH#18295
888888
left_insert = right_insert = value
889889
else:

pandas/core/dtypes/missing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,9 @@ def is_valid_nat_for_dtype(obj, dtype: DtypeObj) -> bool:
608608
return not isinstance(obj, np.timedelta64)
609609
if dtype.kind == "m":
610610
return not isinstance(obj, np.datetime64)
611+
if dtype.kind in ["i", "u", "f", "c"]:
612+
# Numeric
613+
return obj is not NaT and not isinstance(obj, (np.datetime64, np.timedelta64))
611614

612615
# must be PeriodDType
613616
return not isinstance(obj, (np.datetime64, np.timedelta64))

pandas/core/indexes/numeric.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
ABCSeries,
2929
ABCUInt64Index,
3030
)
31-
from pandas.core.dtypes.missing import isna
31+
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna
3232

3333
from pandas.core import algorithms
3434
import pandas.core.common as com
@@ -164,7 +164,12 @@ def is_all_dates(self) -> bool:
164164
def insert(self, loc: int, item):
165165
# treat NA values as nans:
166166
if is_scalar(item) and isna(item):
167-
item = self._na_value
167+
if is_valid_nat_for_dtype(item, self.dtype):
168+
item = self._na_value
169+
else:
170+
# NaT, np.datetime64("NaT"), np.timedelta64("NaT")
171+
return self.astype(object).insert(loc, item)
172+
168173
return super().insert(loc, item)
169174

170175
def _union(self, other, sort):

pandas/tests/indexes/interval/test_interval.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,20 @@ def test_insert(self, data):
204204

205205
# GH 18295 (test missing)
206206
na_idx = IntervalIndex([np.nan], closed=data.closed)
207-
for na in (np.nan, pd.NaT, None):
207+
for na in [np.nan, None, pd.NA]:
208208
expected = data[:1].append(na_idx).append(data[1:])
209209
result = data.insert(1, na)
210210
tm.assert_index_equal(result, expected)
211211

212+
if data.left.dtype.kind not in ["m", "M"]:
213+
# trying to insert pd.NaT into a numeric-dtyped Index should cast/raise
214+
msg = "can only insert Interval objects and NA into an IntervalIndex"
215+
with pytest.raises(ValueError, match=msg):
216+
result = data.insert(1, pd.NaT)
217+
else:
218+
result = data.insert(1, pd.NaT)
219+
tm.assert_index_equal(result, expected)
220+
212221
def test_is_unique_interval(self, closed):
213222
"""
214223
Interval specific tests for is_unique in addition to base class tests

pandas/tests/indexes/ranges/test_range.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,14 @@ def test_insert(self):
100100

101101
# GH 18295 (test missing)
102102
expected = Float64Index([0, np.nan, 1, 2, 3, 4])
103-
for na in (np.nan, pd.NaT, None):
103+
for na in [np.nan, None, pd.NA]:
104104
result = RangeIndex(5).insert(1, na)
105105
tm.assert_index_equal(result, expected)
106106

107+
result = RangeIndex(5).insert(1, pd.NaT)
108+
expected = pd.Index([0, pd.NaT, 1, 2, 3, 4], dtype=object)
109+
tm.assert_index_equal(result, expected)
110+
107111
def test_delete(self):
108112

109113
idx = RangeIndex(5, name="Foo")

pandas/tests/indexes/test_numeric.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,14 @@ def test_index_groupby(self):
8484
expected = {ex_keys[0]: idx[[0, 5]], ex_keys[1]: idx[[1, 4]]}
8585
tm.assert_dict_equal(idx.groupby(to_groupby), expected)
8686

87-
def test_insert(self, nulls_fixture):
87+
def test_insert_na(self, nulls_fixture):
8888
# GH 18295 (test missing)
8989
index = self.create_index()
90-
expected = Float64Index([index[0], np.nan] + list(index[1:]))
90+
91+
if nulls_fixture is pd.NaT:
92+
expected = Index([index[0], pd.NaT] + list(index[1:]), dtype=object)
93+
else:
94+
expected = Float64Index([index[0], np.nan] + list(index[1:]))
9195
result = index.insert(1, nulls_fixture)
9296
tm.assert_index_equal(result, expected)
9397

0 commit comments

Comments
 (0)