Skip to content

Commit 14f039a

Browse files
committed
rebase after #41472
1 parent 1ab07ea commit 14f039a

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
@@ -2430,16 +2430,17 @@ def _is_numeric_index(cls) -> bool:
24302430
24312431
Used to check if an operation should return NumericIndex or plain Index.
24322432
"""
2433-
from pandas.core.indexes.numeric import (
2433+
from pandas import (
24342434
Float64Index,
24352435
Int64Index,
24362436
NumericIndex,
2437+
RangeIndex,
24372438
UInt64Index,
24382439
)
24392440

24402441
if not issubclass(cls, NumericIndex):
24412442
return False
2442-
elif issubclass(cls, (Int64Index, UInt64Index, Float64Index)):
2443+
elif issubclass(cls, (RangeIndex, Int64Index, UInt64Index, Float64Index)):
24432444
return False
24442445
else:
24452446
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

@@ -207,13 +212,52 @@ def _ensure_dtype(
207212
else:
208213
return dtype
209214

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

213254
@cache_readonly
214255
@doc(Index._should_fallback_to_positional)
215256
def _should_fallback_to_positional(self) -> bool:
216-
return False
257+
if self.inferred_type == "floating":
258+
return False
259+
260+
return super()._should_fallback_to_positional()
217261

218262
@doc(Index._convert_slice_indexer)
219263
def _convert_slice_indexer(self, key: slice, kind: str):
@@ -234,6 +278,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
234278
# we will try to coerce to integers
235279
return self._maybe_cast_indexer(label)
236280

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

239298
@doc(Index._shallow_copy)
@@ -265,13 +324,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
265324
return is_numeric_dtype(dtype)
266325

267326
@classmethod
268-
def _assert_safe_casting(cls, data, subarr):
327+
def _assert_safe_casting(cls, data, subarr) -> None:
269328
"""
270-
Subclasses need to override this only if the process of casting data
271-
from some accepted dtype to the internal dtype(s) bears the risk of
272-
truncation (e.g. float to int).
329+
Ensure incoming data can be represented with matching signed-ness.
273330
"""
274-
pass
331+
if is_integer_dtype(subarr.dtype):
332+
if not np.array_equal(data, subarr):
333+
raise TypeError("Unsafe NumPy casting, you must explicitly cast")
275334

276335
@property
277336
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)