Skip to content

Commit 8aad1e7

Browse files
authored
DEPR: Remove NumericIndex.__new__ + related methods (#51050)
* Remove NumericIndex.__new__ (almost) + various methods * adds GH-number * fix errors * fix error II
1 parent 8983b55 commit 8aad1e7

File tree

6 files changed

+33
-125
lines changed

6 files changed

+33
-125
lines changed

doc/source/whatsnew/v2.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ Other API changes
619619
new DataFrame (shallow copy) instead of the original DataFrame, consistent with other
620620
methods to get a full slice (for example ``df.loc[:]`` or ``df[:]``) (:issue:`49469`)
621621
- Disallow computing ``cumprod`` for :class:`Timedelta` object; previously this returned incorrect values (:issue:`50246`)
622+
- Instantiating an :class:`Index` with an numeric numpy dtype with data containing :class:`NA` and/or :class:`NaT` now raises a ``ValueError``. Previously a ``TypeError`` was raised (:issue:`51050`)
622623
- Loading a JSON file with duplicate columns using ``read_json(orient='split')`` renames columns to avoid duplicates, as :func:`read_csv` and the other readers do (:issue:`50370`)
623624
- The levels of the index of the :class:`Series` returned from ``Series.sparse.from_coo`` now always have dtype ``int32``. Previously they had dtype ``int64`` (:issue:`50926`)
624625
- :func:`to_datetime` with ``unit`` of either "Y" or "M" will now raise if a sequence contains a non-round ``float`` value, matching the ``Timestamp`` behavior (:issue:`50301`)

pandas/core/indexes/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ def _ensure_array(cls, data, dtype, copy: bool):
538538
if data.ndim > 1:
539539
# GH#13601, GH#20285, GH#27125
540540
raise ValueError("Index data must be 1-dimensional")
541+
elif dtype == np.float16:
542+
# float16 not supported (no indexing engine)
543+
raise NotImplementedError("float16 indexes are not supported")
544+
541545
if copy:
542546
# asarray_tuplesafe does not always copy underlying data,
543547
# so need to make sure that this happens

pandas/core/indexes/numeric.py

Lines changed: 9 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from __future__ import annotations
22

3-
from typing import Callable
4-
53
import numpy as np
64

75
from pandas._typing import Dtype
@@ -10,20 +8,7 @@
108
doc,
119
)
1210

13-
from pandas.core.dtypes.common import (
14-
is_dtype_equal,
15-
is_integer_dtype,
16-
is_numeric_dtype,
17-
is_scalar,
18-
pandas_dtype,
19-
)
20-
from pandas.core.dtypes.generic import ABCSeries
21-
22-
from pandas.core.construction import sanitize_array
23-
from pandas.core.indexes.base import (
24-
Index,
25-
maybe_extract_name,
26-
)
11+
from pandas.core.indexes.base import Index
2712

2813

