Skip to content

Commit d1bd4d5

Browse files
committed
rebase after pandas-dev#41472
1 parent 6126c86 commit d1bd4d5

File tree

8 files changed

+83
-39
lines changed

8 files changed

+83
-39
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
@@ -20,7 +20,6 @@
2020
from pandas.core.indexes.interval import IntervalIndex
2121
from pandas.core.indexes.multi import MultiIndex
2222
from pandas.core.indexes.numeric import (
23-
BaseNumericIndex,
2423
Float64Index,
2524
Int64Index,
2625
NumericIndex,
@@ -46,7 +45,6 @@
4645
"Index",
4746
"MultiIndex",
4847
"NumericIndex",
49-
"BaseNumericIndex",
5048
"Float64Index",
5149
"Int64Index",
5250
"CategoricalIndex",

pandas/core/indexes/base.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -2428,16 +2428,17 @@ def _is_numeric_index(cls) -> bool:
24282428
24292429
Used to check if an operation should return NumericIndex or plain Index.
24302430
"""
2431-
from pandas.core.indexes.numeric import (
2431+
from pandas import (
24322432
Float64Index,
24332433
Int64Index,
24342434
NumericIndex,
2435+
RangeIndex,
24352436
UInt64Index,
24362437
)
24372438

24382439
if not issubclass(cls, NumericIndex):
24392440
return False
2440-
elif issubclass(cls, (Int64Index, UInt64Index, Float64Index)):
2441+
elif issubclass(cls, (RangeIndex, Int64Index, UInt64Index, Float64Index)):
24412442
return False
24422443
else:
24432444
return True

pandas/core/indexes/numeric.py

+73-14
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,13 +207,52 @@ 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

208249
@cache_readonly
209250
@doc(Index._should_fallback_to_positional)
210251
def _should_fallback_to_positional(self) -> bool:
211-
return False
252+
if self.inferred_type == "floating":
253+
return False
254+
255+
return super()._should_fallback_to_positional()
212256

213257
@doc(Index._convert_slice_indexer)
214258
def _convert_slice_indexer(self, key: slice, kind: str):
@@ -229,6 +273,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
229273
# we will try to coerce to integers
230274
return self._maybe_cast_indexer(label)
231275

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

234293
@doc(Index._shallow_copy)
@@ -260,13 +319,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
260319
return is_numeric_dtype(dtype)
261320

262321
@classmethod
263-
def _assert_safe_casting(cls, data, subarr):
322+
def _assert_safe_casting(cls, data, subarr) -> None:
264323
"""
265-
Subclasses need to override this only if the process of casting data
266-
from some accepted dtype to the internal dtype(s) bears the risk of
267-
truncation (e.g. float to int).
324+
Ensure incoming data can be represented with matching signed-ness.
268325
"""
269-
pass
326+
if is_integer_dtype(subarr.dtype):
327+
if not np.array_equal(data, subarr):
328+
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
270329

271330
@property
272331
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)