Skip to content

Commit b678eca

Browse files
authored
REF: consolidate DTI/TDI/PI get_value in ExtensionIndex, associated cleanups (#31406)
1 parent f878ddc commit b678eca

File tree

9 files changed

+55
-69
lines changed

9 files changed

+55
-69
lines changed

pandas/core/indexes/base.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from datetime import datetime
22
import operator
33
from textwrap import dedent
4-
from typing import Any, FrozenSet, Hashable, Optional, Union
4+
from typing import TYPE_CHECKING, Any, FrozenSet, Hashable, Optional, Union
55
import warnings
66

77
import numpy as np
@@ -83,6 +83,10 @@
8383
pprint_thing,
8484
)
8585

86+
if TYPE_CHECKING:
87+
from pandas import Series
88+
89+
8690
__all__ = ["Index"]
8791

8892
_unsortable_types = frozenset(("mixed", "mixed-integer"))
@@ -4577,21 +4581,15 @@ def argsort(self, *args, **kwargs) -> np.ndarray:
45774581
result = np.array(self)
45784582
return result.argsort(*args, **kwargs)
45794583

4580-
_index_shared_docs[
4581-
"get_value"
4582-
] = """
4584+
def get_value(self, series: "Series", key):
4585+
"""
45834586
Fast lookup of value from 1-dimensional ndarray. Only use this if you
45844587
know what you're doing.
45854588
45864589
Returns
45874590
-------
4588-
scalar
4589-
A value in the Series with the index of the key value in self.
4591+
scalar or Series
45904592
"""
4591-
4592-
@Appender(_index_shared_docs["get_value"] % _index_doc_kwargs)
4593-
def get_value(self, series, key):
4594-
45954593
if not is_scalar(key):
45964594
# if key is not a scalar, directly raise an error (the code below
45974595
# would convert to numpy arrays and raise later any way) - GH29926
@@ -4616,7 +4614,7 @@ def get_value(self, series, key):
46164614

46174615
return self._get_values_for_loc(series, loc)
46184616

4619-
def _get_values_for_loc(self, series, loc):
4617+
def _get_values_for_loc(self, series: "Series", loc):
46204618
"""
46214619
Do a positional lookup on the given Series, returning either a scalar
46224620
or a Series.

pandas/core/indexes/category.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, List
1+
from typing import TYPE_CHECKING, Any, List
22
import warnings
33

44
import numpy as np
@@ -7,7 +7,6 @@
77

88
from pandas._libs import index as libindex
99
from pandas._libs.hashtable import duplicated_int64
10-
from pandas._typing import AnyArrayLike
1110
from pandas.util._decorators import Appender, cache_readonly
1211

