Skip to content

Commit efe4e84

Browse files
committed
rebase after pandas-dev#41472
1 parent 82318c6 commit efe4e84

File tree

8 files changed

+97
-38
lines changed

8 files changed

+97
-38
lines changed

pandas/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
RangeIndex,
8080
Float64Index,
8181
MultiIndex,
82+
NumericIndex,
8283
IntervalIndex,
8384
TimedeltaIndex,
8485
DatetimeIndex,

pandas/core/index.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import warnings
22

33
from pandas.core.indexes.api import ( # noqa:F401
4-
BaseNumericIndex,
54
CategoricalIndex,
65
DatetimeIndex,
76
Float64Index,
@@ -10,6 +9,7 @@
109
IntervalIndex,
1110
MultiIndex,
1211
NaT,
12+
NumericIndex,
1313
PeriodIndex,
1414
RangeIndex,
1515
TimedeltaIndex,

pandas/core/indexes/api.py

-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from pandas.core.indexes.interval import IntervalIndex
2323
from pandas.core.indexes.multi import MultiIndex
2424
from pandas.core.indexes.numeric import (
25-
BaseNumericIndex,
2625
Float64Index,
2726
Int64Index,
2827
NumericIndex,
@@ -48,7 +47,6 @@
4847
"Index",
4948
"MultiIndex",
5049
"NumericIndex",
51-
"BaseNumericIndex",
5250
"Float64Index",
5351
"Int64Index",
5452
"CategoricalIndex",

pandas/core/indexes/base.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -2435,16 +2435,17 @@ def _is_numeric_index(cls) -> bool:
24352435
24362436
Used to check if an operation should return NumericIndex or plain Index.
24372437
"""
2438-
from pandas.core.indexes.numeric import (
2438+
from pandas import (
24392439
Float64Index,
24402440
Int64Index,
24412441
NumericIndex,
2442+
RangeIndex,
24422443
UInt64Index,
24432444
)
24442445

24452446
if not issubclass(cls, NumericIndex):
24462447
return False
2447-
elif issubclass(cls, (Int64Index, UInt64Index, Float64Index)):
2448+
elif issubclass(cls, (RangeIndex, Int64Index, UInt64Index, Float64Index)):
24482449
return False
24492450
else:
24502451
return True

pandas/core/indexes/numeric.py

+87-13
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,21 @@
8080

8181

8282
class NumericIndex(Index):
83-
"""
84-
Provide numeric type operations.
85-
86-
This is an abstract class.
87-
"""
83+
_numeric_index_descr_args = {
84+
"klass": "NumericIndex",
85+
"ltype": "integer or float",
86+
"dtype": "inferred",
87+
"extra": "",
88+
}
89+
__doc__ = _num_index_shared_docs["class_descr"] % _numeric_index_descr_args
8890

89-
_default_dtype: np.dtype | None = None
91+
_typ = "numericindex"
9092
_values: np.ndarray
91-
_dtype_validation_metadata: tuple[Callable[..., bool], str]
92-
93+
_default_dtype: np.dtype | None = None
94+
_dtype_validation_metadata: tuple[Callable[..., bool], str] = (
95+
is_numeric_dtype,
96+
"numeric type",
97+
)
9398
_is_numeric_dtype = True
9499
_can_hold_strings = False
95100

@@ -202,9 +207,63 @@ def _ensure_dtype(
202207
else:
203208
return dtype
204209

210+
def __contains__(self, key) -> bool:
211+
"""
212+
Check if key is a float and has a decimal. If it has, return False.
213+
"""
214+
if not is_integer_dtype(self.dtype):
215+
return super().__contains__(key)
216+
217+
hash(key)
218+
try:
219+
if is_float(key) and int(key) != key:
220+
# otherwise the `key in self._engine` check casts e.g. 1.1 -> 1
221+
return False
222+
return key in self._engine
223+
except (OverflowError, TypeError, ValueError):
224+
return False
225+
226+
@doc(Index.astype)
227+
def astype(self, dtype, copy=True):
228+
if is_float_dtype(self.dtype):
229+
dtype = pandas_dtype(dtype)
230+
if needs_i8_conversion(dtype):
231+
raise TypeError(
232+
f"Cannot convert Float64Index to dtype {dtype}; integer "
233+
"values are required for conversion"
234+
)
235+
elif is_integer_dtype(dtype) and not is_extension_array_dtype(dtype):
236+
# TODO(jreback); this can change once we have an EA Index type
237+
# GH 13149
238+
arr = astype_nansafe(self._values, dtype=dtype)
239+
if isinstance(self, Float64Index):
240+
return Int64Index(arr, name=self.name)
241+
else:
242+
return NumericIndex(arr, name=self.name, dtype=dtype)
243+
244+
return super().astype(dtype, copy=copy)
245+
205246
# ----------------------------------------------------------------
206247
# Indexing Methods
207248

249+
@doc(Index._should_fallback_to_positional)
250+
def _should_fallback_to_positional(self) -> bool:
251+
if self.inferred_type == "floating":
252+
return False
253+
254+
return super()._should_fallback_to_positional()
255+
256+
@doc(Index._convert_slice_indexer)
257+
def _convert_slice_indexer(self, key: slice, kind: str):
258+
if is_float_dtype(self.dtype):
259+
assert kind in ["loc", "getitem"]
260+
261+
# We always treat __getitem__ slicing as label-based
262+
# translate to locations
263+
return self.slice_indexer(key.start, key.stop, key.step, kind=kind)
264+
265+
return super()._convert_slice_indexer(key, kind=kind)
266+
208267
@doc(Index._maybe_cast_slice_bound)
209268
def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
210269
assert kind in ["loc", "getitem", None, lib.no_default]
@@ -213,6 +272,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
213272
# we will try to coerce to integers
214273
return self._maybe_cast_indexer(label)
215274

275+
@doc(Index._convert_arr_indexer)
276+
def _convert_arr_indexer(self, keyarr) -> np.ndarray:
277+
if is_unsigned_integer_dtype(self.dtype):
278+
# Cast the indexer to uint64 if possible so that the values returned
279+
# from indexing are also uint64.
280+
dtype = None
281+
if is_integer_dtype(keyarr) or (
282+
lib.infer_dtype(keyarr, skipna=False) == "integer"
283+
):
284+
dtype = np.dtype(np.uint64)
285+
286+
return com.asarray_tuplesafe(keyarr, dtype=dtype)
287+
288+
return super()._convert_arr_indexer(keyarr)
289+
216290
# ----------------------------------------------------------------
217291

218292
@doc(Index._shallow_copy)
@@ -244,13 +318,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
244318
return is_numeric_dtype(dtype)
245319

246320
@classmethod
247-
def _assert_safe_casting(cls, data, subarr):
321+
def _assert_safe_casting(cls, data, subarr) -> None:
248322
"""
249-
Subclasses need to override this only if the process of casting data
250-
from some accepted dtype to the internal dtype(s) bears the risk of
251-
truncation (e.g. float to int).
323+
Ensure incoming data can be represented with matching signed-ness.
252324
"""
253-
pass
325+
if is_integer_dtype(subarr.dtype):
326+
if not np.array_equal(data, subarr):
327+
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
254328

255329
@property
256330
def _is_all_dates(self) -> bool:

pandas/core/indexes/range.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242
import pandas.core.indexes.base as ibase
4343
from pandas.core.indexes.base import maybe_extract_name
4444
from pandas.core.indexes.numeric import (
45-
BaseNumericIndex,
4645
Float64Index,
4746
Int64Index,
47+
NumericIndex,
4848
)
4949
from pandas.core.ops.common import unpack_zerodim_and_defer
5050

@@ -54,7 +54,7 @@
5454
_empty_range = range(0)
5555

5656

57-
class RangeIndex(BaseNumericIndex):
57+
class RangeIndex(NumericIndex):
5858
"""
5959
Immutable Index implementing a monotonic integer range.
6060

pandas/tests/indexes/test_numpy_compat.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
)
1313
import pandas._testing as tm
1414
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
15-
from pandas.core.indexes.numeric import BaseNumericIndex
15+
from pandas.core.indexes.numeric import NumericIndex
1616

1717

1818
@pytest.mark.parametrize(
@@ -51,7 +51,7 @@ def test_numpy_ufuncs_basic(index, func):
5151
with tm.external_error_raised((TypeError, AttributeError)):
5252
with np.errstate(all="ignore"):
5353
func(index)
54-
elif isinstance(index, BaseNumericIndex):
54+
elif isinstance(index, NumericIndex):
5555
# coerces to float (e.g. np.sin)
5656
with np.errstate(all="ignore"):
5757
result = func(index)
@@ -106,7 +106,7 @@ def test_numpy_ufuncs_other(index, func, request):
106106
with tm.external_error_raised(TypeError):
107107
func(index)
108108

109-
elif isinstance(index, BaseNumericIndex):
109+
elif isinstance(index, NumericIndex):
110110
# Results in bool array
111111
result = func(index)
112112
assert isinstance(result, np.ndarray)

pandas/tests/indexes/test_setops.py

-15
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,7 @@
3131

3232
COMPATIBLE_INCONSISTENT_PAIRS = [
3333
(np.float64, np.int64),
34-
(np.float64, np.int32),
35-
(np.float64, np.int16),
36-
(np.float64, np.int8),
3734
(np.float64, np.uint64),
38-
(np.float64, np.uint32),
39-
(np.float64, np.uint16),
40-
(np.float64, np.uint8),
41-
(np.float32, np.int64),
42-
(np.float32, np.int32),
43-
(np.float32, np.int16),
44-
(np.float32, np.int8),
45-
(np.float32, np.uint64),
46-
(np.float32, np.uint32),
47-
(np.float32, np.uint16),
48-
(np.float32, np.uint8),
49-
(np.float32, np.float64),
5035
]
5136

5237

0 commit comments

Comments
 (0)