Skip to content

Commit bc537b7

Browse files
authored
REF: share more methods in ExtensionIndex (#37970)
1 parent 7077a08 commit bc537b7

File tree

8 files changed

+48
-78
lines changed

8 files changed

+48
-78
lines changed

pandas/core/indexes/category.py

+13-18
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
from pandas._config import get_option
77

88
from pandas._libs import index as libindex
9-
from pandas._libs.hashtable import duplicated_int64
109
from pandas._libs.lib import no_default
1110
from pandas._typing import ArrayLike, Label
1211
from pandas.util._decorators import Appender, cache_readonly, doc
1312

1413
from pandas.core.dtypes.common import (
1514
ensure_platform_int,
1615
is_categorical_dtype,
17-
is_list_like,
1816
is_scalar,
1917
)
2018
from pandas.core.dtypes.dtypes import CategoricalDtype
@@ -226,9 +224,14 @@ def _simple_new(cls, values: Categorical, name: Label = None):
226224

227225
# --------------------------------------------------------------------
228226

227+
# error: Argument 1 of "_shallow_copy" is incompatible with supertype
228+
# "ExtensionIndex"; supertype defines the argument type as
229+
# "Optional[ExtensionArray]" [override]
229230
@doc(Index._shallow_copy)
230-
def _shallow_copy(
231-
self, values: Optional[Categorical] = None, name: Label = no_default
231+
def _shallow_copy( # type:ignore[override]
232+
self,
233+
values: Optional[Categorical] = None,
234+
name: Label = no_default,
232235
):
233236
name = self.name if name is no_default else name
234237

@@ -247,6 +250,10 @@ def _is_dtype_compat(self, other) -> Categorical:
247250
provide a comparison between the dtype of self and other (coercing if
248251
needed)
249252
253+
Parameters
254+
----------
255+
other : Index
256+
250257
Returns
251258
-------
252259
Categorical
@@ -263,8 +270,6 @@ def _is_dtype_compat(self, other) -> Categorical:
263270
)
264271
else:
265272
values = other
266-
if not is_list_like(values):
267-
values = [values]
268273

269274
cat = Categorical(other, dtype=self.dtype)
270275
other = CategoricalIndex(cat)
@@ -358,11 +363,6 @@ def values(self):
358363
""" return the underlying data, which is a Categorical """
359364
return self._data
360365

361-
@property
362-
def _has_complex_internals(self) -> bool:
363-
# used to avoid libreduction code paths, which raise or require conversion
364-
return True
365-
366366
@doc(Index.__contains__)
367367
def __contains__(self, key: Any) -> bool:
368368
# if key is a NaN, check if any NaN is in self.
@@ -399,11 +399,6 @@ def unique(self, level=None):
399399
# of result, not self.
400400
return type(self)._simple_new(result, name=self.name)
401401

402-
@doc(Index.duplicated)
403-
def duplicated(self, keep="first"):
404-
codes = self.codes.astype("i8")
405-
return duplicated_int64(codes, keep)
406-
407402
def _to_safe_for_reshape(self):
408403
""" convert to object if we are a categorical """
409404
return self.astype("object")
@@ -482,7 +477,7 @@ def reindex(self, target, method=None, level=None, limit=None, tolerance=None):
482477
new_target = np.asarray(new_target)
483478
if is_categorical_dtype(target):
484479
new_target = Categorical(new_target, dtype=target.dtype)
485-
new_target = target._shallow_copy(new_target, name=self.name)
480+
new_target = type(self)._simple_new(new_target, name=self.name)
486481
else:
487482
new_target = Index(new_target, name=self.name)
488483

@@ -506,7 +501,7 @@ def _reindex_non_unique(self, target):
506501
# .reindex returns normal Index. Revert to CategoricalIndex if
507502
# all targets are included in my categories
508503
new_target = Categorical(new_target, dtype=self.dtype)
509-
new_target = self._shallow_copy(new_target)
504+
new_target = type(self)._simple_new(new_target, name=self.name)
510505

511506
return new_target, indexer, new_indexer
512507

pandas/core/indexes/datetimelike.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from pandas._libs.tslibs import BaseOffset, Resolution, Tick
1111
from pandas._typing import Callable, Label
1212
from pandas.compat.numpy import function as nv
13-
from pandas.errors import AbstractMethodError
1413
from pandas.util._decorators import Appender, cache_readonly, doc
1514

1615
from pandas.core.dtypes.common import (
@@ -124,16 +123,6 @@ def _simple_new(
124123
def _is_all_dates(self) -> bool:
125124
return True
126125

127-
def _shallow_copy(self, values=None, name: Label = lib.no_default):
128-
name = self.name if name is lib.no_default else name
129-
130-
if values is not None:
131-
return self._simple_new(values, name=name)
132-
133-
result = self._simple_new(self._data, name=name)
134-
result._cache = self._cache
135-
return result
136-
137126
# ------------------------------------------------------------------------
138127
# Abstract data attributes
139128

@@ -399,7 +388,7 @@ def _format_with_header(
399388

400389
@property
401390
def _formatter_func(self):
402-
raise AbstractMethodError(self)
391+
return self._data._formatter()
403392

404393
def _format_attrs(self):
405394
"""
@@ -692,6 +681,11 @@ def _with_freq(self, freq):
692681
arr = self._data._with_freq(freq)
693682
return type(self)._simple_new(arr, name=self.name)
694683

684+
@property
685+
def _has_complex_internals(self) -> bool:
686+
# used to avoid libreduction code paths, which raise or require conversion
687+
return False
688+
695689
# --------------------------------------------------------------------
696690
# Set Operation Methods
697691

pandas/core/indexes/datetimes.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@
2020

2121
from pandas.core.dtypes.common import (
2222
DT64NS_DTYPE,
23-
is_datetime64_any_dtype,
2423
is_datetime64_dtype,
2524
is_datetime64tz_dtype,
26-
is_float,
27-
is_integer,
2825
is_scalar,
2926
)
3027
from pandas.core.dtypes.missing import is_valid_nat_for_dtype
@@ -354,8 +351,6 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
354351
"""
355352
Can we compare values of the given dtype to our own?
356353
"""
357-
if not is_datetime64_any_dtype(dtype):
358-
return False
359354
if self.tz is not None:
360355
# If we have tz, we can compare to tzaware
361356
return is_datetime64tz_dtype(dtype)
@@ -720,9 +715,6 @@ def _maybe_cast_slice_bound(self, label, side: str, kind):
720715
"""
721716
assert kind in ["loc", "getitem", None]
722717

723-
if is_float(label) or isinstance(label, time) or is_integer(label):
724-
self._invalid_indexer("slice", label)
725-
726718
if isinstance(label, str):
727719
freq = getattr(self, "freqstr", getattr(self, "inferred_freq", None))
728720
parsed, reso = parsing.parse_time_string(label, freq)
@@ -739,6 +731,9 @@ def _maybe_cast_slice_bound(self, label, side: str, kind):
739731
return lower if side == "left" else upper
740732
elif isinstance(label, (self._data._recognized_scalars, date)):
741733
self._deprecate_mismatched_indexing(label)
734+
else:
735+
self._invalid_indexer("slice", label)
736+
742737
return self._maybe_cast_for_get_loc(label)
743738

744739
def _get_string_slice(self, key: str):

pandas/core/indexes/extension.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""
22
Shared methods for Index subclasses backed by ExtensionArray.
33
"""
4-
from typing import List, TypeVar
4+
from typing import List, Optional, TypeVar
55

66
import numpy as np
77

8+
from pandas._libs import lib
9+
from pandas._typing import Label
810
from pandas.compat.numpy import function as nv
911
from pandas.errors import AbstractMethodError
1012
from pandas.util._decorators import cache_readonly, doc
@@ -211,6 +213,24 @@ class ExtensionIndex(Index):
211213
__le__ = _make_wrapped_comparison_op("__le__")
212214
__ge__ = _make_wrapped_comparison_op("__ge__")
213215

216+
@doc(Index._shallow_copy)
217+
def _shallow_copy(
218+
self, values: Optional[ExtensionArray] = None, name: Label = lib.no_default
219+
):
220+
name = self.name if name is lib.no_default else name
221+
222+
if values is not None:
223+
return self._simple_new(values, name=name)
224+
225+
result = self._simple_new(self._data, name=name)
226+
result._cache = self._cache
227+
return result
228+
229+
@property
230+
def _has_complex_internals(self) -> bool:
231+
# used to avoid libreduction code paths, which raise or require conversion
232+
return True
233+
214234
# ---------------------------------------------------------------------
215235
# NDarray-Like Methods
216236

@@ -251,7 +271,7 @@ def _get_engine_target(self) -> np.ndarray:
251271
def repeat(self, repeats, axis=None):
252272
nv.validate_repeat(tuple(), dict(axis=axis))
253273
result = self._data.repeat(repeats, axis=axis)
254-
return self._shallow_copy(result)
274+
return type(self)._simple_new(result, name=self.name)
255275

256276
def insert(self, loc: int, item):
257277
# ExtensionIndex subclasses must override Index.insert

pandas/core/indexes/interval.py

+2-20
Original file line numberDiff line numberDiff line change
@@ -320,19 +320,6 @@ def from_tuples(
320320

321321
# --------------------------------------------------------------------
322322

323-
@Appender(Index._shallow_copy.__doc__)
324-
def _shallow_copy(
325-
self, values: Optional[IntervalArray] = None, name: Label = lib.no_default
326-
):
327-
name = self.name if name is lib.no_default else name
328-
329-
if values is not None:
330-
return self._simple_new(values, name=name)
331-
332-
result = self._simple_new(self._data, name=name)
333-
result._cache = self._cache
334-
return result
335-
336323
@cache_readonly
337324
def _engine(self):
338325
left = self._maybe_convert_i8(self.left)
@@ -373,11 +360,6 @@ def values(self) -> IntervalArray:
373360
"""
374361
return self._data
375362

376-
@property
377-
def _has_complex_internals(self) -> bool:
378-
# used to avoid libreduction code paths, which raise or require conversion
379-
return True
380-
381363
def __array_wrap__(self, result, context=None):
382364
# we don't want the superclass implementation
383365
return result
@@ -893,7 +875,7 @@ def delete(self, loc):
893875
new_left = self.left.delete(loc)
894876
new_right = self.right.delete(loc)
895877
result = IntervalArray.from_arrays(new_left, new_right, closed=self.closed)
896-
return self._shallow_copy(result)
878+
return type(self)._simple_new(result, name=self.name)
897879

898880
def insert(self, loc, item):
899881
"""
@@ -915,7 +897,7 @@ def insert(self, loc, item):
915897
new_left = self.left.insert(loc, left_insert)
916898
new_right = self.right.insert(loc, right_insert)
917899
result = IntervalArray.from_arrays(new_left, new_right, closed=self.closed)
918-
return self._shallow_copy(result)
900+
return type(self)._simple_new(result, name=self.name)
919901

920902
# --------------------------------------------------------------------
921903
# Rendering Methods

pandas/core/indexes/multi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3629,7 +3629,7 @@ def astype(self, dtype, copy=True):
36293629
return self._shallow_copy()
36303630
return self
36313631

3632-
def _validate_insert_value(self, item):
3632+
def _validate_fill_value(self, item):
36333633
if not isinstance(item, tuple):
36343634
# Pad the key with empty strings if lower levels of the key
36353635
# aren't specified:
@@ -3652,7 +3652,7 @@ def insert(self, loc: int, item):
36523652
-------
36533653
new_index : Index
36543654
"""
3655-
item = self._validate_insert_value(item)
3655+
item = self._validate_fill_value(item)
36563656

36573657
new_levels = []
36583658
new_codes = []

pandas/core/indexes/period.py

-9
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,6 @@ def __new__(
251251
def values(self) -> np.ndarray:
252252
return np.asarray(self)
253253

254-
@property
255-
def _has_complex_internals(self) -> bool:
256-
# used to avoid libreduction code paths, which raise or require conversion
257-
return True
258-
259254
def _maybe_convert_timedelta(self, other):
260255
"""
261256
Convert timedelta-like input to an integer multiple of self.freq
@@ -307,10 +302,6 @@ def _mpl_repr(self):
307302
# how to represent ourselves to matplotlib
308303
return self.astype(object)._values
309304

310-
@property
311-
def _formatter_func(self):
312-
return self._data._formatter(boxed=False)
313-
314305
# ------------------------------------------------------------------------
315306
# Indexing
316307

pandas/core/indexes/timedeltas.py

-7
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,6 @@ def __new__(
157157
)
158158
return cls._simple_new(tdarr, name=name)
159159

160-
# -------------------------------------------------------------------
161-
# Rendering Methods
162-
163-
@property
164-
def _formatter_func(self):
165-
return self._data._formatter()
166-
167160
# -------------------------------------------------------------------
168161

169162
@doc(Index.astype)

0 commit comments

Comments
 (0)