1312
from pandas.core.dtypes.common import (
@@ -31,6 +30,9 @@
3130
import pandas.core.missing as missing
3231
from pandas.core.ops import get_op_result_name
3332

33+
if TYPE_CHECKING:
34+
from pandas import Series
35+
3436
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
3537
_index_doc_kwargs.update(dict(target_klass="CategoricalIndex"))
3638

@@ -494,14 +496,14 @@ def get_loc(self, key, method=None):
494496
except KeyError:
495497
raise KeyError(key)
496498

497-
def get_value(self, series: AnyArrayLike, key: Any):
499+
def get_value(self, series: "Series", key: Any):
498500
"""
499501
Fast lookup of value from 1-dimensional ndarray. Only use this if you
500502
know what you're doing
501503
502504
Parameters
503505
----------
504-
series : Series, ExtensionArray, Index, or ndarray
506+
series : Series
505507
1-dimensional array to take values from
506508
key: : scalar
507509
The value of this index at the position of the desired value,
@@ -521,7 +523,7 @@ def get_value(self, series: AnyArrayLike, key: Any):
521523
pass
522524

523525
# we might be a positional inexer
524-
return super().get_value(series, key)
526+
return Index.get_value(self, series, key)
525527

526528
@Appender(Index.where.__doc__)
527529
def where(self, cond, other=None):

pandas/core/indexes/datetimelike.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ def _format_attrs(self):
383383
return attrs
384384

385385
# --------------------------------------------------------------------
386+
# Indexing Methods
386387

387388
def _convert_scalar_indexer(self, key, kind=None):
388389
"""
@@ -409,6 +410,8 @@ def _convert_scalar_indexer(self, key, kind=None):
409410

410411
return super()._convert_scalar_indexer(key, kind=kind)
411412

413+
# --------------------------------------------------------------------
414+
412415
__add__ = make_wrapped_arith_op("__add__")
413416
__radd__ = make_wrapped_arith_op("__radd__")
414417
__sub__ = make_wrapped_arith_op("__sub__")

pandas/core/indexes/datetimes.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -616,17 +616,6 @@ def _maybe_promote(self, other):
616616
other = DatetimeIndex(other)
617617
return self, other
618618

619-
def get_value(self, series, key):
620-
"""
621-
Fast lookup of value from 1-dimensional ndarray. Only use this if you
622-
know what you're doing
623-
"""
624-
if is_integer(key):
625-
loc = key
626-
else:
627-
loc = self.get_loc(key)
628-
return self._get_values_for_loc(series, loc)
629-
630619
def get_loc(self, key, method=None, tolerance=None):
631620
"""
632621
Get integer location for requested label
@@ -642,7 +631,7 @@ def get_loc(self, key, method=None, tolerance=None):
642631
if is_valid_nat_for_dtype(key, self.dtype):
643632
key = NaT
644633

645-
if isinstance(key, (datetime, np.datetime64)):
634+
if isinstance(key, self._data._recognized_scalars):
646635
# needed to localize naive datetimes
647636
key = self._maybe_cast_for_get_loc(key)
648637

pandas/core/indexes/extension.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Shared methods for Index subclasses backed by ExtensionArray.
33
"""
4-
from typing import List
4+
from typing import TYPE_CHECKING, List
55

66
import numpy as np
77

@@ -11,6 +11,7 @@
1111
from pandas.core.dtypes.common import (
1212
ensure_platform_int,
1313
is_dtype_equal,
14+
is_integer,
1415
is_object_dtype,
1516
)
1617
from pandas.core.dtypes.generic import ABCSeries
@@ -20,6 +21,9 @@
2021
from pandas.core.indexes.base import Index
2122
from pandas.core.ops import get_op_result_name
2223

24+
if TYPE_CHECKING:
25+
from pandas import Series
26+
2327

2428
def inherit_from_data(name: str, delegate, cache: bool = False, wrap: bool = False):
2529
"""
@@ -293,3 +297,26 @@ def astype(self, dtype, copy=True):
293297
# pass copy=False because any copying will be done in the
294298
# _data.astype call above
295299
return Index(new_values, dtype=new_values.dtype, name=self.name, copy=False)
300+
301+
# --------------------------------------------------------------------
302+
# Indexing Methods
303+
304+
@Appender(Index.get_value.__doc__)
305+
def get_value(self, series: "Series", key):
306+
"""
307+
Fast lookup of value from 1-dimensional ndarray. Only use this if you
308+
know what you're doing
309+
"""
310+
try:
311+
loc = self.get_loc(key)
312+
except KeyError:
313+
# e.g. DatetimeIndex doesn't hold integers
314+
if is_integer(key) and not self.holds_integer():
315+
# Fall back to positional
316+
loc = key
317+
else:
318+
raise
319+
320+
return self._get_values_for_loc(series, loc)
321+
322+
# --------------------------------------------------------------------

pandas/core/indexes/interval.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" define the IntervalIndex """
22
from operator import le, lt
33
import textwrap
4-
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union
4+
from typing import Any, Optional, Tuple, Union
55

66
import numpy as np
77

@@ -57,10 +57,6 @@
5757
from pandas.tseries.frequencies import to_offset
5858
from pandas.tseries.offsets import DateOffset
5959

60-
if TYPE_CHECKING:
61-
from pandas import Series
62-
63-
6460
_VALID_CLOSED = {"left", "right", "both", "neither"}
6561
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
6662

@@ -527,6 +523,10 @@ def is_overlapping(self) -> bool:
527523
# GH 23309
528524
return self._engine.is_overlapping
529525

526+
def holds_integer(self):
527+
return self.dtype.subtype.kind not in ["m", "M"]
528+
# TODO: There must already exist something for this?
529+
530530
@Appender(Index._convert_scalar_indexer.__doc__)
531531
def _convert_scalar_indexer(self, key, kind=None):
532532
if kind == "iloc":
@@ -884,11 +884,6 @@ def get_indexer_for(self, target: AnyArrayLike, **kwargs) -> np.ndarray:
884884
return self.get_indexer_non_unique(target)[0]
885885
return self.get_indexer(target, **kwargs)
886886

887-
@Appender(_index_shared_docs["get_value"] % _index_doc_kwargs)
888-
def get_value(self, series: "Series", key):
889-
loc = self.get_loc(key)
890-
return series.iloc[loc]
891-
892887
def _convert_slice_indexer(self, key: slice, kind=None):
893888
if not (key.step is None or key.step == 1):
894889
raise ValueError("cannot support not-default step in a slice")

pandas/core/indexes/numeric.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ def _format_native_types(
416416
)
417417
return formatter.get_result_as_array()
418418

419+
@Appender(Index.get_value.__doc__)
419420
def get_value(self, series: "Series", key):
420421
"""
421422
We always want to get an index value, never a value.

pandas/core/indexes/period.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime, timedelta
2-
from typing import TYPE_CHECKING, Any
2+
from typing import Any
33
import weakref
44

55
import numpy as np
@@ -18,7 +18,6 @@
1818
is_float,
1919
is_integer,
2020
is_integer_dtype,
21-
is_list_like,
2221
is_object_dtype,
2322
is_scalar,
2423
pandas_dtype,
@@ -51,9 +50,6 @@
5150
_index_doc_kwargs = dict(ibase._index_doc_kwargs)
5251
_index_doc_kwargs.update(dict(target_klass="PeriodIndex or list of Periods"))
5352

54-
if TYPE_CHECKING:
55-
from pandas import Series
56-
5753
# --- Period index sketch
5854

5955

@@ -455,17 +451,6 @@ def inferred_type(self) -> str:
455451
# indexing
456452
return "period"
457453

458-
def get_value(self, series: "Series", key):
459-
"""
460-
Fast lookup of value from 1-dimensional ndarray. Only use this if you
461-
know what you're doing
462-
"""
463-
if is_integer(key):
464-
loc = key
465-
else:
466-
loc = self.get_loc(key)
467-
return self._get_values_for_loc(series, loc)
468-
469454
@Appender(_index_shared_docs["get_indexer"] % _index_doc_kwargs)
470455
def get_indexer(self, target, method=None, limit=None, tolerance=None):
471456
target = ensure_index(target)
@@ -560,9 +545,6 @@ def get_loc(self, key, method=None, tolerance=None):
560545
key = Period(key, freq=self.freq)
561546
except ValueError:
562547
# we cannot construct the Period
563-
# as we have an invalid type
564-
if is_list_like(key):
565-
raise TypeError(f"'{key}' is an invalid key")
566548
raise KeyError(key)
567549

568550
ordinal = key.ordinal if key is not NaT else key.value

pandas/core/indexes/timedeltas.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,17 +222,6 @@ def _maybe_promote(self, other):
222222
other = TimedeltaIndex(other)
223223
return self, other
224224

225-
def get_value(self, series, key):
226-
"""
227-
Fast lookup of value from 1-dimensional ndarray. Only use this if you
228-
know what you're doing
229-
"""
230-
if is_integer(key):
231-
loc = key
232-
else:
233-
loc = self.get_loc(key)
234-
return self._get_values_for_loc(series, loc)
235-
236225
def get_loc(self, key, method=None, tolerance=None):
237226
"""
238227
Get integer location for requested label

0 commit comments

Comments
 (0)