Skip to content

Commit b4be77d

Browse files
committed
rebase after pandas-dev#41472
1 parent 69953b4 commit b4be77d

File tree

8 files changed

+70
-31
lines changed

8 files changed

+70
-31
lines changed

pandas/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
RangeIndex,
7777
Float64Index,
7878
MultiIndex,
79+
NumericIndex,
7980
IntervalIndex,
8081
TimedeltaIndex,
8182
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
@@ -2441,16 +2441,17 @@ def _is_numeric_index(cls) -> bool:
24412441
24422442
Used to check if an operation should return NumericIndex or plain Index.
24432443
"""
2444-
from pandas.core.indexes.numeric import (
2444+
from pandas import (
24452445
Float64Index,
24462446
Int64Index,
24472447
NumericIndex,
2448+
RangeIndex,
24482449
UInt64Index,
24492450
)
24502451

24512452
if not issubclass(cls, NumericIndex):
24522453
return False
2453-
elif issubclass(cls, (Int64Index, UInt64Index, Float64Index)):
2454+
elif issubclass(cls, (RangeIndex, Int64Index, UInt64Index, Float64Index)):
24542455
return False
24552456
else:
24562457
return True

pandas/core/indexes/numeric.py

+60-6
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,52 @@ def _ensure_dtype(
212212
else:
213213
return dtype
214214

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+
215251
# ----------------------------------------------------------------
216252
# Indexing Methods
217253

218254
@cache_readonly
219255
@doc(Index._should_fallback_to_positional)
220256
def _should_fallback_to_positional(self) -> bool:
221-
return False
257+
if self.inferred_type == "floating":
258+
return False
259+
260+
return super()._should_fallback_to_positional()
222261

223262
@doc(Index._convert_slice_indexer)
224263
def _convert_slice_indexer(self, key: slice, kind: str):
@@ -239,6 +278,21 @@ def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
239278
# we will try to coerce to integers
240279
return self._maybe_cast_indexer(label)
241280

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+
242296
# ----------------------------------------------------------------
243297

244298
@doc(Index._shallow_copy)
@@ -270,13 +324,13 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
270324
return is_numeric_dtype(dtype)
271325

272326
@classmethod
273-
def _assert_safe_casting(cls, data, subarr):
327+
def _assert_safe_casting(cls, data, subarr) -> None:
274328
"""
275-
Subclasses need to override this only if the process of casting data
276-
from some accepted dtype to the internal dtype(s) bears the risk of
277-
truncation (e.g. float to int).
329+
Ensure incoming data can be represented with matching signed-ness.
278330
"""
279-
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")
280334

281335
@property
282336
def _is_all_dates(self) -> bool:

pandas/core/indexes/range.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
import pandas.core.indexes.base as ibase
4646
from pandas.core.indexes.base import maybe_extract_name
4747
from pandas.core.indexes.numeric import (
48-
BaseNumericIndex,
4948
Float64Index,
5049
Int64Index,
50+
NumericIndex,
5151
)
5252
from pandas.core.ops.common import unpack_zerodim_and_defer
5353

@@ -57,7 +57,7 @@
5757
_empty_range = range(0)
5858

5959

60-
class RangeIndex(BaseNumericIndex):
60+
class RangeIndex(NumericIndex):
6161
"""
6262
Immutable Index implementing a monotonic integer range.
6363

pandas/tests/indexes/test_numpy_compat.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
)
1111
import pandas._testing as tm
1212
from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin
13-
from pandas.core.indexes.numeric import BaseNumericIndex
13+
from pandas.core.indexes.numeric import NumericIndex
1414

1515

1616
@pytest.mark.parametrize(
@@ -49,7 +49,7 @@ def test_numpy_ufuncs_basic(index, func):
4949
with tm.external_error_raised((TypeError, AttributeError)):
5050
with np.errstate(all="ignore"):
5151
func(index)
52-
elif isinstance(index, BaseNumericIndex):
52+
elif isinstance(index, NumericIndex):
5353
# coerces to float (e.g. np.sin)
5454
with np.errstate(all="ignore"):
5555
result = func(index)
@@ -94,7 +94,7 @@ def test_numpy_ufuncs_other(index, func, request):
9494
with tm.external_error_raised(TypeError):
9595
func(index)
9696

97-
elif isinstance(index, BaseNumericIndex):
97+
elif isinstance(index, NumericIndex):
9898
# Results in bool array
9999
result = func(index)
100100
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)