2914
class NumericIndex(Index):
@@ -64,102 +49,20 @@ class NumericIndex(Index):
6449
"""
6550

6651
_typ = "numericindex"
67-
_values: np.ndarray
6852
_default_dtype: np.dtype | None = None
69-
_dtype_validation_metadata: tuple[Callable[..., bool], str] = (
70-
is_numeric_dtype,
71-
"numeric type",
72-
)
7353
_can_hold_strings = False
7454

7555
def __new__(
7656
cls, data=None, dtype: Dtype | None = None, copy: bool = False, name=None
7757
) -> NumericIndex:
78-
name = maybe_extract_name(name, data, cls)
79-
80-
subarr = cls._ensure_array(data, dtype, copy)
81-
return cls._simple_new(subarr, name=name)
82-
83-
@classmethod
84-
def _ensure_array(cls, data, dtype, copy: bool):
85-
"""
86-
Ensure we have a valid array to pass to _simple_new.
87-
"""
88-
cls._validate_dtype(dtype)
89-
if dtype == np.float16:
90-
91-
# float16 not supported (no indexing engine)
92-
raise NotImplementedError("float16 indexes are not supported")
93-
94-
if not isinstance(data, (np.ndarray, Index)):
95-
# Coerce to ndarray if not already ndarray or Index
96-
if is_scalar(data):
97-
cls._raise_scalar_data_error(data)
98-
99-
# other iterable of some kind
100-
if not isinstance(data, (ABCSeries, list, tuple)):
101-
data = list(data)
102-
103-
if isinstance(data, (list, tuple)):
104-
if len(data):
105-
data = sanitize_array(data, index=None)
106-
else:
107-
data = np.array([], dtype=np.int64)
108-
109-
dtype = cls._ensure_dtype(dtype)
110-
111-
if copy or not is_dtype_equal(data.dtype, dtype):
112-
# TODO: the try/except below is because it's difficult to predict the error
113-
# and/or error message from different combinations of data and dtype.
114-
# Efforts to avoid this try/except welcome.
115-
# See https://github.com/pandas-dev/pandas/pull/41153#discussion_r676206222
116-
try:
117-
subarr = np.array(data, dtype=dtype, copy=copy)
118-
cls._validate_dtype(subarr.dtype)
119-
except (TypeError, ValueError):
120-
raise ValueError(f"data is not compatible with {cls.__name__}")
121-
cls._assert_safe_casting(data, subarr)
122-
else:
123-
subarr = data
124-
125-
if subarr.ndim > 1:
126-
# GH#13601, GH#20285, GH#27125
127-
raise ValueError("Index data must be 1-dimensional")
128-
129-
subarr = np.asarray(subarr)
130-
if subarr.dtype == "float16":
131-
# float16 not supported (no indexing engine)
132-
raise NotImplementedError("float16 indexes are not implemented")
133-
134-
return subarr
135-
136-
@classmethod
137-
def _validate_dtype(cls, dtype: Dtype | None) -> None:
138-
if dtype is None:
139-
return
140-
141-
validation_func, expected = cls._dtype_validation_metadata
142-
if not validation_func(dtype):
143-
raise ValueError(
144-
f"Incorrect `dtype` passed: expected {expected}, received {dtype}"
145-
)
146-
147-
@classmethod
148-
def _ensure_dtype(cls, dtype: Dtype | None) -> np.dtype | None:
149-
"""
150-
Assumes dtype has already been validated.
151-
"""
152-
if dtype is None:
153-
return cls._default_dtype
154-
155-
dtype = pandas_dtype(dtype)
156-
if not isinstance(dtype, np.dtype):
157-
raise TypeError(f"{dtype} not a numpy type")
158-
elif dtype == np.float16:
159-
# float16 not supported (no indexing engine)
160-
raise NotImplementedError("float16 indexes are not supported")
161-
162-
return dtype
58+
# temporary scaffolding, will be removed soon.
59+
if isinstance(data, list) and len(data) == 0:
60+
data = np.array([], dtype=np.int64)
61+
elif isinstance(data, range):
62+
data = np.arange(data.start, data.stop, data.step, dtype=np.int64)
63+
return super().__new__(
64+
cls, data=data, dtype=dtype, copy=copy, name=name
65+
) # type: ignore[return-value]
16366

16467
# ----------------------------------------------------------------
16568
# Indexing Methods
@@ -168,17 +71,3 @@ def _ensure_dtype(cls, dtype: Dtype | None) -> np.dtype | None:
16871
@doc(Index._should_fallback_to_positional)
16972
def _should_fallback_to_positional(self) -> bool:
17073
return False
171-
172-
# ----------------------------------------------------------------
173-
174-
@classmethod
175-
def _assert_safe_casting(cls, data: np.ndarray, subarr: np.ndarray) -> None:
176-
"""
177-
Ensure incoming data can be represented with matching signed-ness.
178-
179-
Needed if the process of casting data from some accepted dtype to the internal
180-
dtype(s) bears the risk of truncation (e.g. float to int).
181-
"""
182-
if is_integer_dtype(subarr.dtype):
183-
if not np.array_equal(data, subarr):
184-
raise TypeError("Unsafe NumPy casting, you must explicitly cast")

pandas/core/indexes/range.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class RangeIndex(NumericIndex):
9898
_typ = "rangeindex"
9999
_dtype_validation_metadata = (is_signed_integer_dtype, "signed integer")
100100
_range: range
101+
_values: np.ndarray
101102

102103
@property
103104
def _engine_type(self) -> type[libindex.Int64Engine]:
@@ -178,6 +179,17 @@ def _simple_new( # type: ignore[override]
178179
result._reset_identity()
179180
return result
180181

182+
@classmethod
183+
def _validate_dtype(cls, dtype: Dtype | None) -> None:
184+
if dtype is None:
185+
return
186+
187+
validation_func, expected = cls._dtype_validation_metadata
188+
if not validation_func(dtype):
189+
raise ValueError(
190+
f"Incorrect `dtype` passed: expected {expected}, received {dtype}"
191+
)
192+
181193
# --------------------------------------------------------------------
182194

183195
# error: Return type "Type[NumericIndex]" of "_constructor" incompatible with return

pandas/tests/indexes/interval/test_constructors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ class ConstructorTests:
3939
params=[
4040
([3, 14, 15, 92, 653], np.int64),
4141
(np.arange(10, dtype="int64"), np.int64),
42-
(NumericIndex(range(-10, 11), dtype=np.int64), np.int64),
43-
(NumericIndex(range(10, 31), dtype=np.uint64), np.uint64),
42+
(NumericIndex(np.arange(-10, 11, dtype=np.int64)), np.int64),
43+
(NumericIndex(np.arange(10, 31, dtype=np.uint64)), np.uint64),
4444
(NumericIndex(np.arange(20, 30, 0.5), dtype=np.float64), np.float64),
4545
(date_range("20180101", periods=10), "<M8[ns]"),
4646
(

pandas/tests/indexes/test_base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ def test_constructor_dtypes_timedelta(self, attr, klass):
309309
"klass",
310310
[
311311
Index,
312-
NumericIndex,
313312
CategoricalIndex,
314313
DatetimeIndex,
315314
TimedeltaIndex,
@@ -873,8 +872,11 @@ def test_isin_nan_common_float64(self, nulls_fixture):
873872
if nulls_fixture is pd.NaT or nulls_fixture is pd.NA:
874873
# Check 1) that we cannot construct a float64 Index with this value
875874
# and 2) that with an NaN we do not have .isin(nulls_fixture)
876-
msg = "data is not compatible with NumericIndex"
877-
with pytest.raises(ValueError, match=msg):
875+
msg = (
876+
r"float\(\) argument must be a string or a (real )?number, "
877+
f"not {repr(type(nulls_fixture).__name__)}"
878+
)
879+
with pytest.raises(TypeError, match=msg):
878880
NumericIndex([1.0, nulls_fixture], dtype=np.float64)
879881

880882
idx = NumericIndex([1.0, np.nan], dtype=np.float64)

0 commit comments

Comments
 (0)