Skip to content

Commit b529857

Browse files
jbrockmendeljreback
authored andcommitted
make TDI.get_value use get_loc, fix wrong-dtype NaT (#31230)
1 parent 49f2779 commit b529857

File tree

4 files changed

+55
-53
lines changed

4 files changed

+55
-53
lines changed

pandas/core/arrays/timedeltas.py

-4
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@
4444
from pandas.tseries.offsets import Tick
4545

4646

47-
def _is_convertible_to_td(key):
48-
return isinstance(key, (Tick, timedelta, np.timedelta64, str))
49-
50-
5147
def _field_accessor(name, alias, docstring=None):
5248
def f(self):
5349
values = self.asi8

pandas/core/indexes/timedeltas.py

+29-34
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
""" implement the TimedeltaIndex """
2-
from datetime import datetime
32

43
import numpy as np
54

@@ -10,19 +9,23 @@
109
_TD_DTYPE,
1110
is_float,
1211
is_integer,
13-
is_list_like,
1412
is_scalar,
1513
is_timedelta64_dtype,
1614
is_timedelta64_ns_dtype,
1715
pandas_dtype,
1816
)
19-
from pandas.core.dtypes.missing import isna
17+
from pandas.core.dtypes.missing import is_valid_nat_for_dtype
2018

2119
from pandas.core.accessor import delegate_names
2220
from pandas.core.arrays import datetimelike as dtl
23-
from pandas.core.arrays.timedeltas import TimedeltaArray, _is_convertible_to_td
21+
from pandas.core.arrays.timedeltas import TimedeltaArray
2422
import pandas.core.common as com
25-
from pandas.core.indexes.base import Index, _index_shared_docs, maybe_extract_name
23+
from pandas.core.indexes.base import (
24+
Index,
25+
InvalidIndexError,
26+
_index_shared_docs,
27+
maybe_extract_name,
28+
)
2629
from pandas.core.indexes.datetimelike import (
2730
DatetimeIndexOpsMixin,
2831
DatetimelikeDelegateMixin,
@@ -236,22 +239,10 @@ def get_value(self, series, key):
236239
Fast lookup of value from 1-dimensional ndarray. Only use this if you
237240
know what you're doing
238241
"""
239-
240-
if isinstance(key, str):
241-
try:
242-
key = Timedelta(key)
243-
except ValueError:
244-
raise KeyError(key)
245-
246-
if isinstance(key, self._data._recognized_scalars) or key is NaT:
247-
key = Timedelta(key)
248-
return self.get_value_maybe_box(series, key)
249-
250-
value = Index.get_value(self, series, key)
251-
return com.maybe_box(self, value, series, key)
252-
253-
def get_value_maybe_box(self, series, key: Timedelta):
254-
loc = self.get_loc(key)
242+
if is_integer(key):
243+
loc = key
244+
else:
245+
loc = self.get_loc(key)
255246
return self._get_values_for_loc(series, loc)
256247

257248
def get_loc(self, key, method=None, tolerance=None):
@@ -260,27 +251,31 @@ def get_loc(self, key, method=None, tolerance=None):
260251
261252
Returns
262253
-------
263-
loc : int
254+
loc : int, slice, or ndarray[int]
264255
"""
265-
if is_list_like(key) or (isinstance(key, datetime) and key is not NaT):
266-
# GH#20464 datetime check here is to ensure we don't allow
267-
# datetime objects to be incorrectly treated as timedelta
268-
# objects; NaT is a special case because it plays a double role
269-
# as Not-A-Timedelta
270-
raise TypeError
271-
272-
if isna(key):
256+
if not is_scalar(key):
257+
raise InvalidIndexError(key)
258+
259+
if is_valid_nat_for_dtype(key, self.dtype):
273260
key = NaT
274261

262+
elif isinstance(key, str):
263+
try:
264+
key = Timedelta(key)
265+
except ValueError:
266+
raise KeyError(key)
267+
268+
elif isinstance(key, self._data._recognized_scalars) or key is NaT:
269+
key = Timedelta(key)
270+
271+
else:
272+
raise KeyError(key)
273+
275274
if tolerance is not None:
276275
# try converting tolerance now, so errors don't get swallowed by
277276
# the try/except clauses below
278277
tolerance = self._convert_tolerance(tolerance, np.asarray(key))
279278

280-
if _is_convertible_to_td(key) or key is NaT:
281-
key = Timedelta(key)
282-
return Index.get_loc(self, key, method, tolerance)
283-
284279
return Index.get_loc(self, key, method, tolerance)
285280

286281
def _maybe_cast_slice_bound(self, label, side, kind):

pandas/core/indexing.py

+16-13
Original file line numberDiff line numberDiff line change
@@ -1608,19 +1608,22 @@ def _convert_to_indexer(self, obj, axis: int, raise_missing: bool = False):
16081608
is_int_index = labels.is_integer()
16091609
is_int_positional = is_integer(obj) and not is_int_index
16101610

1611-
# if we are a label return me
1612-
try:
1613-
return labels.get_loc(obj)
1614-
except LookupError:
1615-
if isinstance(obj, tuple) and isinstance(labels, ABCMultiIndex):
1616-
if len(obj) == labels.nlevels:
1617-
return {"key": obj}
1618-
raise
1619-
except TypeError:
1620-
pass
1621-
except ValueError:
1622-
if not is_int_positional:
1623-
raise
1611+
if is_scalar(obj) or isinstance(labels, ABCMultiIndex):
1612+
# Otherwise get_loc will raise InvalidIndexError
1613+
1614+
# if we are a label return me
1615+
try:
1616+
return labels.get_loc(obj)
1617+
except LookupError:
1618+
if isinstance(obj, tuple) and isinstance(labels, ABCMultiIndex):
1619+
if len(obj) == labels.nlevels:
1620+
return {"key": obj}
1621+
raise
1622+
except TypeError:
1623+
pass
1624+
except ValueError:
1625+
if not is_int_positional:
1626+
raise
16241627

16251628
# a positional
16261629
if is_int_positional:

pandas/tests/indexes/timedeltas/test_indexing.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime, timedelta
2+
import re
23

34
import numpy as np
45
import pytest
@@ -48,12 +49,19 @@ def test_getitem(self):
4849

4950
@pytest.mark.parametrize(
5051
"key",
51-
[pd.Timestamp("1970-01-01"), pd.Timestamp("1970-01-02"), datetime(1970, 1, 1)],
52+
[
53+
pd.Timestamp("1970-01-01"),
54+
pd.Timestamp("1970-01-02"),
55+
datetime(1970, 1, 1),
56+
pd.Timestamp("1970-01-03").to_datetime64(),
57+
# non-matching NA values
58+
np.datetime64("NaT"),
59+
],
5260
)
5361
def test_timestamp_invalid_key(self, key):
5462
# GH#20464
5563
tdi = pd.timedelta_range(0, periods=10)
56-
with pytest.raises(TypeError):
64+
with pytest.raises(KeyError, match=re.escape(repr(key))):
5765
tdi.get_loc(key)
5866

5967

0 commit comments

Comments
 (0)